NetServices Stack source

Dependents:   HelloWorld ServoInterfaceBoardExample1 4180_Lab4

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   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 }