Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
Generated on Tue Jul 12 2022 18:55:01 by
1.7.2