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