Harmeet Bindra / Mbed 2 deprecated Warehouse_navigator

Dependencies:   Motordriver mbed

Fork of Alexa_Client by Harmeet Bindra

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPClient.cpp Source File

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 }