The code from https://github.com/vpcola/Nucleo
Diff: HttpClient.cpp
- Revision:
- 0:5464d5e415e5
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HttpClient.cpp Wed Oct 08 11:00:24 2014 +0000 @@ -0,0 +1,357 @@ +#include "HttpClient.h" +#include "Utility.h" +#include <string> +#include <cstring> +#include <vector> + +extern Serial pc; + +#if 1 +//Enable debug +#include <cstdio> +#define DBG(x, ...) pc.printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__); +#define WARN(x, ...) pc.printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__); +#define ERR(x, ...) pc.printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__); +#else +//Disable debug +#define DBG(x, ...) +#define WARN(x, ...) +#define ERR(x, ...) +#endif + + + + +HttpClient::HttpClient(const char * hostname) + : _hostname(hostname) +{ +} + +int HttpClient::get(const char * url, char * buffer, size_t buf_len, HeaderInfo & hinfo, int timeout) +{ + char scheme[8]; + uint16_t port; + char host[32]; + char path[64]; + + _timeout = timeout; + + if(parse_url(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path))) + { + ERR("Error parsing URL"); + return -1; + } + + if(port == 0) //TODO do handle HTTPS->443 + { + port = 80; + } + + DBG("Scheme: %s", scheme); + DBG("Host: %s", host); + DBG("Port: %d", port); + DBG("Path: %s", path); + + // Connect to server + int ret = m_sock.connect(host, port); + if (ret < 0) + { + m_sock.close(); + ERR("Could not connect"); + return -1; + } + + //Send request + std::string req_header = build_http_headers("GET", (const char *) path); + ret = send((char *) req_header.c_str(), req_header.size()); + if(ret) + { + m_sock.close(); + ERR("Could not write request\r\n"); + return -1; + } + // Process the response + // First get the header + DBG("Reading HTTP header ...\r\n"); + char header_buf[512]; + int header_line = 0, numread = 0; + + do { + memset(header_buf, 0, sizeof(header_buf)); + numread = recv_until(header_buf, sizeof(header_buf), '\n'); + // remove trailing \r\n + std::string line(header_buf, numread); + line = trim(line, " \t\r\n"); + + DBG("Header line [%s]\r\n", line.c_str()); + parse_headers(line, hinfo, header_line); + header_line++; + }while(numread > 2); // empty line for \r\n + + // Show the header information + DBG("Http response: %d\r\n", hinfo.http_response); + DBG("Http response text: %s\r\n", hinfo.http_response_text.c_str()); + for(std::map<std::string, std::string>::iterator it = hinfo.http_info.begin(); + it != hinfo.http_info.end(); + it++) + { + DBG("Key [%s] : Value [%s]\r\n", it->first.c_str(), it->second.c_str()); + } + + // Read the rest of the data based on content type + return read_data(hinfo, buffer, buf_len); + +} + +int HttpClient::read_data(HeaderInfo & hinfo, char * buffer, size_t buf_len) +{ + int chunk_size = 0; + char tmp_buf[10]; // just for reading the \r\n values + char * buf_ptr = buffer; + int numread = 0; + + m_sock.set_blocking(false, _timeout); + if (hinfo.http_info["Transfer-Encoding"] == "chunked") + { + do { + // read the chunk size + DBG("Reading chunk size ...\r\n"); + if ( m_sock.receive_until(tmp_buf, 10, '\n') > 0) + { + chunk_size = strtoul(tmp_buf, NULL, 16); + DBG("Chunk size: %d\r\n", chunk_size); + }else + break; + + if (chunk_size > 0) + { + // read one char at a time untill chunk size + int cur_read2 = 0; + do { + if (m_sock.receive(buf_ptr,1) < 0) + { + DBG("Error reading chunks with total read [%d]...\r\n", numread); + return -1; + } + cur_read2 ++; + buf_ptr ++; + }while(cur_read2 < chunk_size); + DBG("Read %d chunk\r\n", cur_read2); + } + + numread += chunk_size; + DBG("Consuming new lines ...\r\n"); + // Consume the next new line + m_sock.receive_until(tmp_buf, 10, '\n'); + + }while((chunk_size > 0) && ((buf_ptr - buffer) < buf_len)); + + }else + { + DBG("Reading complete data in one go ...\r\n"); + numread = m_sock.receive_all(buffer, buf_len); + } + + DBG("Read %d bytes", numread); + + // Finally close the socket + m_sock.close(); + + return numread; +} + +int HttpClient::parse_headers(std::string header_line, HeaderInfo & hinfo, int lineno) +{ + std::vector<std::string> tokens; + // remove the trailing \n + + if (lineno == 0) + { + // Tokenize the first line using spaces + split(tokens, header_line, " "); + if (tokens.size() == 3) + { + //for(int i=0; i < tokens.size() ; i++) + // pc.printf("Token[%d] = [%s]\r\n", i, tokens[i].c_str()); + + // The second part is the http response + hinfo.http_response = atol(tokens[1].c_str()); + DBG("HTTP response [%d]\r\n", hinfo.http_response); + hinfo.http_response_text = trim(tokens[2]); + DBG("HTTP text [%s]\r\n", hinfo.http_response_text.c_str()); + } + }else + { + + // Scan for key value pairs + split(tokens, header_line, ":"); + if (tokens.size() == 2) + { + //for(int i=0; i < tokens.size() ; i++) + // pc.printf("Token[%d] = [%s]\r\n", i, tokens[i].c_str()); + + std::string key = trim(tokens[0]); + std::string value = trim(tokens[1]); + + hinfo.http_info[key] = value; + } + } + + return 0; +} + +std::string HttpClient::build_http_headers(std::string type, std::string path) +{ + std::string request_hdr = type + " " + path + " " + "HTTP/1.1" + "\r\n"; + request_hdr += "Host:" + _hostname + "\r\n"; + request_hdr += "\r\n"; // empty line terminates the header + + return request_hdr; + +} + +int HttpClient::parse_url(const char* url, char* scheme, + size_t scheme_len, + char* host, + size_t host_len, + uint16_t* port, + char* path, + size_t path_len) //Parse URL +{ + char* schemePtr = (char*) url; + char* hostPtr = (char*) strstr(url, "://"); + if(hostPtr == NULL) + { + ERR("Invalid URL [%s]\r\n", url); + return -1; //URL is invalid + } + + if( scheme_len < hostPtr - schemePtr + 1 ) //including NULL-terminating char + { + ERR("Scheme str is too small (%d >= %d)\r\n", scheme_len, hostPtr - schemePtr + 1); + return -1; + } + memcpy(scheme, schemePtr, hostPtr - schemePtr); + scheme[hostPtr - schemePtr] = '\0'; + + hostPtr+=3; + + size_t hostLen = 0; + + char* portPtr = strchr(hostPtr, ':'); + if( portPtr != NULL ) + { + hostLen = portPtr - hostPtr; + portPtr++; + if( sscanf(portPtr, "%hu", port) != 1) + { + ERR("Could not find port\r\n"); + return -1; + } + } + else + { + *port=0; + } + char* pathPtr = strchr(hostPtr, '/'); + if( hostLen == 0 ) + { + hostLen = pathPtr - hostPtr; + } + + if( host_len < hostLen + 1 ) //including NULL-terminating char + { + ERR("Host str is too small (%d >= %d)\r\n", host_len, hostLen + 1); + return -1; + } + memcpy(host, hostPtr, hostLen); + host[hostLen] = '\0'; + + size_t pathLen; + char* fragmentPtr = strchr(hostPtr, '#'); + if(fragmentPtr != NULL) + { + pathLen = fragmentPtr - pathPtr; + } + else + { + pathLen = strlen(pathPtr); + } + + if( path_len < pathLen + 1 ) //including NULL-terminating char + { + ERR("Path str is too small (%d >= %d)\r\n", path_len, pathLen + 1); + return -1; + } + memcpy(path, pathPtr, pathLen); + path[pathLen] = '\0'; + + return 0; +} + +int HttpClient::recv_until(char * buf, size_t len, char term) +{ + int numread = 0; + if(!m_sock.is_connected()) + { + ERR("Connection was closed by server\r\n"); + return -1; //Connection was closed by server + } + + m_sock.set_blocking(false, _timeout); + int ret = m_sock.receive_until(buf, len, term); + if(ret > 0) + { + numread += ret; + } + else if( ret == 0 ) + { + ERR("Connection was closed by server\r\n"); + return -1; //Connection was closed by server + } + else + { + ERR("Connection error (recv returned %d)\r\n", ret); + return -1; + } + + DBG("Received %d bytes", numread); + return numread; +} + +int HttpClient::send(char* buf, size_t len) //0 on success, err code on failure +{ + if(len == 0) + { + len = strlen(buf); + } + //pc.printf("Trying to write %d bytes\r\n", len); + size_t writtenLen = 0; + + if(!m_sock.is_connected()) + { + ERR("Connection was closed by server\r\n"); + return -1; //Connection was closed by server + } + + m_sock.set_blocking(false, _timeout); + int ret = m_sock.send_all(buf, len); + if(ret > 0) + { + writtenLen += ret; + } + else if( ret == 0 ) + { + ERR("Connection was closed by server\r\n"); + return -1; //Connection was closed by server + } + else + { + ERR("Connection error (send returned %d)\r\n", ret); + return -1; + } + + DBG("Written %d bytes\r\n", writtenLen); + return 0; +} +