Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of Email2Screen by
HTTPClient.cpp
00001 00002 /* 00003 Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com) 00004 00005 Permission is hereby granted, free of charge, to any person obtaining a copy 00006 of this software and associated documentation files (the "Software"), to deal 00007 in the Software without restriction, including without limitation the rights 00008 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00009 copies of the Software, and to permit persons to whom the Software is 00010 furnished to do so, subject to the following conditions: 00011 00012 The above copyright notice and this permission notice shall be included in 00013 all copies or substantial portions of the Software. 00014 00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00018 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00020 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 00021 THE SOFTWARE. 00022 */ 00023 00024 #include "core/netservice.h" 00025 #include "HTTPClient.h" 00026 #include "../util/base64.h" 00027 #include "../util/url.h" 00028 00029 //#define __DEBUG 00030 #include "dbg/dbg.h" 00031 00032 #define HTTP_REQUEST_TIMEOUT 30000//15000 00033 #define HTTP_PORT 80 00034 00035 #define CHUNK_SIZE 256 00036 00037 HTTPClient::HTTPClient() : NetService(false) /*Not owned by the pool*/, m_meth(HTTP_GET), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL), 00038 m_watchdog(), m_timeout(0), m_pDnsReq(NULL), m_server(), m_path(), 00039 m_closed(true),m_proxy(false), m_state(HTTP_CLOSED), 00040 m_pDataOut(NULL), m_pDataIn(NULL), m_dataChunked(false), m_dataPos(0), m_dataLen(0), m_httpResponseCode(0), m_blockingResult(HTTP_PROCESSING) 00041 00042 { 00043 setTimeout(HTTP_REQUEST_TIMEOUT); 00044 m_buf = new char[CHUNK_SIZE]; 00045 DBG("New HTTPClient %p\n",this); 00046 } 00047 00048 HTTPClient::~HTTPClient() 00049 { 00050 close(); 00051 delete[] m_buf; 00052 } 00053 00054 void HTTPClient::setProxy(const char* proxy_url) // host:port 00055 { 00056 if ( proxy_url == NULL ) 00057 { 00058 m_proxy = false; 00059 return; 00060 } 00061 Url url; 00062 url.fromString(proxy_url); 00063 m_server.setName(url.getHost().c_str()); 00064 00065 if( url.getPort() > 0 ) 00066 m_server.setPort( url.getPort() ); 00067 else 00068 m_server.setPort( 8080 ); 00069 DBG("Setting proxy server\r\nHost: %s\r\nPort: %d\r\n", url.getHost().c_str(), url.getPort()); 00070 IpAddr ip; 00071 if( url.getHostIp(&ip) ) 00072 { 00073 m_server.setIp(ip); 00074 } 00075 else 00076 { 00077 DBG("DNS Query...\n"); 00078 m_pDnsReq = new DNSRequest(); 00079 //m_pDnsReq->setOnReply(this, &HTTPClient::onDNSReply); 00080 m_pDnsReq->resolve(&m_server); 00081 DBG("HTTPClient : DNSRequest %p\n", m_pDnsReq); 00082 } 00083 m_proxy = true; 00084 } 00085 00086 00087 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification 00088 { 00089 if(user==NULL) 00090 { 00091 m_reqHeaders.erase("Authorization"); //Remove auth str 00092 return; 00093 } 00094 string auth = "Basic "; 00095 string decStr = user; 00096 decStr += ":"; 00097 decStr += password; 00098 auth.append( Base64::encode(decStr) ); 00099 DBG("Auth str is %s\n", auth.c_str()); 00100 m_reqHeaders["Authorization"] = auth; 00101 } 00102 00103 //High Level setup functions 00104 HTTPResult HTTPClient::get(const char* uri, HTTPData* pDataIn) //Blocking 00105 { 00106 doGet(uri, pDataIn); 00107 return blockingProcess(); 00108 } 00109 00110 HTTPResult HTTPClient::get(const char* uri, HTTPData* pDataIn, void (*pMethod)(HTTPResult)) //Non blocking 00111 { 00112 setOnResult(pMethod); 00113 doGet(uri, pDataIn); 00114 return HTTP_PROCESSING; 00115 } 00116 00117 #if 0 //For info only 00118 template<class T> 00119 HTTPResult HTTPClient::get(const char* uri, HTTPData* pDataIn, T* pItem, void (T::*pMethod)(HTTPResult)) //Non blocking 00120 { 00121 setOnResult(pItem, pMethod); 00122 doGet(uri, pDataIn); 00123 return HTTP_PROCESSING; 00124 } 00125 #endif 00126 00127 HTTPResult HTTPClient::post(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn) //Blocking 00128 { 00129 doPost(uri, dataOut, pDataIn); 00130 return blockingProcess(); 00131 } 00132 00133 HTTPResult HTTPClient::post(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn, void (*pMethod)(HTTPResult)) //Non blocking 00134 { 00135 setOnResult(pMethod); 00136 doPost(uri, dataOut, pDataIn); 00137 return HTTP_PROCESSING; 00138 } 00139 00140 #if 0 //For info only 00141 template<class T> 00142 HTTPResult HTTPClient::post(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn, T* pItem, void (T::*pMethod)(HTTPResult)) //Non blocking 00143 { 00144 setOnResult(pItem, pMethod); 00145 doPost(uri, dataOut, pDataIn); 00146 return HTTP_PROCESSING; 00147 } 00148 #endif 00149 00150 void HTTPClient::doGet(const char* uri, HTTPData* pDataIn) 00151 { 00152 m_meth = HTTP_GET; 00153 setup(uri, NULL, pDataIn); 00154 } 00155 00156 void HTTPClient::doPost(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn) 00157 { 00158 m_meth = HTTP_POST; 00159 setup(uri, (HTTPData*) &dataOut, pDataIn); 00160 } 00161 00162 void HTTPClient::setOnResult( void (*pMethod)(HTTPResult) ) 00163 { 00164 m_pCb = pMethod; 00165 m_pCbItem = NULL; 00166 m_pCbMeth = NULL; 00167 } 00168 00169 #if 0 //For info only 00170 template<class T> 00171 void HTTPClient::setOnResult( T* pItem, void (T::*pMethod)(NtpResult) ) 00172 { 00173 m_pCb = NULL; 00174 m_pCbItem = (CDummy*) pItem; 00175 m_pCbMeth = (void (CDummy::*)(NtpResult)) pMethod; 00176 } 00177 #endif 00178 00179 void HTTPClient::setTimeout(int ms) 00180 { 00181 m_timeout = ms; 00182 //resetTimeout(); 00183 } 00184 00185 void HTTPClient::poll() //Called by NetServices 00186 { 00187 if(m_closed) 00188 { 00189 return; 00190 } 00191 if(m_watchdog.read_ms()>m_timeout) 00192 { 00193 onTimeout(); 00194 } 00195 else if(m_state == HTTP_READ_DATA_INCOMPLETE) 00196 { 00197 readData(); //Try to read more data 00198 if( m_state == HTTP_DONE ) 00199 { 00200 //All data has been read, close w/ success :) 00201 DBG("Done :)!\n"); 00202 onResult(HTTP_OK); 00203 close(); 00204 } 00205 } 00206 00207 } 00208 00209 int HTTPClient::getHTTPResponseCode() 00210 { 00211 return m_httpResponseCode; 00212 } 00213 00214 void HTTPClient::setRequestHeader(const string& header, const string& value) 00215 { 00216 m_reqHeaders[header] = value; 00217 } 00218 00219 string& HTTPClient::getResponseHeader(const string& header) 00220 { 00221 return m_respHeaders[header]; 00222 } 00223 00224 void HTTPClient::resetRequestHeaders() 00225 { 00226 m_reqHeaders.clear(); 00227 } 00228 00229 void HTTPClient::resetTimeout() 00230 { 00231 m_watchdog.reset(); 00232 m_watchdog.start(); 00233 } 00234 00235 void HTTPClient::init() //Create and setup socket if needed 00236 { 00237 close(); //Remove previous elements 00238 if(!m_closed) //Already opened 00239 return; 00240 m_state = HTTP_WRITE_HEADERS; 00241 m_pTCPSocket = new TCPSocket; 00242 m_pTCPSocket->setOnEvent(this, &HTTPClient::onTCPSocketEvent); 00243 m_closed = false; 00244 m_httpResponseCode = 0; 00245 } 00246 00247 void HTTPClient::close() 00248 { 00249 if(m_closed) 00250 return; 00251 m_state = HTTP_CLOSED; 00252 //Now Request headers are kept btw requests unless resetRequestHeaders() is called 00253 //m_reqHeaders.clear(); //Clear headers for next requests 00254 m_closed = true; //Prevent recursive calling or calling on an object being destructed by someone else 00255 m_watchdog.stop(); //Stop timeout 00256 m_watchdog.reset(); 00257 m_pTCPSocket->resetOnEvent(); 00258 m_pTCPSocket->close(); 00259 delete m_pTCPSocket; 00260 m_pTCPSocket = NULL; 00261 if( m_pDnsReq ) 00262 { 00263 m_pDnsReq->close(); 00264 delete m_pDnsReq; 00265 m_pDnsReq = NULL; 00266 } 00267 } 00268 00269 void HTTPClient::setup(const char* uri, HTTPData* pDataOut, HTTPData* pDataIn) //Setup request, make DNS Req if necessary 00270 { 00271 init(); //Initialize client in known state, create socket 00272 m_pDataOut = pDataOut; 00273 m_pDataIn = pDataIn; 00274 resetTimeout(); 00275 00276 //Erase previous headers 00277 //Do NOT clear m_reqHeaders as they might have already set before connecting 00278 m_respHeaders.clear(); 00279 00280 //Erase response buffer 00281 if(m_pDataIn) 00282 m_pDataIn->clear(); 00283 00284 //Assert that buffers are initialized properly 00285 m_dataLen = 0; 00286 m_bufRemainingLen = 0; 00287 00288 if ( m_proxy ) 00289 { 00290 // m_server is the proxy server, no need to parse the URL 00291 DBG("Using proxy\r\nHost: %s\r\nPort: %d\r\nURL: %s\n", m_server.getName(), m_server.getPort(), uri); 00292 // use the full URL as the request path 00293 m_path = uri; 00294 connect(); 00295 } 00296 else 00297 { 00298 // parse the server to connect to from the URL 00299 Url url; 00300 url.fromString(uri); 00301 00302 m_path = url.getPath(); 00303 00304 m_server.setName(url.getHost().c_str()); 00305 00306 if( url.getPort() > 0 ) 00307 { 00308 m_server.setPort( url.getPort() ); 00309 } 00310 else 00311 { 00312 m_server.setPort( HTTP_PORT ); 00313 } 00314 DBG("URL parsed,\r\nHost: %s\r\nPort: %d\r\nPath: %s\n", url.getHost().c_str(), url.getPort(), url.getPath().c_str()); 00315 IpAddr ip; 00316 if( url.getHostIp(&ip) ) 00317 { 00318 m_server.setIp(ip); 00319 connect(); 00320 } 00321 else 00322 { 00323 DBG("DNS Query...\n"); 00324 m_pDnsReq = new DNSRequest(); 00325 m_pDnsReq->setOnReply(this, &HTTPClient::onDNSReply); 00326 m_pDnsReq->resolve(&m_server); 00327 DBG("HTTPClient : DNSRequest %p\n", m_pDnsReq); 00328 // onDNSReply will call connect() 00329 } 00330 } 00331 } 00332 00333 void HTTPClient::connect() //Start Connection 00334 { 00335 resetTimeout(); 00336 DBG("Connecting...\n"); 00337 m_pTCPSocket->connect(m_server); 00338 } 00339 00340 #define MIN(a,b) ((a)<(b)?(a):(b)) 00341 #define ABS(a) (((a)>0)?(a):0) 00342 int HTTPClient::tryRead() //Try to read data from tcp packet and put in the HTTPData object 00343 { 00344 int len = 0; 00345 int readLen; 00346 do 00347 { 00348 if(m_state == HTTP_READ_DATA_INCOMPLETE) //First try to complete buffer copy 00349 { 00350 readLen = m_bufRemainingLen; 00351 /* if (readLen == 0) 00352 { 00353 m_state = HTTP_READ_DATA; 00354 continue; 00355 }*/ 00356 } 00357 else 00358 { 00359 readLen = m_pTCPSocket->recv(m_buf, MIN(ABS(m_dataLen-m_dataPos),CHUNK_SIZE)); 00360 if(readLen < 0) //Error 00361 { 00362 return readLen; 00363 } 00364 00365 DBG("%d bytes read\n", readLen); 00366 00367 m_pBufRemaining = m_buf; 00368 } 00369 if (readLen == 0) 00370 { 00371 m_state = HTTP_READ_DATA; 00372 return len; 00373 } 00374 00375 DBG("Trying to write %d bytes\n", readLen); 00376 00377 int writtenLen = m_pDataIn->write(m_pBufRemaining, readLen); 00378 m_dataPos += writtenLen; 00379 00380 DBG("%d bytes written\n", writtenLen); 00381 00382 if(writtenLen<readLen) //Data was not completely written 00383 { 00384 m_pBufRemaining += writtenLen; 00385 m_bufRemainingLen = readLen - writtenLen; 00386 m_state = HTTP_READ_DATA_INCOMPLETE; 00387 return len + writtenLen; 00388 } 00389 else 00390 { 00391 m_state = HTTP_READ_DATA; 00392 } 00393 len += readLen; 00394 } while(readLen>0); 00395 00396 return len; 00397 } 00398 00399 void HTTPClient::readData() //Data has been read 00400 { 00401 if(m_pDataIn == NULL) //Nothing to read (in HEAD for instance, not supported now) 00402 { 00403 m_state = HTTP_DONE; 00404 return; 00405 } 00406 DBG("Reading response...\n"); 00407 int len = 0; 00408 do 00409 { 00410 if(m_dataChunked && (m_state != HTTP_READ_DATA_INCOMPLETE)) 00411 { 00412 if(m_dataLen==0) 00413 { 00414 DBG("Reading chunk length...\n"); 00415 //New block 00416 static char chunkHeader[16]; 00417 //We use m_dataPos to retain the read position in chunkHeader, it has been set to 0 before the first call of readData() 00418 m_dataPos += readLine(chunkHeader + m_dataPos, ABS(16 - m_dataPos)); 00419 if( m_dataPos > 0 ) 00420 { 00421 if( chunkHeader[strlen(chunkHeader)-1] == 0x0d ) 00422 { 00423 sscanf(chunkHeader, "%x%*[^\r\n]", &m_dataLen); 00424 DBG("Chunk length is %d\n", m_dataLen); 00425 m_dataPos = 0; 00426 } 00427 else 00428 { 00429 //Wait for end of line 00430 DBG("Wait for CRLF\n"); 00431 return; 00432 } 00433 } 00434 else 00435 { 00436 DBG("Wait for data\n"); 00437 //Wait for data 00438 return; 00439 } 00440 } 00441 } 00442 00443 //Proper data recovery 00444 len = tryRead(); 00445 if(len<0) //Error 00446 { 00447 onResult(HTTP_CONN); 00448 return; 00449 } 00450 00451 if(len>0) 00452 resetTimeout(); 00453 00454 if(m_state == HTTP_READ_DATA_INCOMPLETE) 00455 return; 00456 00457 //Chunk Tail 00458 if(m_dataChunked) 00459 { 00460 if(m_dataPos >= m_dataLen) 00461 { 00462 DBG("Chunk read, wait for CRLF\n"); 00463 char chunkTail[3]; 00464 m_dataPos += readLine(chunkTail, 3); 00465 } 00466 00467 if(m_dataPos >= m_dataLen + 1) //1 == strlen("\n"), 00468 { 00469 DBG("End of chunk\n"); 00470 if(m_dataLen==0) 00471 { 00472 DBG("End of file\n"); 00473 //End of file 00474 m_state = HTTP_DONE; //Done 00475 } 00476 m_dataLen = 0; 00477 m_dataPos = 0; 00478 } 00479 } 00480 00481 } while(len>0); 00482 00483 00484 if(!m_dataChunked && (m_dataPos >= m_dataLen)) //All Data has been received 00485 { 00486 DBG("End of file\n"); 00487 m_state = HTTP_DONE; //Done 00488 } 00489 } 00490 00491 void HTTPClient::writeData() //Data has been written & buf is free 00492 { 00493 if(m_pDataOut == NULL) //Nothing to write (in POST for instance) 00494 { 00495 m_dataLen = 0; //Reset Data Length 00496 m_state = HTTP_READ_HEADERS; 00497 return; 00498 } 00499 int len = m_pDataOut->read(m_buf, CHUNK_SIZE); 00500 if( m_dataChunked ) 00501 { 00502 //Write chunk header 00503 char chunkHeader[16]; 00504 sprintf(chunkHeader, "%d\r\n", len); 00505 int ret = m_pTCPSocket->send(chunkHeader, strlen(chunkHeader)); 00506 if(ret < 0)//Error 00507 { 00508 onResult(HTTP_CONN); 00509 return; 00510 } 00511 } 00512 m_pTCPSocket->send(m_buf, len); 00513 m_dataPos+=len; 00514 if( m_dataChunked ) 00515 { 00516 m_pTCPSocket->send("\r\n", 2); //Chunk-terminating CRLF 00517 } 00518 if( ( !m_dataChunked && (m_dataPos >= m_dataLen) ) 00519 || ( m_dataChunked && !len ) ) //All Data has been sent 00520 { 00521 m_dataLen = 0; //Reset Data Length 00522 m_state = HTTP_READ_HEADERS; //Wait for resp 00523 } 00524 } 00525 00526 void HTTPClient::onTCPSocketEvent(TCPSocketEvent e) 00527 { 00528 DBG("Event %d in HTTPClient::onTCPSocketEvent()\n", e); 00529 00530 if(m_closed) 00531 { 00532 DBG("WARN: Discarded\n"); 00533 return; 00534 } 00535 00536 switch(e) 00537 { 00538 case TCPSOCKET_READABLE: //Incoming data 00539 resetTimeout(); 00540 switch(m_state) 00541 { 00542 case HTTP_READ_HEADERS: 00543 if( !readHeaders() ) 00544 { 00545 return; //Connection has been closed or incomplete data 00546 } 00547 if( m_pDataIn ) 00548 { 00549 //Data chunked? 00550 if(m_respHeaders["Transfer-Encoding"].find("chunked")!=string::npos) 00551 { 00552 m_dataChunked = true; 00553 m_dataPos = 0; 00554 m_dataLen = 0; 00555 DBG("Encoding is chunked, Content-Type is %s\n", m_respHeaders["Content-Type"].c_str() ); 00556 } 00557 else 00558 { 00559 m_dataChunked = false; 00560 int len = 0; 00561 //DBG("Preparing read... len = %s\n", m_respHeaders["Content-Length"].c_str()); 00562 sscanf(m_respHeaders["Content-Length"].c_str(), "%d", &len); 00563 m_pDataIn->setDataLen( len ); 00564 m_dataPos = 0; 00565 m_dataLen = len; 00566 DBG("Content-Length is %d, Content-Type is %s\n", len, m_respHeaders["Content-Type"].c_str() ); 00567 } 00568 m_pDataIn->setDataType( m_respHeaders["Content-Type"] ); 00569 } 00570 case HTTP_READ_DATA: 00571 readData(); 00572 break; 00573 case HTTP_READ_DATA_INCOMPLETE: 00574 break; //We need to handle previously received data first 00575 default: 00576 //Should not receive data now, req is not complete 00577 onResult(HTTP_PRTCL); 00578 } 00579 //All data has been read, close w/ success :) 00580 if( m_state == HTTP_DONE ) 00581 { 00582 DBG("Done :)!\n"); 00583 onResult(HTTP_OK); 00584 } 00585 break; 00586 case TCPSOCKET_CONNECTED: 00587 case TCPSOCKET_WRITEABLE: //We can send data 00588 resetTimeout(); 00589 switch(m_state) 00590 { 00591 case HTTP_WRITE_HEADERS: 00592 //Update headers fields according to m_pDataOut 00593 if( m_pDataOut ) 00594 { 00595 //Data is chunked? 00596 if(m_pDataOut->getIsChunked()) 00597 { 00598 m_dataChunked = true; 00599 m_reqHeaders.erase("Content-Length"); 00600 m_reqHeaders["Transfer-Encoding"] = "chunked"; 00601 } 00602 else 00603 { 00604 m_dataChunked = false; 00605 char c_len[16] = "0"; 00606 int len = m_pDataOut->getDataLen(); 00607 sprintf(c_len, "%d", len); 00608 m_dataPos = 0; 00609 m_dataLen = len; 00610 m_reqHeaders.erase("Transfer-Encoding"); 00611 m_reqHeaders["Content-Length"] = string(c_len); 00612 } 00613 string type = m_pDataOut->getDataType(); 00614 if(!type.empty()) 00615 { 00616 m_reqHeaders["Content-Type"] = type; 00617 } 00618 else 00619 { 00620 m_reqHeaders.erase("Content-Type"); 00621 } 00622 } 00623 if( !writeHeaders() ) 00624 { 00625 return; //Connection has been closed 00626 } 00627 break; //Wait for writeable event before sending payload 00628 case HTTP_WRITE_DATA: 00629 writeData(); 00630 break; 00631 } 00632 //Otherwise request has been sent, now wait for resp 00633 break; 00634 case TCPSOCKET_CONTIMEOUT: 00635 case TCPSOCKET_CONRST: 00636 case TCPSOCKET_CONABRT: 00637 case TCPSOCKET_ERROR: 00638 DBG("Connection error.\n"); 00639 onResult(HTTP_CONN); 00640 case TCPSOCKET_DISCONNECTED: 00641 //There might still be some data available for reading 00642 //So if we are in a reading state, do not close the socket yet 00643 if( (m_state != HTTP_READ_DATA_INCOMPLETE) && (m_state != HTTP_DONE) && (m_state != HTTP_CLOSED) ) 00644 { 00645 onResult(HTTP_CONN); 00646 } 00647 DBG("Connection closed by remote host.\n"); 00648 break; 00649 } 00650 } 00651 00652 void HTTPClient::onDNSReply(DNSReply r) 00653 { 00654 if(m_closed) 00655 { 00656 DBG("WARN: Discarded\n"); 00657 return; 00658 } 00659 00660 if( r != DNS_FOUND ) 00661 { 00662 DBG("Could not resolve hostname.\n"); 00663 onResult(HTTP_DNS); 00664 return; 00665 } 00666 00667 DBG("DNS Resolved to %d.%d.%d.%d.\n",m_server.getIp()[0],m_server.getIp()[1],m_server.getIp()[2],m_server.getIp()[3]); 00668 //If no error, m_server has been updated by m_pDnsReq so we're set to go ! 00669 m_pDnsReq->close(); 00670 delete m_pDnsReq; 00671 m_pDnsReq = NULL; 00672 connect(); 00673 } 00674 00675 void HTTPClient::onResult(HTTPResult r) //Called when exchange completed or on failure 00676 { 00677 if(m_pCbItem && m_pCbMeth) 00678 (m_pCbItem->*m_pCbMeth)(r); 00679 else if(m_pCb) 00680 m_pCb(r); 00681 m_blockingResult = r; //Blocking mode 00682 close(); //FIXME:Remove suppl. close() calls 00683 } 00684 00685 void HTTPClient::onTimeout() //Connection has timed out 00686 { 00687 DBG("Timed out.\n"); 00688 onResult(HTTP_TIMEOUT); 00689 close(); 00690 } 00691 00692 //Headers 00693 00694 //TODO: Factorize w/ HTTPRequestHandler in a single HTTPHeader class 00695 00696 HTTPResult HTTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available 00697 { 00698 //Disable callbacks 00699 m_pCb = NULL; 00700 m_pCbItem = NULL; 00701 m_pCbMeth = NULL; 00702 m_blockingResult = HTTP_PROCESSING; 00703 do 00704 { 00705 Net::poll(); 00706 } while(m_blockingResult == HTTP_PROCESSING); 00707 Net::poll(); //Necessary for cleanup 00708 return m_blockingResult; 00709 } 00710 00711 bool HTTPClient::readHeaders() 00712 { 00713 static char* line = m_buf; 00714 static char key[128]; 00715 static char value[128]; 00716 if(!m_dataLen) //No incomplete header in buffer, this is the first time we read data 00717 { 00718 if( readLine(line, 128) > 0 ) 00719 { 00720 //Check RC 00721 m_httpResponseCode = 0; 00722 if( sscanf(line, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) 00723 { 00724 //Cannot match string, error 00725 DBG("Not a correct HTTP answer : %s\n", line); 00726 onResult(HTTP_PRTCL); 00727 close(); 00728 return false; 00729 } 00730 00731 if(m_httpResponseCode != 200) 00732 { 00733 DBG("Response: error code %d\n", m_httpResponseCode); 00734 HTTPResult res = HTTP_ERROR; 00735 switch(m_httpResponseCode) 00736 { 00737 case 404: 00738 res = HTTP_NOTFOUND; 00739 break; 00740 case 403: 00741 res = HTTP_REFUSED; 00742 break; 00743 default: 00744 res = HTTP_ERROR; 00745 } 00746 onResult(res); 00747 close(); 00748 return false; 00749 } 00750 DBG("Response OK\n"); 00751 } 00752 else 00753 { 00754 //Empty packet, weird! 00755 DBG("Empty packet!\n"); 00756 onResult(HTTP_PRTCL); 00757 close(); 00758 return false; 00759 } 00760 } 00761 bool incomplete = false; 00762 while( true ) 00763 { 00764 int readLen = readLine(line + m_dataLen, 128 - m_dataLen, &incomplete); 00765 m_dataLen = 0; 00766 if( readLen <= 2 ) //if == 1 or 2, it is an empty line = end of headers 00767 { 00768 DBG("All headers read.\n"); 00769 m_state = HTTP_READ_DATA; 00770 break; 00771 } 00772 else if( incomplete == true ) 00773 { 00774 m_dataLen = readLen;//Sets data length available in buffer 00775 return false; 00776 } 00777 //DBG("Header : %s\n", line); 00778 int n = sscanf(line, "%[^:] : %[^\r\n]", key, value); 00779 if ( n == 2 ) 00780 { 00781 DBG("Read header : %s: %s\n", key, value); 00782 m_respHeaders[key] = value; 00783 } 00784 //TODO: Impl n==1 case (part 2 of previous header) 00785 } 00786 00787 return true; 00788 } 00789 00790 bool HTTPClient::writeHeaders() //Called at the first writeData call 00791 { 00792 static char* line = m_buf; 00793 const char* HTTP_METH_STR[] = {"GET", "POST", "HEAD"}; 00794 00795 //Req 00796 sprintf(line, "%s %s HTTP/1.1\r\nHost: %s\r\n", HTTP_METH_STR[m_meth], m_path.c_str(), m_server.getName()); //Write request 00797 m_pTCPSocket->send(line, strlen(line)); 00798 DBG("Request: %s\n", line); 00799 00800 DBG("Writing headers:\n"); 00801 map<string,string>::iterator it; 00802 for( it = m_reqHeaders.begin(); it != m_reqHeaders.end(); it++ ) 00803 { 00804 sprintf(line, "%s: %s\r\n", (*it).first.c_str(), (*it).second.c_str() ); 00805 DBG("\r\n%s", line); 00806 m_pTCPSocket->send(line, strlen(line)); 00807 } 00808 m_pTCPSocket->send("\r\n",2); //End of head 00809 m_state = HTTP_WRITE_DATA; 00810 return true; 00811 } 00812 00813 int HTTPClient::readLine(char* str, int maxLen, bool* pIncomplete /* = NULL*/) 00814 { 00815 int ret; 00816 int len = 0; 00817 if(pIncomplete) 00818 *pIncomplete = false; 00819 for(int i = 0; i < maxLen - 1; i++) 00820 { 00821 ret = m_pTCPSocket->recv(str, 1); 00822 if(ret != 1) 00823 { 00824 if(pIncomplete) 00825 *pIncomplete = true; 00826 break; 00827 } 00828 if( (len > 1) && *(str-1)=='\r' && *str=='\n' ) 00829 { 00830 break; 00831 } 00832 else if( *str=='\n' ) 00833 { 00834 break; 00835 } 00836 str++; 00837 len++; 00838 } 00839 *str = 0; 00840 return len; 00841 }
Generated on Tue Jul 12 2022 14:12:34 by
1.7.2
