A HTTP Client for the mbed networking libraries, with PUT method

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 256
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 HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00086 {
00087   return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00088 }
00089 
00090 int HTTPClient::getHTTPResponseCode()
00091 {
00092   return m_httpResponseCode;
00093 }
00094 
00095 #define CHECK_CONN_ERR(ret) \
00096   do{ \
00097     if(ret) { \
00098       m_sock.close(); \
00099       ERR("Connection error (%d)", ret); \
00100       return HTTP_CONN; \
00101     } \
00102   } while(0)
00103 
00104 #define PRTCL_ERR() \
00105   do{ \
00106     m_sock.close(); \
00107     ERR("Protocol error"); \
00108     return HTTP_PRTCL; \
00109   } while(0)
00110 
00111 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
00112 { 
00113   m_httpResponseCode = 0; //Invalidate code
00114   m_timeout = timeout;
00115 
00116   char scheme[8];
00117   uint16_t port;
00118   char host[32];
00119   char path[64];
00120   //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
00121   HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
00122   if(res != HTTP_OK)
00123   {
00124     ERR("parseURL returned %d", res);
00125     return res;
00126   }
00127 
00128   if(port == 0) //TODO do handle HTTPS->443
00129   {
00130     port = 80;
00131   }
00132 
00133   DBG("Scheme: %s", scheme);
00134   DBG("Host: %s", host);
00135   DBG("Port: %d", port);
00136   DBG("Path: %s", path);
00137 
00138   //Connect
00139   DBG("Connecting socket to server");
00140   int ret = m_sock.connect(host, port);
00141   if (ret < 0)
00142   {
00143     m_sock.close();
00144     ERR("Could not connect");
00145     return HTTP_CONN;
00146   }
00147 
00148   //Send request
00149   DBG("Sending request");
00150   char buf[CHUNK_SIZE];
00151   const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":"";
00152   snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
00153   ret = send(buf);
00154   if(ret)
00155   {
00156     m_sock.close();
00157     ERR("Could not write request");
00158     return HTTP_CONN;
00159   }
00160 
00161   //Send all headers
00162 
00163   //Send default headers
00164   DBG("Sending headers");
00165   if( (method == HTTP_POST || method == HTTP_PUT) && (pDataOut != NULL) )
00166   {
00167     if( pDataOut->getIsChunked() )
00168     {
00169       ret = send("Transfer-Encoding: chunked\r\n");
00170       CHECK_CONN_ERR(ret);
00171     }
00172     else
00173     {
00174       snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
00175       ret = send(buf);
00176       CHECK_CONN_ERR(ret);
00177     }
00178     char type[48];
00179     if( pDataOut->getDataType(type, 48) == HTTP_OK )
00180     {
00181       snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
00182       ret = send(buf);
00183       CHECK_CONN_ERR(ret);
00184     }
00185   }
00186   
00187   //Close headers
00188   DBG("Headers sent");
00189   ret = send("\r\n");
00190   CHECK_CONN_ERR(ret);
00191 
00192   size_t trfLen;
00193   
00194   //Send data (if POST)
00195   if( (method == HTTP_POST || method == HTTP_PUT) && (pDataOut != NULL) )
00196   {
00197     DBG("Sending data");
00198     while(true)
00199     {
00200       size_t writtenLen = 0;
00201       pDataOut->read(buf, CHUNK_SIZE, &trfLen);
00202       if( pDataOut->getIsChunked() )
00203       {
00204         //Write chunk header
00205         char chunkHeader[16];
00206         snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
00207         ret = send(chunkHeader);
00208         CHECK_CONN_ERR(ret);
00209       }
00210       else if( trfLen == 0 )
00211       {
00212         break;
00213       }
00214       if( trfLen != 0 )
00215       {
00216         ret = send(buf, trfLen);
00217         CHECK_CONN_ERR(ret);
00218       }
00219 
00220       if( pDataOut->getIsChunked()  )
00221       {
00222         ret = send("\r\n"); //Chunk-terminating CRLF
00223         CHECK_CONN_ERR(ret);
00224       }
00225       else
00226       {
00227         writtenLen += trfLen;
00228         if( writtenLen >= pDataOut->getDataLen() )
00229         {
00230           break;
00231         }
00232       }
00233 
00234       if( trfLen == 0 )
00235       {
00236         break;
00237       }
00238     }
00239 
00240   }
00241   
00242   //Receive response
00243   DBG("Receiving response");
00244   ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
00245   CHECK_CONN_ERR(ret);
00246 
00247   buf[trfLen] = '\0';
00248 
00249   char* crlfPtr = strstr(buf, "\r\n");
00250   if(crlfPtr == NULL)
00251   {
00252     PRTCL_ERR();
00253   }
00254 
00255   int crlfPos = crlfPtr - buf;
00256   buf[crlfPos] = '\0';
00257 
00258   //Parse HTTP response
00259   if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
00260   {
00261     //Cannot match string, error
00262     ERR("Not a correct HTTP answer : %s\n", buf);
00263     PRTCL_ERR();
00264   }
00265 
00266   if(m_httpResponseCode != 200)
00267   {
00268     //Cannot match string, error
00269     WARN("Response code %d", m_httpResponseCode);
00270     PRTCL_ERR();
00271   }
00272 
00273   DBG("Reading headers");
00274 
00275   memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00276   trfLen -= (crlfPos + 2);
00277 
00278   size_t recvContentLength = 0;
00279   bool recvChunked = false;
00280   //Now get headers
00281   while( true )
00282   {
00283     crlfPtr = strstr(buf, "\r\n");
00284     if(crlfPtr == NULL)
00285     {
00286       if( trfLen < CHUNK_SIZE - 1 )
00287       {
00288         size_t newTrfLen;
00289         ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00290         trfLen += newTrfLen;
00291         buf[trfLen] = '\0';
00292         DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
00293         CHECK_CONN_ERR(ret);
00294         continue;
00295       }
00296       else
00297       {
00298         PRTCL_ERR();
00299       }
00300     }
00301 
00302     crlfPos = crlfPtr - buf;
00303 
00304     if(crlfPos == 0) //End of headers
00305     {
00306       DBG("Headers read");
00307       memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
00308       trfLen -= 2;
00309       break;
00310     }
00311 
00312     buf[crlfPos] = '\0';
00313 
00314     char key[32];
00315     char value[32];
00316 
00317     key[31] = '\0';
00318     value[31] = '\0';
00319 
00320     int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
00321     if ( n == 2 )
00322     {
00323       DBG("Read header : %s: %s\n", key, value);
00324       if( !strcmp(key, "Content-Length") )
00325       {
00326         sscanf(value, "%d", &recvContentLength);
00327         pDataIn->setDataLen(recvContentLength);
00328       }
00329       else if( !strcmp(key, "Transfer-Encoding") )
00330       {
00331         if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
00332         {
00333           recvChunked = true;
00334           pDataIn->setIsChunked(true);
00335         }
00336       }
00337       else if( !strcmp(key, "Content-Type") )
00338       {
00339         pDataIn->setDataType(value);
00340       }
00341 
00342       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00343       trfLen -= (crlfPos + 2);
00344 
00345     }
00346     else
00347     {
00348       ERR("Could not parse header");
00349       PRTCL_ERR();
00350     }
00351 
00352   }
00353 
00354   //Receive data
00355   DBG("Receiving data");
00356   while(true)
00357   {
00358     size_t readLen = 0;
00359 
00360     if( recvChunked )
00361     {
00362       //Read chunk header
00363       crlfPos=0;
00364       for(crlfPos++; crlfPos < trfLen - 2; crlfPos++)
00365       {
00366         if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
00367         {
00368           break;
00369         }
00370       }
00371       if(crlfPos >= trfLen - 2) //Try to read more
00372       {
00373         if( trfLen < CHUNK_SIZE )
00374         {
00375           size_t newTrfLen;
00376           ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00377           trfLen += newTrfLen;
00378           CHECK_CONN_ERR(ret);
00379           continue;
00380         }
00381         else
00382         {
00383           PRTCL_ERR();
00384         }
00385       }
00386       buf[crlfPos] = '\0';
00387       int n = sscanf(buf, "%x", &readLen);
00388       if(n!=1)
00389       {
00390         ERR("Could not read chunk length");
00391         PRTCL_ERR();
00392       }
00393 
00394       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
00395       trfLen -= (crlfPos + 2);
00396 
00397       if( readLen == 0 )
00398       {
00399         //Last chunk
00400         break;
00401       }
00402     }
00403     else
00404     {
00405       readLen = recvContentLength;
00406     }
00407 
00408     DBG("Retrieving %d bytes", readLen);
00409 
00410     do
00411     {
00412       pDataIn->write(buf, MIN(trfLen, readLen));
00413       if( trfLen > readLen )
00414       {
00415         memmove(buf, &buf[readLen], trfLen - readLen);
00416         trfLen -= readLen;
00417         readLen = 0;
00418       }
00419       else
00420       {
00421         readLen -= trfLen;
00422       }
00423 
00424       if(readLen)
00425       {
00426         ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
00427         CHECK_CONN_ERR(ret);
00428       }
00429     } while(readLen);
00430 
00431     if( recvChunked )
00432     {
00433       if(trfLen < 2)
00434       {
00435         size_t newTrfLen;
00436         //Read missing chars to find end of chunk
00437         ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen);
00438         CHECK_CONN_ERR(ret);
00439         trfLen += newTrfLen;
00440       }
00441       if( (buf[0] != '\r') || (buf[1] != '\n') )
00442       {
00443         ERR("Format error");
00444         PRTCL_ERR();
00445       }
00446       memmove(buf, &buf[2], trfLen - 2);
00447       trfLen -= 2;
00448     }
00449     else
00450     {
00451       break;
00452     }
00453 
00454   }
00455 
00456   m_sock.close();
00457   DBG("Completed HTTP transaction");
00458 
00459   return HTTP_OK;
00460 }
00461 
00462 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
00463 {
00464   DBG("Trying to read between %d and %d bytes", minLen, maxLen);
00465   size_t readLen = 0;
00466       
00467   if(!m_sock.is_connected())
00468   {
00469     WARN("Connection was closed by server");
00470     return HTTP_CLOSED; //Connection was closed by server 
00471   }
00472     
00473   int ret;
00474   while(readLen < maxLen)
00475   {
00476     if(readLen < minLen)
00477     {
00478       DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
00479       m_sock.set_blocking(false, m_timeout);
00480       ret = m_sock.receive_all(buf + readLen, minLen - readLen);
00481     }
00482     else
00483     {
00484       DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
00485       m_sock.set_blocking(false, 0);
00486       ret = m_sock.receive(buf + readLen, maxLen - readLen);
00487     }
00488     
00489     if( ret > 0)
00490     {
00491       readLen += ret;
00492     }
00493     else if( ret == 0 )
00494     {
00495       break;
00496     }
00497     else
00498     {
00499       if(!m_sock.is_connected())
00500       {
00501         ERR("Connection error (recv returned %d)", ret);
00502         *pReadLen = readLen;
00503         return HTTP_CONN;
00504       }
00505       else
00506       {
00507         break;      
00508       }
00509     }
00510     
00511     if(!m_sock.is_connected())
00512     {
00513       break;
00514     }
00515   }
00516   DBG("Read %d bytes", readLen);
00517   *pReadLen = readLen;
00518   return HTTP_OK;
00519 }
00520 
00521 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
00522 {
00523   if(len == 0)
00524   {
00525     len = strlen(buf);
00526   }
00527   DBG("Trying to write %d bytes", len);
00528   size_t writtenLen = 0;
00529     
00530   if(!m_sock.is_connected())
00531   {
00532     WARN("Connection was closed by server");
00533     return HTTP_CLOSED; //Connection was closed by server 
00534   }
00535   
00536   m_sock.set_blocking(false, m_timeout);
00537   int ret = m_sock.send_all(buf, len);
00538   if(ret > 0)
00539   {
00540     writtenLen += ret;
00541   }
00542   else if( ret == 0 )
00543   {
00544     WARN("Connection was closed by server");
00545     return HTTP_CLOSED; //Connection was closed by server
00546   }
00547   else
00548   {
00549     ERR("Connection error (send returned %d)", ret);
00550     return HTTP_CONN;
00551   }
00552   
00553   DBG("Written %d bytes", writtenLen);
00554   return HTTP_OK;
00555 }
00556 
00557 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
00558 {
00559   char* schemePtr = (char*) url;
00560   char* hostPtr = (char*) strstr(url, "://");
00561   if(hostPtr == NULL)
00562   {
00563     WARN("Could not find host");
00564     return HTTP_PARSE; //URL is invalid
00565   }
00566 
00567   if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
00568   {
00569     WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
00570     return HTTP_PARSE;
00571   }
00572   memcpy(scheme, schemePtr, hostPtr - schemePtr);
00573   scheme[hostPtr - schemePtr] = '\0';
00574 
00575   hostPtr+=3;
00576 
00577   size_t hostLen = 0;
00578 
00579   char* portPtr = strchr(hostPtr, ':');
00580   if( portPtr != NULL )
00581   {
00582     hostLen = portPtr - hostPtr;
00583     portPtr++;
00584     if( sscanf(portPtr, "%hu", port) != 1)
00585     {
00586       WARN("Could not find port");
00587       return HTTP_PARSE;
00588     }
00589   }
00590   else
00591   {
00592     *port=0;
00593   }
00594   char* pathPtr = strchr(hostPtr, '/');
00595   if( hostLen == 0 )
00596   {
00597     hostLen = pathPtr - hostPtr;
00598   }
00599 
00600   if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
00601   {
00602     WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
00603     return HTTP_PARSE;
00604   }
00605   memcpy(host, hostPtr, hostLen);
00606   host[hostLen] = '\0';
00607 
00608   size_t pathLen;
00609   char* fragmentPtr = strchr(hostPtr, '#');
00610   if(fragmentPtr != NULL)
00611   {
00612     pathLen = fragmentPtr - pathPtr;
00613   }
00614   else
00615   {
00616     pathLen = strlen(pathPtr);
00617   }
00618 
00619   if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
00620   {
00621     WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
00622     return HTTP_PARSE;
00623   }
00624   memcpy(path, pathPtr, pathLen);
00625   path[pathLen] = '\0';
00626 
00627   return HTTP_OK;
00628 }