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