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