Multi-Hackers / AxedaWrapper

Dependents:   axeda_wrapper_dev MTS_Axeda_Example

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