Freeman Oldman / NUCLEO_STM32F401RE_CC3000_ILI9341
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HttpClient.cpp Source File

HttpClient.cpp

00001 #include "HttpClient.h"
00002 #include "Utility.h"
00003 #include <string>
00004 #include <cstring>
00005 #include <vector>
00006 
00007 extern Serial pc;
00008 
00009 #if 1
00010 //Enable debug
00011 #include <cstdio>
00012 #define DBG(x, ...) pc.printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__); 
00013 #define WARN(x, ...) pc.printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__); 
00014 #define ERR(x, ...) pc.printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__); 
00015 #else
00016 //Disable debug
00017 #define DBG(x, ...) 
00018 #define WARN(x, ...)
00019 #define ERR(x, ...) 
00020 #endif
00021 
00022 
00023 
00024 
00025 HttpClient::HttpClient(const char * hostname)
00026     : _hostname(hostname)
00027 {
00028 }
00029 
00030 int HttpClient::get(const char * url, char * buffer, size_t buf_len, HeaderInfo & hinfo, int timeout)
00031 {
00032     char scheme[8];
00033   uint16_t port;
00034   char host[32];
00035   char path[64];
00036 
00037     _timeout = timeout;
00038     
00039   if(parse_url(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path)))
00040   {
00041     ERR("Error parsing URL");
00042     return -1;
00043   }
00044 
00045   if(port == 0) //TODO do handle HTTPS->443
00046   {
00047     port = 80;
00048   }
00049 
00050   DBG("Scheme: %s", scheme);
00051   DBG("Host: %s", host);
00052   DBG("Port: %d", port);
00053   DBG("Path: %s", path);
00054     
00055     // Connect to server
00056   int ret = m_sock.connect(host, port);
00057   if (ret < 0)
00058   {
00059     m_sock.close();
00060     ERR("Could not connect");
00061     return -1;
00062   }
00063 
00064   //Send request
00065     std::string req_header = build_http_headers("GET", (const char *) path);
00066   ret = send((char *) req_header.c_str(), req_header.size());
00067   if(ret)
00068   {
00069     m_sock.close();
00070     ERR("Could not write request\r\n");
00071     return -1;
00072   }
00073     // Process the response
00074     // First get the header
00075     DBG("Reading HTTP header ...\r\n");
00076     char header_buf[512];
00077     int header_line = 0, numread = 0;
00078     
00079     do {
00080         memset(header_buf, 0, sizeof(header_buf));
00081         numread = recv_until(header_buf, sizeof(header_buf), '\n');
00082         // remove trailing \r\n
00083         std::string line(header_buf, numread);
00084         line = trim(line, " \t\r\n");
00085         
00086         DBG("Header line [%s]\r\n", line.c_str());
00087         parse_headers(line, hinfo, header_line);
00088         header_line++;
00089     }while(numread > 2); // empty line for \r\n
00090     
00091     // Show the header information
00092     DBG("Http response: %d\r\n", hinfo.http_response);
00093     DBG("Http response text: %s\r\n", hinfo.http_response_text.c_str());
00094     for(std::map<std::string, std::string>::iterator it = hinfo.http_info.begin();
00095         it != hinfo.http_info.end();
00096         it++)
00097     {
00098         DBG("Key [%s] : Value [%s]\r\n", it->first.c_str(), it->second.c_str());
00099     }
00100     
00101     // Read the rest of the data based on content type
00102     return read_data(hinfo, buffer, buf_len);
00103     
00104 }
00105 
00106 int HttpClient::read_data(HeaderInfo & hinfo, char * buffer, size_t buf_len)
00107 {
00108     int chunk_size = 0;
00109     char tmp_buf[10]; // just for reading the \r\n values
00110     char * buf_ptr = buffer;
00111     int numread = 0;
00112     
00113     m_sock.set_blocking(false, _timeout);
00114     if (hinfo.http_info["Transfer-Encoding"] == "chunked")
00115     {
00116         do {
00117             // read the chunk size
00118             DBG("Reading chunk size ...\r\n");
00119             if ( m_sock.receive_until(tmp_buf, 10, '\n') > 0)
00120             {
00121                 chunk_size = strtoul(tmp_buf, NULL, 16);
00122                 DBG("Chunk size: %d\r\n", chunk_size);
00123             }else
00124                 break;
00125 
00126             if (chunk_size > 0)
00127             {
00128                 // read one char at a time untill chunk size
00129                 int cur_read2 = 0;
00130                 do {
00131                         if (m_sock.receive(buf_ptr,1) < 0)
00132                         {
00133                             DBG("Error reading chunks with total read [%d]...\r\n", numread);
00134                             return -1;
00135                         }
00136                         cur_read2 ++;
00137                         buf_ptr ++;
00138                 }while(cur_read2 < chunk_size);
00139                 DBG("Read %d chunk\r\n", cur_read2);
00140             }
00141     
00142             numread += chunk_size;
00143             DBG("Consuming new lines ...\r\n");
00144             // Consume the next new line
00145             m_sock.receive_until(tmp_buf, 10, '\n');
00146             
00147         }while((chunk_size > 0) && ((buf_ptr - buffer) < buf_len)); 
00148         
00149     }else
00150     {
00151             DBG("Reading complete data in one go ...\r\n");
00152             numread = m_sock.receive_all(buffer, buf_len);
00153     }
00154     
00155     DBG("Read %d bytes", numread);
00156     
00157     // Finally close the socket
00158     m_sock.close();
00159         
00160     return numread;
00161 }
00162 
00163 int HttpClient::parse_headers(std::string header_line, HeaderInfo & hinfo, int lineno)
00164 {
00165     std::vector<std::string> tokens;
00166     // remove the trailing \n
00167     
00168     if (lineno == 0)
00169     {
00170         // Tokenize the first line using spaces
00171         split(tokens, header_line, " ");
00172         if (tokens.size() == 3)
00173         {
00174             //for(int i=0; i < tokens.size() ; i++)
00175             //  pc.printf("Token[%d] = [%s]\r\n", i, tokens[i].c_str());
00176             
00177             // The second part is the http response
00178             hinfo.http_response = atol(tokens[1].c_str());
00179             DBG("HTTP response [%d]\r\n", hinfo.http_response);
00180             hinfo.http_response_text = trim(tokens[2]);
00181             DBG("HTTP text [%s]\r\n", hinfo.http_response_text.c_str());
00182         }
00183     }else
00184     {
00185         
00186         // Scan for key value pairs
00187         split(tokens, header_line, ":");
00188         if (tokens.size() == 2)
00189         {
00190             //for(int i=0; i < tokens.size() ; i++)
00191             //  pc.printf("Token[%d] = [%s]\r\n", i, tokens[i].c_str());
00192             
00193             std::string key = trim(tokens[0]);
00194             std::string value = trim(tokens[1]);
00195             
00196             hinfo.http_info[key] = value;
00197         }
00198     }
00199     
00200     return 0;
00201 }
00202 
00203 std::string HttpClient::build_http_headers(std::string type, std::string path)
00204 {
00205     std::string request_hdr = type + " " + path + " " + "HTTP/1.1" + "\r\n";
00206     request_hdr += "Host:" + _hostname + "\r\n";
00207     request_hdr += "\r\n"; // empty line terminates the header
00208     
00209     return request_hdr;
00210     
00211 }
00212 
00213 int HttpClient::parse_url(const char* url, char* scheme, 
00214     size_t scheme_len, 
00215     char* host, 
00216     size_t host_len, 
00217     uint16_t* port, 
00218     char* path, 
00219     size_t path_len) //Parse URL
00220 {
00221   char* schemePtr = (char*) url;
00222   char* hostPtr = (char*) strstr(url, "://");
00223   if(hostPtr == NULL)
00224     {
00225         ERR("Invalid URL [%s]\r\n", url);
00226     return -1; //URL is invalid
00227     }
00228 
00229   if( scheme_len < hostPtr - schemePtr + 1 ) //including NULL-terminating char
00230   {
00231     ERR("Scheme str is too small (%d >= %d)\r\n", scheme_len, hostPtr - schemePtr + 1);
00232     return -1;
00233   }
00234   memcpy(scheme, schemePtr, hostPtr - schemePtr);
00235   scheme[hostPtr - schemePtr] = '\0';
00236 
00237   hostPtr+=3;
00238 
00239   size_t hostLen = 0;
00240 
00241   char* portPtr = strchr(hostPtr, ':');
00242   if( portPtr != NULL )
00243   {
00244     hostLen = portPtr - hostPtr;
00245     portPtr++;
00246     if( sscanf(portPtr, "%hu", port) != 1)
00247     {
00248       ERR("Could not find port\r\n");
00249       return -1;
00250     }
00251   }
00252   else
00253   {
00254     *port=0;
00255   }
00256   char* pathPtr = strchr(hostPtr, '/');
00257   if( hostLen == 0 )
00258   {
00259     hostLen = pathPtr - hostPtr;
00260   }
00261 
00262   if( host_len < hostLen + 1 ) //including NULL-terminating char
00263   {
00264     ERR("Host str is too small (%d >= %d)\r\n", host_len, hostLen + 1);
00265     return -1;
00266   }
00267   memcpy(host, hostPtr, hostLen);
00268   host[hostLen] = '\0';
00269 
00270   size_t pathLen;
00271   char* fragmentPtr = strchr(hostPtr, '#');
00272   if(fragmentPtr != NULL)
00273   {
00274     pathLen = fragmentPtr - pathPtr;
00275   }
00276   else
00277   {
00278     pathLen = strlen(pathPtr);
00279   }
00280 
00281   if( path_len < pathLen + 1 ) //including NULL-terminating char
00282   {
00283     ERR("Path str is too small (%d >= %d)\r\n", path_len, pathLen + 1);
00284     return -1;
00285   }
00286   memcpy(path, pathPtr, pathLen);
00287   path[pathLen] = '\0';
00288 
00289   return 0;
00290 }
00291 
00292 int HttpClient::recv_until(char * buf, size_t len, char term)
00293 {
00294     int numread = 0;
00295   if(!m_sock.is_connected())
00296   {
00297     ERR("Connection was closed by server\r\n");
00298     return -1; //Connection was closed by server 
00299   }
00300   
00301   m_sock.set_blocking(false, _timeout);
00302   int ret = m_sock.receive_until(buf, len, term);
00303   if(ret > 0)
00304   {
00305     numread += ret;
00306   }
00307   else if( ret == 0 )
00308   {
00309     ERR("Connection was closed by server\r\n");
00310     return -1; //Connection was closed by server
00311   }
00312   else
00313   {
00314     ERR("Connection error (recv returned %d)\r\n", ret);
00315     return -1;
00316   }
00317   
00318   DBG("Received %d bytes", numread);
00319   return numread;   
00320 }
00321 
00322 int HttpClient::send(char* buf, size_t len) //0 on success, err code on failure
00323 {
00324   if(len == 0)
00325   {
00326     len = strlen(buf);
00327   }
00328   //pc.printf("Trying to write %d bytes\r\n", len);
00329   size_t writtenLen = 0;
00330     
00331   if(!m_sock.is_connected())
00332   {
00333     ERR("Connection was closed by server\r\n");
00334     return -1; //Connection was closed by server 
00335   }
00336   
00337   m_sock.set_blocking(false, _timeout);
00338   int ret = m_sock.send_all(buf, len);
00339   if(ret > 0)
00340   {
00341     writtenLen += ret;
00342   }
00343   else if( ret == 0 )
00344   {
00345     ERR("Connection was closed by server\r\n");
00346     return -1; //Connection was closed by server
00347   }
00348   else
00349   {
00350     ERR("Connection error (send returned %d)\r\n", ret);
00351     return -1;
00352   }
00353   
00354   DBG("Written %d bytes\r\n", writtenLen);
00355   return 0;
00356 }
00357