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