Darran Shepherd
/
AutoIpNetStack
Net stack with AutoIP enabled
Embed:
(wiki syntax)
Show/hide line numbers
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("\r\nNew 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("\r\nAuth 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 = 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_ms()>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("\r\nDone :)!\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("\r\nURL 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("\r\nDNS Query...\r\n"); 00281 m_pDnsReq = new DNSRequest(); 00282 m_pDnsReq->setOnReply(this, &HTTPClient::onDNSReply); 00283 m_pDnsReq->resolve(&m_server); 00284 DBG("\r\nHTTPClient : DNSRequest %p\r\n", m_pDnsReq); 00285 } 00286 00287 } 00288 00289 void HTTPClient::connect() //Start Connection 00290 { 00291 resetTimeout(); 00292 DBG("\r\nConnecting...\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("\r\nReading 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("\r\nReading 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("\r\nChunk length is %d\r\n", m_dataLen); 00374 m_dataPos = 0; 00375 } 00376 else 00377 { 00378 //Wait for end of line 00379 DBG("\r\nWait for CRLF\r\n"); 00380 return; 00381 } 00382 } 00383 else 00384 { 00385 DBG("\r\nWait 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(len>0) 00401 resetTimeout(); 00402 00403 if(m_state == HTTP_READ_DATA_INCOMPLETE) 00404 return; 00405 00406 //Chunk Tail 00407 if(m_dataChunked) 00408 { 00409 if(m_dataPos >= m_dataLen) 00410 { 00411 DBG("\r\nChunk read, wait for CRLF\r\n"); 00412 char chunkTail[3]; 00413 m_dataPos += readLine(chunkTail, 3); 00414 } 00415 00416 if(m_dataPos >= m_dataLen + 1) //1 == strlen("\n"), 00417 { 00418 DBG("\r\nEnd of chunk\r\n"); 00419 if(m_dataLen==0) 00420 { 00421 DBG("\r\nEnd of file\r\n"); 00422 //End of file 00423 m_state = HTTP_DONE; //Done 00424 } 00425 m_dataLen = 0; 00426 m_dataPos = 0; 00427 } 00428 } 00429 00430 } while(len>0); 00431 00432 00433 if(!m_dataChunked && (m_dataPos >= m_dataLen)) //All Data has been received 00434 { 00435 DBG("\r\nEnd of file\r\n"); 00436 m_state = HTTP_DONE; //Done 00437 } 00438 } 00439 00440 void HTTPClient::writeData() //Data has been written & buf is free 00441 { 00442 if(m_pDataOut == NULL) //Nothing to write (in POST for instance) 00443 { 00444 m_dataLen = 0; //Reset Data Length 00445 m_state = HTTP_READ_HEADERS; 00446 return; 00447 } 00448 int len = m_pDataOut->read(m_buf, CHUNK_SIZE); 00449 if( m_dataChunked ) 00450 { 00451 //Write chunk header 00452 char chunkHeader[16]; 00453 sprintf(chunkHeader, "%d\r\n", len); 00454 int ret = m_pTCPSocket->send(chunkHeader, strlen(chunkHeader)); 00455 if(ret < 0)//Error 00456 { 00457 onResult(HTTP_CONN); 00458 return; 00459 } 00460 } 00461 m_pTCPSocket->send(m_buf, len); 00462 m_dataPos+=len; 00463 if( m_dataChunked ) 00464 { 00465 m_pTCPSocket->send("\r\n", 2); //Chunk terminating CRLF 00466 } 00467 if( ( !m_dataChunked && (m_dataPos >= m_dataLen) ) 00468 || ( m_dataChunked && !len ) ) //All Data has been sent 00469 { 00470 m_dataLen = 0; //Reset Data Length 00471 m_state = HTTP_READ_HEADERS; //Wait for resp 00472 } 00473 } 00474 00475 void HTTPClient::onTCPSocketEvent(TCPSocketEvent e) 00476 { 00477 DBG("\r\nEvent %d in HTTPClient::onTCPSocketEvent()\r\n", e); 00478 00479 if(m_closed) 00480 { 00481 DBG("\r\nWARN: Discarded\r\n"); 00482 return; 00483 } 00484 00485 switch(e) 00486 { 00487 case TCPSOCKET_READABLE: //Incoming data 00488 resetTimeout(); 00489 switch(m_state) 00490 { 00491 case HTTP_READ_HEADERS: 00492 if( !readHeaders() ) 00493 { 00494 return; //Connection has been closed or incomplete data 00495 } 00496 if( m_pDataIn ) 00497 { 00498 //Data chunked? 00499 if(m_respHeaders["Transfer-Encoding"].find("chunked")!=string::npos) 00500 { 00501 m_dataChunked = true; 00502 m_dataPos = 0; 00503 m_dataLen = 0; 00504 DBG("\r\nEncoding is chunked, Content-Type is %s\r\n", m_respHeaders["Content-Type"].c_str() ); 00505 } 00506 else 00507 { 00508 m_dataChunked = false; 00509 int len = 0; 00510 //DBG("\r\nPreparing read... len = %s\r\n", m_respHeaders["Content-Length"].c_str()); 00511 sscanf(m_respHeaders["Content-Length"].c_str(), "%d", &len); 00512 m_pDataIn->setDataLen( len ); 00513 m_dataPos = 0; 00514 m_dataLen = len; 00515 DBG("\r\nContent-Length is %d, Content-Type is %s\r\n", len, m_respHeaders["Content-Type"].c_str() ); 00516 } 00517 m_pDataIn->setDataType( m_respHeaders["Content-Type"] ); 00518 } 00519 case HTTP_READ_DATA: 00520 readData(); 00521 break; 00522 case HTTP_READ_DATA_INCOMPLETE: 00523 break; //We need to handle previously received data first 00524 default: 00525 //Should not receive data now, req is not complete 00526 onResult(HTTP_PRTCL); 00527 } 00528 //All data has been read, close w/ success :) 00529 if( m_state == HTTP_DONE ) 00530 { 00531 DBG("\r\nDone :)!\r\n"); 00532 onResult(HTTP_OK); 00533 } 00534 break; 00535 case TCPSOCKET_CONNECTED: 00536 case TCPSOCKET_WRITEABLE: //We can send data 00537 resetTimeout(); 00538 switch(m_state) 00539 { 00540 case HTTP_WRITE_HEADERS: 00541 //Update headers fields according to m_pDataOut 00542 if( m_pDataOut ) 00543 { 00544 //Data is chunked? 00545 if(m_pDataOut->getIsChunked()) 00546 { 00547 m_dataChunked = true; 00548 m_reqHeaders.erase("Content-Length"); 00549 m_reqHeaders["Transfer-Encoding"] = "chunked"; 00550 } 00551 else 00552 { 00553 m_dataChunked = false; 00554 char c_len[16] = "0"; 00555 int len = m_pDataOut->getDataLen(); 00556 sprintf(c_len, "%d", len); 00557 m_dataPos = 0; 00558 m_dataLen = len; 00559 m_reqHeaders.erase("Transfer-Encoding"); 00560 m_reqHeaders["Content-Length"] = string(c_len); 00561 } 00562 string type = m_pDataOut->getDataType(); 00563 if(!type.empty()) 00564 { 00565 m_reqHeaders["Content-Type"] = type; 00566 } 00567 else 00568 { 00569 m_reqHeaders.erase("Content-Type"); 00570 } 00571 } 00572 if( !writeHeaders() ) 00573 { 00574 return; //Connection has been closed 00575 } 00576 break; //Wait for writeable event before sending payload 00577 case HTTP_WRITE_DATA: 00578 writeData(); 00579 break; 00580 } 00581 //Otherwise request has been sent, now wait for resp 00582 break; 00583 case TCPSOCKET_CONTIMEOUT: 00584 case TCPSOCKET_CONRST: 00585 case TCPSOCKET_CONABRT: 00586 case TCPSOCKET_ERROR: 00587 DBG("\r\nConnection error.\r\n"); 00588 onResult(HTTP_CONN); 00589 case TCPSOCKET_DISCONNECTED: 00590 //There might still be some data available for reading 00591 //So if we are in a reading state, do not close the socket yet 00592 if( (m_state != HTTP_READ_DATA_INCOMPLETE) && (m_state != HTTP_DONE) && (m_state != HTTP_CLOSED) ) 00593 { 00594 onResult(HTTP_CONN); 00595 } 00596 DBG("\r\nConnection closed by remote host.\r\n"); 00597 break; 00598 } 00599 } 00600 00601 void HTTPClient::onDNSReply(DNSReply r) 00602 { 00603 if(m_closed) 00604 { 00605 DBG("\r\nWARN: Discarded\r\n"); 00606 return; 00607 } 00608 00609 if( r != DNS_FOUND ) 00610 { 00611 DBG("\r\nCould not resolve hostname.\r\n"); 00612 onResult(HTTP_DNS); 00613 return; 00614 } 00615 00616 DBG("\r\nDNS Resolved to %d.%d.%d.%d.\r\n",m_server.getIp()[0],m_server.getIp()[1],m_server.getIp()[2],m_server.getIp()[3]); 00617 //If no error, m_server has been updated by m_pDnsReq so we're set to go ! 00618 m_pDnsReq->close(); 00619 delete m_pDnsReq; 00620 m_pDnsReq = NULL; 00621 connect(); 00622 } 00623 00624 void HTTPClient::onResult(HTTPResult r) //Called when exchange completed or on failure 00625 { 00626 if(m_pCbItem && m_pCbMeth) 00627 (m_pCbItem->*m_pCbMeth)(r); 00628 else if(m_pCb) 00629 m_pCb(r); 00630 m_blockingResult = r; //Blocking mode 00631 close(); //FIXME:Remove suppl. close() calls 00632 } 00633 00634 void HTTPClient::onTimeout() //Connection has timed out 00635 { 00636 DBG("\r\nTimed out.\n"); 00637 onResult(HTTP_TIMEOUT); 00638 close(); 00639 } 00640 00641 //Headers 00642 00643 //TODO: Factorize w/ HTTPRequestHandler in a single HTTPHeader class 00644 00645 HTTPResult HTTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available 00646 { 00647 //Disable callbacks 00648 m_pCb = NULL; 00649 m_pCbItem = NULL; 00650 m_pCbMeth = NULL; 00651 m_blockingResult = HTTP_PROCESSING; 00652 do 00653 { 00654 Net::poll(); 00655 } while(m_blockingResult == HTTP_PROCESSING); 00656 Net::poll(); //Necessary for cleanup 00657 return m_blockingResult; 00658 } 00659 00660 bool HTTPClient::readHeaders() 00661 { 00662 static char* line = m_buf; 00663 static char key[128]; 00664 static char value[128]; 00665 if(!m_dataLen) //No incomplete header in buffer, this is the first time we read data 00666 { 00667 if( readLine(line, 128) > 0 ) 00668 { 00669 //Check RC 00670 m_httpResponseCode = 0; 00671 if( sscanf(line, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) 00672 { 00673 //Cannot match string, error 00674 DBG("\r\nNot a correct HTTP answer : %s\r\n", line); 00675 onResult(HTTP_PRTCL); 00676 close(); 00677 return false; 00678 } 00679 00680 if(m_httpResponseCode != 200) 00681 { 00682 DBG("\r\nResponse: error code %d\r\n", m_httpResponseCode); 00683 HTTPResult res = HTTP_ERROR; 00684 switch(m_httpResponseCode) 00685 { 00686 case 404: 00687 res = HTTP_NOTFOUND; 00688 break; 00689 case 403: 00690 res = HTTP_REFUSED; 00691 break; 00692 default: 00693 res = HTTP_ERROR; 00694 } 00695 onResult(res); 00696 close(); 00697 return false; 00698 } 00699 DBG("\r\nResponse OK\r\n"); 00700 } 00701 else 00702 { 00703 //Empty packet, weird! 00704 DBG("\r\nEmpty packet!\r\n"); 00705 onResult(HTTP_PRTCL); 00706 close(); 00707 return false; 00708 } 00709 } 00710 bool incomplete = false; 00711 while( true ) 00712 { 00713 int readLen = readLine(line + m_dataLen, 128 - m_dataLen, &incomplete); 00714 m_dataLen = 0; 00715 if( readLen <= 2 ) //if == 1 or 2, it is an empty line = end of headers 00716 { 00717 DBG("\r\nAll headers read.\r\n"); 00718 m_state = HTTP_READ_DATA; 00719 break; 00720 } 00721 else if( incomplete == true ) 00722 { 00723 m_dataLen = readLen;//Sets data length available in buffer 00724 return false; 00725 } 00726 //DBG("\r\nHeader : %s\r\n", line); 00727 int n = sscanf(line, "%[^:] : %[^\r\n]", key, value); 00728 if ( n == 2 ) 00729 { 00730 DBG("\r\nRead header : %s: %s\r\n", key, value); 00731 m_respHeaders[key] = value; 00732 } 00733 //TODO: Impl n==1 case (part 2 of previous header) 00734 } 00735 00736 return true; 00737 } 00738 00739 bool HTTPClient::writeHeaders() //Called at the first writeData call 00740 { 00741 static char* line = m_buf; 00742 const char* HTTP_METH_STR[] = {"GET", "POST", "HEAD"}; 00743 00744 //Req 00745 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 00746 m_pTCPSocket->send(line, strlen(line)); 00747 DBG("\r\nRequest: %s\r\n", line); 00748 00749 DBG("\r\nWriting headers:\r\n"); 00750 map<string,string>::iterator it; 00751 for( it = m_reqHeaders.begin(); it != m_reqHeaders.end(); it++ ) 00752 { 00753 sprintf(line, "%s: %s\r\n", (*it).first.c_str(), (*it).second.c_str() ); 00754 DBG("\r\n%s", line); 00755 m_pTCPSocket->send(line, strlen(line)); 00756 } 00757 m_pTCPSocket->send("\r\n",2); //End of head 00758 m_state = HTTP_WRITE_DATA; 00759 return true; 00760 } 00761 00762 int HTTPClient::readLine(char* str, int maxLen, bool* pIncomplete /* = NULL*/) 00763 { 00764 int ret; 00765 int len = 0; 00766 if(pIncomplete) 00767 *pIncomplete = false; 00768 for(int i = 0; i < maxLen - 1; i++) 00769 { 00770 ret = m_pTCPSocket->recv(str, 1); 00771 if(ret != 1) 00772 { 00773 if(pIncomplete) 00774 *pIncomplete = true; 00775 break; 00776 } 00777 if( (len > 1) && *(str-1)=='\r' && *str=='\n' ) 00778 { 00779 break; 00780 } 00781 else if( *str=='\n' ) 00782 { 00783 break; 00784 } 00785 str++; 00786 len++; 00787 } 00788 *str = 0; 00789 return len; 00790 }
Generated on Tue Jul 12 2022 15:37:03 by 1.7.2