Fixed custom headers and Basic authorization, added support for redirection, functional file download interface can be used for SW updates and more.

Dependents:   Sample_HTTPClient Sample_HTTPClient LWM2M_NanoService_Ethernet LWM2M_NanoService_Ethernet ... more

Fork of HTTPClient by Vincent Wochnik

More recent changes - added iCal processing.

Derivative of a derivative, however this one works when it comes to supplying Basic authorization to access a protected resource. Some additional changes to the debug interface to clean it up for consistency with many other components I have.

Files at this revision

API Documentation at this revision

Comitter:
WiredHome
Date:
Sat Mar 15 22:18:30 2014 +0000
Parent:
23:517fec8b8b99
Child:
25:76084defa790
Commit message:
Reformatted to match standard style.; Added support for automatically following redirection, and for getting the redirect location.

Changed in this revision

HTTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPClient.h Show annotated file Show diff for this revision Revisions of this file
data/HTTPFile.cpp Show annotated file Show diff for this revision Revisions of this file
--- 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");
--- a/HTTPClient.h	Sat Mar 15 18:40:29 2014 +0000
+++ b/HTTPClient.h	Sat Mar 15 22:18:30 2014 +0000
@@ -147,6 +147,19 @@
     */
     int getHTTPResponseCode();
 
+    /** Set the maximum number of automated redirections
+    @param i is the number of redirections. Values < 1 are
+        set to 1.
+    */
+    void setMaxRedirections(int i = 1);
+
+    /** get the redirect location url
+    @returns const char pointer to the url.
+    */
+    const char * getLocation() {
+        return m_location;
+    }
+
 private:
     enum HTTP_METH {
         HTTP_GET,
@@ -173,6 +186,8 @@
     const char** m_customHeaders;
     size_t m_nCustomHeaders;
     int m_httpResponseCode;
+    int m_maxredirections;
+    char * m_location;
 
 };
 
--- a/data/HTTPFile.cpp	Sat Mar 15 18:40:29 2014 +0000
+++ b/data/HTTPFile.cpp	Sat Mar 15 22:18:30 2014 +0000
@@ -30,6 +30,10 @@
 
 }
 
+void HTTPFile::setLocation(const char * location) {
+
+}
+
 void HTTPFile::setIsChunked(bool chunked) {
     m_chunked = chunked;
 }