Fork HTTPClient and Modfiy code for mbed 6.0
Dependents: mbed-demo-http-get-json
Diff: HTTPClient.cpp
- Revision:
- 24:eee214e3e806
- Parent:
- 22:d6b08d9749d6
- Child:
- 25:76084defa790
--- a/HTTPClient.cpp Sat Mar 15 18:40:29 2014 +0000 +++ b/HTTPClient.cpp Sat Mar 15 22:18:30 2014 +0000 @@ -46,7 +46,8 @@ #include "HTTPClient.h" HTTPClient::HTTPClient() : - m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0) + m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0), + m_maxredirections(1), m_location(NULL) { } @@ -58,7 +59,7 @@ void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification { - #if 1 +#if 1 if (m_basicAuthUser) free(m_basicAuthUser); m_basicAuthUser = (char *)malloc(strlen(user)+1); @@ -66,11 +67,11 @@ if (m_basicAuthPassword) free(m_basicAuthPassword); m_basicAuthPassword = (char *)malloc(strlen(password)+1); - strcpy(m_basicAuthPassword, password); - #else + strcpy(m_basicAuthPassword, password); +#else m_basicAuthUser = user; m_basicAuthPassword = password; - #endif +#endif } void HTTPClient::customHeaders(const char **headers, size_t pairs) @@ -112,6 +113,13 @@ return m_httpResponseCode; } +void HTTPClient::setMaxRedirections(int i) +{ + if (i < 1) + i = 1; + m_maxredirections = i; +} + #define CHECK_CONN_ERR(ret) \ do{ \ if(ret) { \ @@ -142,230 +150,255 @@ uint16_t port; char host[32]; char path[64]; - //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?) - HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path)); - if(res != HTTP_OK) { - ERR("parseURL returned %d", res); - return res; - } - - 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); + size_t recvContentLength = 0; + bool recvChunked = false; + int crlfPos = 0; + char buf[CHUNK_SIZE]; + size_t trfLen; + int ret = 0; + + int maxRedirect = m_maxredirections; - //Connect - DBG("Connecting socket to server"); - int ret = m_sock.connect(host, port); - if (ret < 0) { - m_sock.close(); - ERR("Could not connect"); - return HTTP_CONN; - } + while (maxRedirect--) { + bool takeRedirect = false; + + INFO("parse: [%s]", url); + //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?) + HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path)); + if(res != HTTP_OK) { + ERR("parseURL returned %d", res); + return res; + } - //Send request - DBG("Sending request"); - char buf[CHUNK_SIZE]; - const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":""; - snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s:%d\r\nConnection: keep-alive\r\n", meth, path, host, port); //Write request - ret = send(buf); - if (ret) { - m_sock.close(); - ERR("Could not write request"); - return HTTP_CONN; - } + if(port == 0) { //TODO do handle HTTPS->443 + port = 80; + } - // send authorization - if (m_basicAuthUser && m_basicAuthPassword) { - strcpy(buf, "Authorization: Basic "); - createauth(m_basicAuthUser, m_basicAuthPassword, buf+strlen(buf), sizeof(buf)-strlen(buf)); - strcat(buf, "\r\n"); - INFO(" (%s,%s) => (%s)", m_basicAuthUser, m_basicAuthPassword, buf); - ret = send(buf); - INFO(" ret = %d", ret); - if(ret) { + DBG("Scheme: %s", scheme); + DBG("Host: %s", host); + DBG("Port: %d", port); + DBG("Path: %s", path); + + //Connect + DBG("Connecting socket to server"); + ret = m_sock.connect(host, port); + if (ret < 0) { m_sock.close(); - ERR("Could not write request"); + ERR("Could not connect"); return HTTP_CONN; } - } - //Send all headers - for (size_t nh = 0; nh < m_nCustomHeaders * 2; nh+=2) { - INFO("hdr[%d] %s:", nh, m_customHeaders[nh]); - INFO(" %s", m_customHeaders[nh+1]); - snprintf(buf, sizeof(buf), "%s: %s\r\n", m_customHeaders[nh], m_customHeaders[nh+1]); + //Send request + DBG("Sending request"); + const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":""; + snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s:%d\r\nConnection: keep-alive\r\n", meth, path, host, port); //Write request ret = send(buf); if (ret) { - ERR("closing"); - wait_ms(50); m_sock.close(); ERR("Could not write request"); return HTTP_CONN; } - INFO(" hdr %d", ret); - } - //Send default headers - DBG("Sending headers"); - if( pDataOut != NULL ) { - if( pDataOut->getIsChunked() ) { - ret = send("Transfer-Encoding: chunked\r\n"); - CHECK_CONN_ERR(ret); - } else { - snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen()); + // send authorization + if (m_basicAuthUser && m_basicAuthPassword) { + strcpy(buf, "Authorization: Basic "); + createauth(m_basicAuthUser, m_basicAuthPassword, buf+strlen(buf), sizeof(buf)-strlen(buf)); + strcat(buf, "\r\n"); + INFO(" (%s,%s) => (%s)", m_basicAuthUser, m_basicAuthPassword, buf); ret = send(buf); - CHECK_CONN_ERR(ret); + INFO(" ret = %d", ret); + if(ret) { + m_sock.close(); + ERR("Could not write request"); + return HTTP_CONN; + } } - char type[48]; - if( pDataOut->getDataType(type, 48) == HTTP_OK ) { - snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type); - ret = send(buf); - CHECK_CONN_ERR(ret); - } - } - //Close headers - DBG("Headers sent"); - ret = send("\r\n"); - CHECK_CONN_ERR(ret); - - size_t trfLen; + //Send all headers + for (size_t nh = 0; nh < m_nCustomHeaders * 2; nh+=2) { + INFO("hdr[%d] %s:", nh, m_customHeaders[nh]); + INFO(" %s", m_customHeaders[nh+1]); + snprintf(buf, sizeof(buf), "%s: %s\r\n", m_customHeaders[nh], m_customHeaders[nh+1]); + ret = send(buf); + if (ret) { + ERR("closing"); + wait_ms(50); + m_sock.close(); + ERR("Could not write request"); + return HTTP_CONN; + } + INFO(" hdr %d", ret); + } - //Send data (if available) - if( pDataOut != NULL ) { - DBG("Sending data"); - while(true) { - size_t writtenLen = 0; - pDataOut->read(buf, CHUNK_SIZE, &trfLen); + //Send default headers + DBG("Sending headers"); + if( pDataOut != NULL ) { if( pDataOut->getIsChunked() ) { - //Write chunk header - char chunkHeader[16]; - snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding - ret = send(chunkHeader); + ret = send("Transfer-Encoding: chunked\r\n"); CHECK_CONN_ERR(ret); - } else if( trfLen == 0 ) { - break; - } - if( trfLen != 0 ) { - ret = send(buf, trfLen); + } else { + snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen()); + ret = send(buf); CHECK_CONN_ERR(ret); } - - if( pDataOut->getIsChunked() ) { - ret = send("\r\n"); //Chunk-terminating CRLF + char type[48]; + if( pDataOut->getDataType(type, 48) == HTTP_OK ) { + snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type); + ret = send(buf); CHECK_CONN_ERR(ret); - } else { - writtenLen += trfLen; - if( writtenLen >= pDataOut->getDataLen() ) { + } + } + + //Close headers + DBG("Headers sent"); + ret = send("\r\n"); + CHECK_CONN_ERR(ret); + + //Send data (if available) + if( pDataOut != NULL ) { + DBG("Sending data"); + while(true) { + size_t writtenLen = 0; + pDataOut->read(buf, CHUNK_SIZE, &trfLen); + if( pDataOut->getIsChunked() ) { + //Write chunk header + char chunkHeader[16]; + snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding + ret = send(chunkHeader); + CHECK_CONN_ERR(ret); + } else if( trfLen == 0 ) { + break; + } + if( trfLen != 0 ) { + ret = send(buf, trfLen); + CHECK_CONN_ERR(ret); + } + + if( pDataOut->getIsChunked() ) { + ret = send("\r\n"); //Chunk-terminating CRLF + CHECK_CONN_ERR(ret); + } else { + writtenLen += trfLen; + if( writtenLen >= pDataOut->getDataLen() ) { + break; + } + } + + if( trfLen == 0 ) { break; } } - if( trfLen == 0 ) { - break; - } } - } - - //Receive response - DBG("Receiving response"); - ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes - CHECK_CONN_ERR(ret); - buf[trfLen] = '\0'; - INFO("Received \r\n(%s\r\n)", buf); - - char* crlfPtr = strstr(buf, "\r\n"); - if( crlfPtr == NULL) { - PRTCL_ERR(); - } - - int crlfPos = crlfPtr - buf; - buf[crlfPos] = '\0'; - - //Parse HTTP response - if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) { - //Cannot match string, error - ERR("Not a correct HTTP answer : %s\n", buf); - PRTCL_ERR(); - } + //Receive response + DBG("Receiving response"); + ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes + CHECK_CONN_ERR(ret); + buf[trfLen] = '\0'; + INFO("Received \r\n(%s\r\n)", buf); - if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 300) ) { - //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers - WARN("Response code %d", m_httpResponseCode); - PRTCL_ERR(); - } - - DBG("Reading headers"); - - memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well - trfLen -= (crlfPos + 2); - - size_t recvContentLength = 0; - bool recvChunked = false; - //Now get headers - while( true ) { - crlfPtr = strstr(buf, "\r\n"); - if(crlfPtr == NULL) { - if( trfLen < CHUNK_SIZE - 1 ) { - size_t newTrfLen = 0; - ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen); - trfLen += newTrfLen; - buf[trfLen] = '\0'; - DBG("Read %d chars; In buf: [%s]", newTrfLen, buf); - CHECK_CONN_ERR(ret); - continue; - } else { - PRTCL_ERR(); - } + char* crlfPtr = strstr(buf, "\r\n"); + if( crlfPtr == NULL) { + PRTCL_ERR(); } crlfPos = crlfPtr - buf; - - if(crlfPos == 0) { //End of headers - DBG("Headers read"); - memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well - trfLen -= 2; - break; - } - buf[crlfPos] = '\0'; - char key[32]; - char value[32]; - - key[31] = '\0'; - value[31] = '\0'; + //Parse HTTP response + if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) { + //Cannot match string, error + ERR("Not a correct HTTP answer : %s\n", buf); + PRTCL_ERR(); + } - int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value); - if ( n == 2 ) { - DBG("Read header : %s: %s\n", key, value); - if( !strcmp(key, "Content-Length") ) { - sscanf(value, "%d", &recvContentLength); - pDataIn->setDataLen(recvContentLength); - } else if( !strcmp(key, "Transfer-Encoding") ) { - if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") ) { - recvChunked = true; - pDataIn->setIsChunked(true); - } - } else if( !strcmp(key, "Content-Type") ) { - pDataIn->setDataType(value); - } - - memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well - trfLen -= (crlfPos + 2); - - } else { - ERR("Could not parse header"); + if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 400) ) { + //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers + WARN("Response code %d", m_httpResponseCode); PRTCL_ERR(); } - } + DBG("Reading headers"); + + memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well + trfLen -= (crlfPos + 2); + + recvContentLength = 0; + recvChunked = false; + //Now get headers + while( true ) { + crlfPtr = strstr(buf, "\r\n"); + if(crlfPtr == NULL) { + if( trfLen < CHUNK_SIZE - 1 ) { + size_t newTrfLen = 0; + ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen); + trfLen += newTrfLen; + buf[trfLen] = '\0'; + DBG("Read %d chars; In buf: [%s]", newTrfLen, buf); + CHECK_CONN_ERR(ret); + continue; + } else { + PRTCL_ERR(); + } + } + + crlfPos = crlfPtr - buf; + + if(crlfPos == 0) { //End of headers + DBG("Headers read"); + memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well + trfLen -= 2; + break; + } + + buf[crlfPos] = '\0'; + + char key[32]; + char value[64]; + + key[31] = '\0'; + value[63] = '\0'; + + int n = sscanf(buf, "%31[^:]: %63[^\r\n]", key, value); + if ( n == 2 ) { + DBG("Read header : %s: %s\n", key, value); + if( !strcmp(key, "Content-Length") ) { + sscanf(value, "%d", &recvContentLength); + pDataIn->setDataLen(recvContentLength); + } else if( !strcmp(key, "Transfer-Encoding") ) { + if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") ) { + recvChunked = true; + pDataIn->setIsChunked(true); + } + } else if( !strcmp(key, "Content-Type") ) { + pDataIn->setDataType(value); + } else if ( !strcmp(key, "Location") ) { + if (m_location) + free(m_location); + m_location = (char *)malloc(strlen(value)+1); + if (m_location) { + strcpy(m_location,value); + url = m_location; + INFO("Following redirect[%d] to [%s]", maxRedirect, url); + m_sock.close(); + takeRedirect = true; + break; // exit the while(true) header to follow the redirect + } + } + + memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well + trfLen -= (crlfPos + 2); + + } else { + ERR("Could not parse header"); + PRTCL_ERR(); + } + + } // while(true) // get headers + if (!takeRedirect) + break; + } // while (maxRedirect) //Receive data DBG("Receiving data");