joshua Elsdon / Mbed 2 deprecated Email2Screen

Dependencies:   mbed

Fork of Email2Screen by Oliver Mattos

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