thermostat remote version

Dependents:   df-2013-thermostat-handson df-2013-minihack-thermostat-complete df-2013-minihack-thermostat df-2013-thermostat-remotes

Fork of HTTPClient by Donatien Garnier

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPClient.cpp Source File

HTTPClient.cpp

00001 /* HTTPClient.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 //Debug is disabled by default
00021 #if 1
00022 //Enable debug
00023 #include <cstdio>
00024 //#define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__); 
00025 #define DBG(x, ...) 
00026 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__); 
00027 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__); 
00028 
00029 #else
00030 //Disable debug
00031 #define DBG(x, ...) 
00032 #define WARN(x, ...)
00033 #define ERR(x, ...) 
00034 
00035 #endif
00036 
00037 #define HTTP_PORT 80
00038 
00039 #define OK 0
00040 
00041 #define MIN(x,y) (((x)<(y))?(x):(y))
00042 #define MAX(x,y) (((x)>(y))?(x):(y))
00043 
00044 #define CHUNK_SIZE 512
00045 
00046 #include <cstring>
00047 
00048 #include "HTTPClient.h"
00049 
00050 HTTPClient::HTTPClient() :
00051 m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
00052 {
00053 
00054 }
00055 
00056 HTTPClient::~HTTPClient()
00057 {
00058 
00059 }
00060 
00061 #if 0
00062 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
00063 {
00064   m_basicAuthUser = user;
00065   m_basicAuthPassword = password;
00066 }
00067 #endif
00068 
00069 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00070 {
00071   return connect(url, HTTP_GET, NULL, pDataIn, timeout);
00072 }
00073 
00074 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00075 {
00076   HTTPText str(result, maxResultLen);
00077   return get(url, &str, timeout);
00078 }
00079 
00080 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00081 {
00082   return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00083 }
00084 
00085 int HTTPClient::getHTTPResponseCode()
00086 {
00087   return m_httpResponseCode;
00088 }
00089 
00090 #define CHECK_CONN_ERR(ret) \
00091   do{ \
00092     if(ret) { \
00093       m_sock.close(); \
00094       ERR("Connection error (%d)", ret); \
00095       return HTTP_CONN; \
00096     } \
00097   } while(0)
00098 
00099 #define PRTCL_ERR() \
00100   do{ \
00101     m_sock.close(); \
00102     ERR("Protocol error"); \
00103     return HTTP_PRTCL; \
00104   } while(0)
00105 
00106 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
00107 { 
00108   m_httpResponseCode = 0; //Invalidate code
00109   m_timeout = timeout;
00110 
00111   char scheme[8];
00112   uint16_t port;
00113   char host[32];
00114   char path[64];
00115   //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
00116   HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
00117   if(res != HTTP_OK)
00118   {
00119     ERR("parseURL returned %d", res);
00120     return res;
00121   }
00122 
00123   if(port == 0) //TODO do handle HTTPS->443
00124   {
00125     port = 80;
00126   }
00127 
00128   DBG("Scheme: %s", scheme);
00129   DBG("Host: %s", host);
00130   DBG("Port: %d", port);
00131   DBG("Path: %s", path);
00132 
00133   //Connect
00134   DBG("Connecting socket to server");
00135   int ret = m_sock.connect(host, port);
00136   if (ret < 0)
00137   {
00138     m_sock.close();
00139     ERR("Could not connect");
00140     return HTTP_CONN;
00141   }
00142 
00143   //Send request
00144   DBG("Sending request");
00145   char buf[CHUNK_SIZE];
00146   const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":"";
00147   snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
00148   ret = send(buf);
00149   if(ret)
00150   {
00151     m_sock.close();
00152     ERR("Could not write request");
00153     return HTTP_CONN;
00154   }
00155 
00156   //Send all headers
00157 
00158   //Send default headers
00159   DBG("Sending headers");
00160   if( (method == HTTP_POST) && (pDataOut != NULL) )
00161   {
00162     if( pDataOut->getIsChunked() )
00163     {
00164       ret = send("Transfer-Encoding: chunked\r\n");
00165       CHECK_CONN_ERR(ret);
00166     }
00167     else
00168     {
00169       snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
00170       ret = send(buf);
00171       CHECK_CONN_ERR(ret);
00172     }
00173     char type[48];
00174     if( pDataOut->getDataType(type, 48) == HTTP_OK )
00175     {
00176       snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
00177       ret = send(buf);
00178       CHECK_CONN_ERR(ret);
00179     }
00180   }
00181   
00182   //Close headers
00183   DBG("Headers sent");
00184   ret = send("\r\n");
00185   CHECK_CONN_ERR(ret);
00186 
00187   size_t trfLen;
00188   
00189   //Send data (if POST)
00190   if( (method == HTTP_POST) && (pDataOut != NULL) )
00191   {
00192     DBG("Sending data");
00193     while(true)
00194     {
00195       size_t writtenLen = 0;
00196       pDataOut->read(buf, CHUNK_SIZE, &trfLen);
00197       if( pDataOut->getIsChunked() )
00198       {
00199         //Write chunk header
00200         char chunkHeader[16];
00201         snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
00202         ret = send(chunkHeader);
00203         CHECK_CONN_ERR(ret);
00204       }
00205       else if( trfLen == 0 )
00206       {
00207         break;
00208       }
00209       if( trfLen != 0 )
00210       {
00211         ret = send(buf, trfLen);
00212         CHECK_CONN_ERR(ret);
00213       }
00214 
00215       if( pDataOut->getIsChunked()  )
00216       {
00217         ret = send("\r\n"); //Chunk-terminating CRLF
00218         CHECK_CONN_ERR(ret);
00219       }
00220       else
00221       {
00222         writtenLen += trfLen;
00223         if( writtenLen >= pDataOut->getDataLen() )
00224         {
00225           break;
00226         }
00227       }
00228 
00229       if( trfLen == 0 )
00230       {
00231         break;
00232       }
00233     }
00234 
00235   }
00236   
00237   //Receive response
00238   DBG("Receiving response");
00239   ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
00240   CHECK_CONN_ERR(ret);
00241 
00242   buf[trfLen] = '\0';
00243 
00244   char* crlfPtr = strstr(buf, "\r\n");
00245   if(crlfPtr == NULL)
00246   {
00247     PRTCL_ERR();
00248   }
00249 
00250   int crlfPos = crlfPtr - buf;
00251   buf[crlfPos] = '\0';
00252 
00253   //Parse HTTP response
00254   if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
00255   {
00256     //Cannot match string, error
00257     ERR("Not a correct HTTP answer : %s\n", buf);
00258     PRTCL_ERR();
00259   }
00260 
00261   if(m_httpResponseCode != 200)
00262   {
00263     //Cannot match string, error
00264     WARN("Response code %d", m_httpResponseCode);
00265     PRTCL_ERR();
00266   }
00267 
00268   DBG("Reading headers");
00269 
00270   memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00271   trfLen -= (crlfPos + 2);
00272 
00273   size_t recvContentLength = 0;
00274   bool recvChunked = false;
00275   //Now get headers
00276   while( true )
00277   {
00278     crlfPtr = strstr(buf, "\r\n");
00279     if(crlfPtr == NULL)
00280     {
00281       if( trfLen < CHUNK_SIZE - 1 )
00282       {
00283         size_t newTrfLen;
00284         ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00285         trfLen += newTrfLen;
00286         buf[trfLen] = '\0';
00287         DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
00288         CHECK_CONN_ERR(ret);
00289         continue;
00290       }
00291       else
00292       {
00293         PRTCL_ERR();
00294       }
00295     }
00296 
00297     crlfPos = crlfPtr - buf;
00298 
00299     if(crlfPos == 0) //End of headers
00300     {
00301       DBG("Headers read");
00302       memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
00303       trfLen -= 2;
00304       break;
00305     }
00306 
00307     buf[crlfPos] = '\0';
00308 
00309     char key[32];
00310     char value[32];
00311 
00312     key[31] = '\0';
00313     value[31] = '\0';
00314 
00315     int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
00316     if ( n == 2 )
00317     {
00318       DBG("Read header : %s: %s\n", key, value);
00319       if( !strcmp(key, "Content-Length") )
00320       {
00321         sscanf(value, "%d", &recvContentLength);
00322         pDataIn->setDataLen(recvContentLength);
00323       }
00324       else if( !strcmp(key, "Transfer-Encoding") )
00325       {
00326         if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
00327         {
00328           recvChunked = true;
00329           pDataIn->setIsChunked(true);
00330         }
00331       }
00332       else if( !strcmp(key, "Content-Type") )
00333       {
00334         pDataIn->setDataType(value);
00335       }
00336 
00337       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00338       trfLen -= (crlfPos + 2);
00339 
00340     }
00341     else
00342     {
00343       ERR("Could not parse header");
00344       PRTCL_ERR();
00345     }
00346 
00347   }
00348 
00349   //Receive data
00350   DBG("Receiving data");
00351   while(true)
00352   {
00353     size_t readLen = 0;
00354 
00355     if( recvChunked )
00356     {
00357       //Read chunk header
00358       crlfPos=0;
00359       for(crlfPos++; crlfPos < trfLen - 2; crlfPos++)
00360       {
00361         if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
00362         {
00363           break;
00364         }
00365       }
00366       if(crlfPos >= trfLen - 2) //Try to read more
00367       {
00368         if( trfLen < CHUNK_SIZE )
00369         {
00370           size_t newTrfLen;
00371           ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00372           trfLen += newTrfLen;
00373           CHECK_CONN_ERR(ret);
00374           continue;
00375         }
00376         else
00377         {
00378           PRTCL_ERR();
00379         }
00380       }
00381       buf[crlfPos] = '\0';
00382       int n = sscanf(buf, "%x", &readLen);
00383       if(n!=1)
00384       {
00385         ERR("Could not read chunk length");
00386         PRTCL_ERR();
00387       }
00388 
00389       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
00390       trfLen -= (crlfPos + 2);
00391 
00392       if( readLen == 0 )
00393       {
00394         //Last chunk
00395         break;
00396       }
00397     }
00398     else
00399     {
00400       readLen = recvContentLength;
00401     }
00402 
00403     DBG("Retrieving %d bytes", readLen);
00404 
00405     do
00406     {
00407       pDataIn->write(buf, MIN(trfLen, readLen));
00408       if( trfLen > readLen )
00409       {
00410         memmove(buf, &buf[readLen], trfLen - readLen);
00411         trfLen -= readLen;
00412         readLen = 0;
00413       }
00414       else
00415       {
00416         readLen -= trfLen;
00417       }
00418 
00419       if(readLen)
00420       {
00421         ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
00422         CHECK_CONN_ERR(ret);
00423       }
00424     } while(readLen);
00425 
00426     if( recvChunked )
00427     {
00428       if(trfLen < 2)
00429       {
00430         size_t newTrfLen;
00431         //Read missing chars to find end of chunk
00432         ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen);
00433         CHECK_CONN_ERR(ret);
00434         trfLen += newTrfLen;
00435       }
00436       if( (buf[0] != '\r') || (buf[1] != '\n') )
00437       {
00438         ERR("Format error");
00439         PRTCL_ERR();
00440       }
00441       memmove(buf, &buf[2], trfLen - 2);
00442       trfLen -= 2;
00443     }
00444     else
00445     {
00446       break;
00447     }
00448 
00449   }
00450 
00451   m_sock.close();
00452   DBG("Completed HTTP transaction");
00453 
00454   return HTTP_OK;
00455 }
00456 
00457 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
00458 {
00459   DBG("Trying to read between %d and %d bytes", minLen, maxLen);
00460   size_t readLen = 0;
00461       
00462   if(!m_sock.is_connected())
00463   {
00464     WARN("Connection was closed by server");
00465     return HTTP_CLOSED; //Connection was closed by server 
00466   }
00467     
00468   int ret;
00469   while(readLen < maxLen)
00470   {
00471     if(readLen < minLen)
00472     {
00473       DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
00474       m_sock.set_blocking(false, m_timeout);
00475       ret = m_sock.receive_all(buf + readLen, minLen - readLen);
00476     }
00477     else
00478     {
00479       DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
00480       m_sock.set_blocking(false, 0);
00481       ret = m_sock.receive(buf + readLen, maxLen - readLen);
00482     }
00483     
00484     if( ret > 0)
00485     {
00486       readLen += ret;
00487     }
00488     else if( ret == 0 )
00489     {
00490       break;
00491     }
00492     else
00493     {
00494       if(!m_sock.is_connected())
00495       {
00496         ERR("Connection error (recv returned %d)", ret);
00497         *pReadLen = readLen;
00498         return HTTP_CONN;
00499       }
00500       else
00501       {
00502         break;      
00503       }
00504     }
00505     
00506     if(!m_sock.is_connected())
00507     {
00508       break;
00509     }
00510   }
00511   DBG("Read %d bytes", readLen);
00512   *pReadLen = readLen;
00513   return HTTP_OK;
00514 }
00515 
00516 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
00517 {
00518   if(len == 0)
00519   {
00520     len = strlen(buf);
00521   }
00522   DBG("Trying to write %d bytes", len);
00523   size_t writtenLen = 0;
00524     
00525   if(!m_sock.is_connected())
00526   {
00527     WARN("Connection was closed by server");
00528     return HTTP_CLOSED; //Connection was closed by server 
00529   }
00530   
00531   m_sock.set_blocking(false, m_timeout);
00532   int ret = m_sock.send_all(buf, len);
00533   if(ret > 0)
00534   {
00535     writtenLen += ret;
00536   }
00537   else if( ret == 0 )
00538   {
00539     WARN("Connection was closed by server");
00540     return HTTP_CLOSED; //Connection was closed by server
00541   }
00542   else
00543   {
00544     ERR("Connection error (send returned %d)", ret);
00545     return HTTP_CONN;
00546   }
00547   
00548   DBG("Written %d bytes", writtenLen);
00549   return HTTP_OK;
00550 }
00551 
00552 HTTPResult HTTPClient::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL
00553 {
00554   char* schemePtr = (char*) url;
00555   char* hostPtr = (char*) strstr(url, "://");
00556   if(hostPtr == NULL)
00557   {
00558     WARN("Could not find host");
00559     return HTTP_PARSE; //URL is invalid
00560   }
00561 
00562   if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
00563   {
00564     WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
00565     return HTTP_PARSE;
00566   }
00567   memcpy(scheme, schemePtr, hostPtr - schemePtr);
00568   scheme[hostPtr - schemePtr] = '\0';
00569 
00570   hostPtr+=3;
00571 
00572   size_t hostLen = 0;
00573 
00574   char* portPtr = strchr(hostPtr, ':');
00575   if( portPtr != NULL )
00576   {
00577     hostLen = portPtr - hostPtr;
00578     portPtr++;
00579     if( sscanf(portPtr, "%hu", port) != 1)
00580     {
00581       WARN("Could not find port");
00582       return HTTP_PARSE;
00583     }
00584   }
00585   else
00586   {
00587     *port=0;
00588   }
00589   char* pathPtr = strchr(hostPtr, '/');
00590   if( hostLen == 0 )
00591   {
00592     hostLen = pathPtr - hostPtr;
00593   }
00594 
00595   if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
00596   {
00597     WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
00598     return HTTP_PARSE;
00599   }
00600   memcpy(host, hostPtr, hostLen);
00601   host[hostLen] = '\0';
00602 
00603   size_t pathLen;
00604   char* fragmentPtr = strchr(hostPtr, '#');
00605   if(fragmentPtr != NULL)
00606   {
00607     pathLen = fragmentPtr - pathPtr;
00608   }
00609   else
00610   {
00611     pathLen = strlen(pathPtr);
00612   }
00613 
00614   if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
00615   {
00616     WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
00617     return HTTP_PARSE;
00618   }
00619   memcpy(path, pathPtr, pathLen);
00620   path[pathLen] = '\0';
00621 
00622   return HTTP_OK;
00623 }