Igor Skochinsky / NetServicesSource
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_proxy(false), 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   if ( m_proxy )
00256   {
00257     // m_server is the proxy server, no need to parse the URL
00258     DBG("Using proxy\r\nHost: %s\r\nPort: %d\r\nURL: %s\n", m_server.getName().c_str(), m_server.getPort(), uri);
00259     // use the full URL as the request path
00260     m_path = uri;
00261     connect();
00262   }
00263   else
00264   {
00265     // parse the server to connect to from the URL
00266     Url url;
00267     url.fromString(uri);
00268     
00269     m_path = url.getPath();
00270     
00271     m_server.setName(url.getHost().c_str());
00272     
00273     if( url.getPort() > 0 )
00274     {
00275       m_server.setPort( url.getPort() );
00276     }
00277     else
00278     {
00279       m_server.setPort( HTTP_PORT );
00280     }      
00281     DBG("URL parsed,\r\nHost: %s\r\nPort: %d\r\nPath: %s\n", url.getHost().c_str(), url.getPort(), url.getPath().c_str());
00282     IpAddr ip;
00283     if( url.getHostIp(&ip) )
00284     {
00285       m_server.setIp(ip);
00286       connect();
00287     }
00288     else
00289     {
00290       DBG("DNS Query...\n");
00291       m_pDnsReq = new DNSRequest();
00292       m_pDnsReq->setOnReply(this, &HTTPClient::onDNSReply);
00293       m_pDnsReq->resolve(&m_server);
00294       DBG("HTTPClient : DNSRequest %p\n", m_pDnsReq);
00295       // onDNSReply will call connect()
00296     }
00297   }
00298 }
00299 
00300 void HTTPClient::connect() //Start Connection
00301 {
00302   resetTimeout();
00303   DBG("Connecting...\n");
00304   m_pTCPSocket->connect(m_server);
00305 }
00306 
00307 #define MIN(a,b) ((a)<(b)?(a):(b))
00308 #define ABS(a) (((a)>0)?(a):0)
00309 int HTTPClient::tryRead() //Try to read data from tcp packet and put in the HTTPData object
00310 {
00311   int len = 0;
00312   int readLen;
00313   do
00314   {
00315     if(m_state == HTTP_READ_DATA_INCOMPLETE) //First try to complete buffer copy
00316     {
00317       readLen = m_bufRemainingLen;
00318    /*   if (readLen == 0)
00319       {
00320         m_state = HTTP_READ_DATA;
00321         continue;
00322       }*/
00323     }
00324     else
00325     {
00326       readLen = m_pTCPSocket->recv(m_buf, MIN(ABS(m_dataLen-m_dataPos),CHUNK_SIZE));
00327       if(readLen < 0) //Error
00328       {
00329         return readLen;
00330       }
00331       
00332       DBG("%d bytes read\n", readLen);
00333       
00334       m_pBufRemaining = m_buf;
00335     }
00336     if (readLen == 0)
00337     { 
00338       m_state = HTTP_READ_DATA;
00339       return len;
00340     }
00341     
00342     DBG("Trying to write %d bytes\n", readLen);
00343   
00344     int writtenLen = m_pDataIn->write(m_pBufRemaining, readLen);
00345     m_dataPos += writtenLen;
00346     
00347     DBG("%d bytes written\n", writtenLen);
00348      
00349     if(writtenLen<readLen) //Data was not completely written
00350     {
00351       m_pBufRemaining += writtenLen;
00352       m_bufRemainingLen = readLen - writtenLen;
00353       m_state = HTTP_READ_DATA_INCOMPLETE;
00354       return len + writtenLen;
00355     }
00356     else
00357     {
00358       m_state = HTTP_READ_DATA;
00359     }
00360     len += readLen;
00361   } while(readLen>0);
00362   
00363   return len;
00364 }
00365 
00366 void HTTPClient::readData() //Data has been read
00367 {
00368   if(m_pDataIn == NULL) //Nothing to read (in HEAD for instance, not supported now)
00369   {
00370     m_state = HTTP_DONE;
00371     return;
00372   }
00373   DBG("Reading response...\n");
00374   int len = 0;
00375   do
00376   {
00377     if(m_dataChunked && (m_state != HTTP_READ_DATA_INCOMPLETE))
00378     {
00379       if(m_dataLen==0)
00380       {
00381         DBG("Reading chunk length...\n");
00382         //New block
00383         static char chunkHeader[16];
00384         //We use m_dataPos to retain the read position in chunkHeader, it has been set to 0 before the first call of readData()
00385         m_dataPos += readLine(chunkHeader + m_dataPos, ABS(16 - m_dataPos));
00386         if( m_dataPos > 0 )
00387         {
00388           if( chunkHeader[strlen(chunkHeader)-1] == 0x0d )
00389           {
00390             sscanf(chunkHeader, "%x%*[^\r\n]", &m_dataLen);
00391             DBG("Chunk length is %d\n", m_dataLen);
00392             m_dataPos = 0;
00393           }
00394           else
00395           {
00396             //Wait for end of line
00397             DBG("Wait for CRLF\n");
00398             return;
00399           }
00400         }
00401         else
00402         {
00403           DBG("Wait for data\n");
00404           //Wait for data
00405           return;
00406         }
00407       }
00408     }  
00409       
00410     //Proper data recovery
00411     len = tryRead();
00412     if(len<0) //Error
00413     {
00414       onResult(HTTP_CONN);
00415       return;
00416     }
00417 
00418     if(len>0)
00419       resetTimeout();
00420     
00421     if(m_state == HTTP_READ_DATA_INCOMPLETE)
00422       return;
00423       
00424     //Chunk Tail
00425     if(m_dataChunked)
00426     {  
00427       if(m_dataPos >= m_dataLen)
00428       {
00429         DBG("Chunk read, wait for CRLF\n");
00430         char chunkTail[3];
00431         m_dataPos += readLine(chunkTail, 3);
00432       }
00433       
00434       if(m_dataPos >= m_dataLen + 1) //1 == strlen("\n"),
00435       {
00436         DBG("End of chunk\n");
00437         if(m_dataLen==0)
00438         {
00439           DBG("End of file\n");
00440           //End of file
00441           m_state = HTTP_DONE; //Done
00442         }
00443         m_dataLen = 0;
00444         m_dataPos = 0;
00445       }
00446     }
00447   
00448   } while(len>0);
00449   
00450   
00451   if(!m_dataChunked && (m_dataPos >= m_dataLen)) //All Data has been received
00452   {
00453     DBG("End of file\n");
00454     m_state = HTTP_DONE; //Done
00455   }
00456 }
00457 
00458 void HTTPClient::writeData() //Data has been written & buf is free
00459 {
00460   if(m_pDataOut == NULL) //Nothing to write (in POST for instance)
00461   {
00462     m_dataLen = 0; //Reset Data Length
00463     m_state = HTTP_READ_HEADERS;
00464     return;
00465   }
00466   int len = m_pDataOut->read(m_buf, CHUNK_SIZE);
00467   if( m_dataChunked )
00468   {
00469     //Write chunk header
00470     char chunkHeader[16];
00471     sprintf(chunkHeader, "%d\r\n", len);
00472     int ret = m_pTCPSocket->send(chunkHeader, strlen(chunkHeader));
00473     if(ret < 0)//Error
00474     {
00475       onResult(HTTP_CONN);
00476       return;
00477     }
00478   }
00479   m_pTCPSocket->send(m_buf, len);
00480   m_dataPos+=len;
00481   if( m_dataChunked )
00482   {
00483     m_pTCPSocket->send("\r\n", 2); //Chunk-terminating CRLF
00484   }
00485   if( ( !m_dataChunked && (m_dataPos >= m_dataLen) )
00486     || ( m_dataChunked && !len ) ) //All Data has been sent
00487   {
00488     m_dataLen = 0; //Reset Data Length
00489     m_state = HTTP_READ_HEADERS; //Wait for resp
00490   }
00491 }
00492   
00493 void HTTPClient::onTCPSocketEvent(TCPSocketEvent e)
00494 {
00495   DBG("Event %d in HTTPClient::onTCPSocketEvent()\n", e);
00496 
00497   if(m_closed)
00498   {
00499     DBG("WARN: Discarded\n");
00500     return;
00501   }
00502 
00503   switch(e)
00504   {
00505   case TCPSOCKET_READABLE: //Incoming data
00506     resetTimeout();
00507     switch(m_state)
00508     {
00509     case HTTP_READ_HEADERS:
00510       if( !readHeaders() ) 
00511       {
00512         return; //Connection has been closed or incomplete data
00513       }
00514       if( m_pDataIn )
00515       {
00516         //Data chunked?
00517         if(m_respHeaders["Transfer-Encoding"].find("chunked")!=string::npos)
00518         {
00519           m_dataChunked = true;
00520           m_dataPos = 0;
00521           m_dataLen = 0; 
00522           DBG("Encoding is chunked, Content-Type is %s\n", m_respHeaders["Content-Type"].c_str() );
00523         }
00524         else
00525         {    
00526           m_dataChunked = false;
00527           int len = 0;
00528           //DBG("Preparing read... len = %s\n", m_respHeaders["Content-Length"].c_str());
00529           sscanf(m_respHeaders["Content-Length"].c_str(), "%d", &len);
00530           m_pDataIn->setDataLen( len );
00531           m_dataPos = 0;
00532           m_dataLen = len; 
00533           DBG("Content-Length is %d, Content-Type is %s\n", len, m_respHeaders["Content-Type"].c_str() );
00534         }
00535         m_pDataIn->setDataType( m_respHeaders["Content-Type"] );
00536       }
00537     case HTTP_READ_DATA:
00538       readData();
00539       break;
00540     case HTTP_READ_DATA_INCOMPLETE:
00541       break; //We need to handle previously received data first
00542     default:
00543       //Should not receive data now, req is not complete
00544       onResult(HTTP_PRTCL);
00545     }
00546  //All data has been read, close w/ success :)
00547     if( m_state == HTTP_DONE ) 
00548     {
00549       DBG("Done :)!\n");
00550       onResult(HTTP_OK);
00551     }
00552     break;
00553   case TCPSOCKET_CONNECTED:
00554   case TCPSOCKET_WRITEABLE: //We can send data
00555     resetTimeout();
00556     switch(m_state)
00557     {
00558     case HTTP_WRITE_HEADERS:
00559       //Update headers fields according to m_pDataOut
00560       if( m_pDataOut )
00561       {
00562         //Data is chunked?
00563         if(m_pDataOut->getIsChunked())
00564         {
00565           m_dataChunked = true;
00566           m_reqHeaders.erase("Content-Length");
00567           m_reqHeaders["Transfer-Encoding"] = "chunked";
00568         }
00569         else
00570         {
00571           m_dataChunked = false;
00572           char c_len[16] = "0";
00573           int len = m_pDataOut->getDataLen();
00574           sprintf(c_len, "%d", len);
00575           m_dataPos = 0;
00576           m_dataLen = len;
00577           m_reqHeaders.erase("Transfer-Encoding");
00578           m_reqHeaders["Content-Length"] = string(c_len);
00579         } 
00580         string type = m_pDataOut->getDataType();
00581         if(!type.empty())
00582         {
00583           m_reqHeaders["Content-Type"] = type;
00584         }
00585         else
00586         {
00587           m_reqHeaders.erase("Content-Type");
00588         }
00589       }
00590       if( !writeHeaders() ) 
00591       {
00592         return; //Connection has been closed
00593       }
00594       break; //Wait for writeable event before sending payload
00595     case HTTP_WRITE_DATA:
00596       writeData();
00597       break;
00598     }
00599     //Otherwise request has been sent, now wait for resp
00600     break;
00601   case TCPSOCKET_CONTIMEOUT:
00602   case TCPSOCKET_CONRST:
00603   case TCPSOCKET_CONABRT:
00604   case TCPSOCKET_ERROR:
00605     DBG("Connection error.\n");
00606     onResult(HTTP_CONN);
00607   case TCPSOCKET_DISCONNECTED:
00608     //There might still be some data available for reading
00609     //So if we are in a reading state, do not close the socket yet
00610     if( (m_state != HTTP_READ_DATA_INCOMPLETE) && (m_state != HTTP_DONE) && (m_state != HTTP_CLOSED)  )
00611     {
00612       onResult(HTTP_CONN);
00613     }
00614     DBG("Connection closed by remote host.\n");
00615     break;
00616   }
00617 }
00618 
00619 void HTTPClient::onDNSReply(DNSReply r)
00620 {
00621   if(m_closed)
00622   {
00623     DBG("WARN: Discarded\n");
00624     return;
00625   }
00626   
00627   if( r != DNS_FOUND )
00628   {
00629     DBG("Could not resolve hostname.\n");
00630     onResult(HTTP_DNS);
00631     return;
00632   }
00633   
00634   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]);
00635   //If no error, m_server has been updated by m_pDnsReq so we're set to go !
00636   m_pDnsReq->close();
00637   delete m_pDnsReq;
00638   m_pDnsReq = NULL;
00639   connect();
00640 }
00641 
00642 void HTTPClient::onResult(HTTPResult r) //Called when exchange completed or on failure
00643 {
00644   if(m_pCbItem && m_pCbMeth)
00645     (m_pCbItem->*m_pCbMeth)(r);
00646   else if(m_pCb)
00647     m_pCb(r);
00648   m_blockingResult = r; //Blocking mode
00649   close(); //FIXME:Remove suppl. close() calls
00650 }
00651 
00652 void HTTPClient::onTimeout() //Connection has timed out
00653 {
00654   DBG("Timed out.\n");
00655   onResult(HTTP_TIMEOUT);
00656   close();
00657 }
00658 
00659 //Headers
00660 
00661 //TODO: Factorize w/ HTTPRequestHandler in a single HTTPHeader class
00662 
00663 HTTPResult HTTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available
00664 {
00665   //Disable callbacks
00666   m_pCb = NULL;
00667   m_pCbItem = NULL;
00668   m_pCbMeth = NULL;
00669   m_blockingResult = HTTP_PROCESSING;
00670   do
00671   {
00672     Net::poll();
00673   } while(m_blockingResult == HTTP_PROCESSING);
00674   Net::poll(); //Necessary for cleanup
00675   return m_blockingResult;
00676 }
00677 
00678 bool HTTPClient::readHeaders()
00679 {
00680   static char* line = m_buf;
00681   static char key[128];
00682   static char value[128];
00683   if(!m_dataLen) //No incomplete header in buffer, this is the first time we read data
00684   {
00685     if( readLine(line, 128) > 0 )
00686     {
00687       //Check RC
00688       m_httpResponseCode = 0;
00689       if( sscanf(line, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
00690       {
00691         //Cannot match string, error
00692         DBG("Not a correct HTTP answer : %s\n", line);
00693         onResult(HTTP_PRTCL);
00694         close();
00695         return false;
00696       }
00697       
00698       if(m_httpResponseCode != 200)
00699       {
00700         DBG("Response: error code %d\n", m_httpResponseCode);
00701         HTTPResult res = HTTP_ERROR;
00702         switch(m_httpResponseCode)
00703         {
00704         case 404:
00705           res = HTTP_NOTFOUND;
00706           break;
00707         case 403:
00708           res = HTTP_REFUSED;
00709           break;
00710         default:
00711           res = HTTP_ERROR;
00712         }
00713         onResult(res);
00714         close();
00715         return false;
00716       }
00717       DBG("Response OK\n");
00718     }
00719     else
00720     {
00721       //Empty packet, weird!
00722       DBG("Empty packet!\n");
00723       onResult(HTTP_PRTCL);
00724       close();
00725       return false;
00726     }
00727   }
00728   bool incomplete = false;
00729   while( true )
00730   {
00731     int readLen = readLine(line + m_dataLen, 128 - m_dataLen, &incomplete);
00732     m_dataLen = 0;
00733     if( readLen <= 2 ) //if == 1 or 2, it is an empty line = end of headers
00734     {
00735       DBG("All headers read.\n");
00736       m_state = HTTP_READ_DATA;
00737       break;
00738     }
00739     else if( incomplete == true )
00740     {
00741       m_dataLen = readLen;//Sets data length available in buffer
00742       return false;
00743     }
00744     //DBG("Header : %s\n", line);
00745     int n = sscanf(line, "%[^:] : %[^\r\n]", key, value);
00746     if ( n == 2 )
00747     {
00748       DBG("Read header : %s: %s\n", key, value);
00749       m_respHeaders[key] = value;
00750     }
00751     //TODO: Impl n==1 case (part 2 of previous header)
00752   }
00753 
00754   return true;
00755 }
00756 
00757 bool HTTPClient::writeHeaders() //Called at the first writeData call
00758 {
00759   static char* line = m_buf;
00760   const char* HTTP_METH_STR[] = {"GET", "POST", "HEAD"};
00761   
00762   //Req
00763   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
00764   m_pTCPSocket->send(line, strlen(line));
00765   DBG("Request: %s\n", line);
00766   
00767   DBG("Writing headers:\n");
00768   map<string,string>::iterator it;
00769   for( it = m_reqHeaders.begin(); it != m_reqHeaders.end(); it++ )
00770   {
00771     sprintf(line, "%s: %s\r\n", (*it).first.c_str(), (*it).second.c_str() );
00772     DBG("\r\n%s", line);
00773     m_pTCPSocket->send(line, strlen(line));
00774   }
00775   m_pTCPSocket->send("\r\n",2); //End of head
00776   m_state = HTTP_WRITE_DATA;
00777   return true;
00778 }
00779 
00780 int HTTPClient::readLine(char* str, int maxLen, bool* pIncomplete /* = NULL*/)
00781 {
00782   int ret;
00783   int len = 0;
00784   if(pIncomplete)
00785     *pIncomplete = false;
00786   for(int i = 0; i < maxLen - 1; i++)
00787   {
00788     ret = m_pTCPSocket->recv(str, 1);
00789     if(ret != 1)
00790     {
00791       if(pIncomplete)
00792         *pIncomplete = true;
00793       break;
00794     }
00795     if( (len > 1) && *(str-1)=='\r' && *str=='\n' )
00796     {
00797       break;
00798     }
00799     else if( *str=='\n' )
00800     {
00801       break;    
00802     }
00803     str++;
00804     len++;
00805   }
00806   *str = 0;
00807   return len;
00808 }
00809 
00810 void HTTPClient::setProxy(const char* proxy_url) // host:port
00811 {
00812   if ( proxy_url == NULL )
00813   {
00814     m_proxy = false;
00815     return;
00816   }
00817   Url url;
00818   url.fromString(proxy_url);
00819   m_server.setName(url.getHost().c_str());
00820   
00821   if( url.getPort() > 0 )
00822     m_server.setPort( url.getPort() );
00823   else
00824     m_server.setPort( 8080 );
00825   DBG("Setting proxy server\r\nHost: %s\r\nPort: %d\r\n", url.getHost().c_str(), url.getPort());
00826   IpAddr ip;
00827   if( url.getHostIp(&ip) )
00828   {
00829     m_server.setIp(ip);
00830   }
00831   else
00832   {
00833     DBG("DNS Query...\n");
00834     m_pDnsReq = new DNSRequest();
00835     //m_pDnsReq->setOnReply(this, &HTTPClient::onDNSReply);
00836     m_pDnsReq->resolve(&m_server);
00837     DBG("HTTPClient : DNSRequest %p\n", m_pDnsReq);
00838   }
00839   m_proxy = true;
00840 }