A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.

Dependents:   Smart-WiFly-WebServer WattEye X10Svr SSDP_Server

Revision:
29:00116fc9da74
Parent:
28:f93ef41b78e1
Child:
30:864843965b40
--- a/SW_HTTPServer.cpp	Fri Oct 11 02:33:46 2013 +0000
+++ b/SW_HTTPServer.cpp	Fri Oct 11 23:47:31 2013 +0000
@@ -11,16 +11,17 @@
 // author David Smart, Smartware Computing
 //
 #include "mbed.h"
-#include "SW_HTTPServer.h"
+
 #include "Utility.h"
 
 #define DEBUG "HTTP"
+#include "SW_HTTPServer.h"
 
 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
-#define DBG(x, ...)  std::printf("[DBG %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define WARN(x, ...) std::printf("[WRN %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define ERR(x, ...)  std::printf("[ERR %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define INFO(x, ...) std::printf("[INF %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define DBG(x, ...)  pc->printf("[DBG %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define WARN(x, ...) pc->printf("[WRN %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define ERR(x, ...)  pc->printf("[ERR %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define INFO(x, ...) pc->printf("[INF %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
 #else
 #define DBG(x, ...)
 #define WARN(x, ...)
@@ -62,12 +63,12 @@
 // This uses standard library dynamic memory management, but for an
 // embedded system there are alternates that may make better sense -
 // search the web for embedded system malloc alternates.
-static void * MyMalloc(int x, int y)
+void * HTTPServer::MyMalloc(int x, int y)
 {
-    std::printf("[INF HTTP%4d] malloc(%d)\r\n", y, x);
+    pc->printf("[INF HTTP%4d] malloc(%d)\r\n", y, x);
     return malloc(x);
 }
-static char toP(void * x)
+char HTTPServer::toP(void * x)
 {
     char * c = (char *) x;
     if (*c >= ' ' && *c < 0x7F)
@@ -169,7 +170,8 @@
 {
     typedef enum {
         Idle,               // waiting for a connection
-        Receiving,          // receiving data
+        ReceivingHeader,    // receiving header
+        ReceivingPayload,   // receiving a section after the Header
         Sending,            // send the response
         WaitingToClose,     // small timeout to close
         Reset
@@ -182,7 +184,7 @@
 #if defined(DEBUG)
     static state lastOp = Reset;
     if (lastOp != op) {
-        const char *states[] = {"Idle", "Receiving", "Sending", "WaitingToClose", "Reset"};
+        const char *states[] = {"Idle", "ReceivingHeader", "ReceivingPayload", "Sending", "WaitingToClose", "Reset"};
         INFO("Poll: %s", states[op]);
         lastOp = op;
     }
@@ -197,27 +199,40 @@
             t_ref = (unsigned int)PerformanceTimer.read_us();
             bPtr = headerbuffer;
             if (0 == server->accept(client)) {
-                op = Receiving;
+                op = ReceivingHeader;
                 t_ref = RecordPerformanceData(&perfData.ConnectionAccepted, t_ref);
                 INFO("Accepted at %u", (unsigned int)PerformanceTimer.read_us());
             }
             break;
 
-        case Receiving:
+        case ReceivingHeader:
             n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer));
             if (n < 0) {
+                op = Sending;
                 INFO("*** client.receive() => %d", n);
             } else if (n) {
                 bPtr[n] = '\0';
-                if (ParseHeader(headerbuffer)) {
-                    op = Sending;
-                    t_ref = RecordPerformanceData(&perfData.HeaderParsed, t_ref);
-                    INFO("Header Parsed at %u", (unsigned int)PerformanceTimer.read_us());
+                switch (ParseHeader(headerbuffer)) {
+                    case ACCEPT_ERROR:
+                        break;
+                    case ACCEPT_COMPLETE:
+                        op = Sending;
+                        t_ref = RecordPerformanceData(&perfData.HeaderParsed, t_ref);
+                        INFO("Header Parsed at %u", (unsigned int)PerformanceTimer.read_us());
+                        break;
+                    case ACCEPT_CONTINUE:
+                        op = ReceivingPayload;
+                        break;
                 }
                 bPtr += n;
             }
             break;
 
+        case ReceivingPayload:
+            // After the header, there is a payload that will be handled
+            op = Sending;
+            break;
+
         case Sending:
             SendResponse();
             op = WaitingToClose;
@@ -537,10 +552,10 @@
 }
 
 
-bool HTTPServer::ParseHeader(char * buffer)
+HTTPServer::CallBackResults HTTPServer::ParseHeader(char * buffer)
 {
     char * dblCR;
-    bool advanceState = false;
+    CallBackResults advanceState = ACCEPT_ERROR;
     int bytecount;
 
     // Buffer could have partial, but the double \r\n is the key
@@ -549,7 +564,7 @@
     //      GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Control: max-age=0\r\n\r\n
     dblCR = strstr(buffer,"\r\n\r\n");
     if (dblCR) {  // Have to scan from the beginning in case split on \r
-        INFO("==\r\n%s==\r\n", buffer);
+        INFO("\r\n==\r\n%s==", buffer);
         char * soRec = buffer;                  // start of the next record of text
         char * eoRec = strchr(soRec, '\n');     // search for end of the current record
 
@@ -581,20 +596,21 @@
             // "Connection: keep-alive" becomes "Connection" "keep-alive"
             char *delim = strstr(soRec, ": ");
             char *chkSpace = strchr(soRec, ' ');        // a field-name has no space ahead of the ":"
-            if (delim 
-            && (!chkSpace || (chkSpace && delim < chkSpace))
-            && headerParamCount < maxheaderParams) {
+            if (delim
+                    && (!chkSpace || (chkSpace && delim < chkSpace))
+                    && headerParamCount < maxheaderParams) {
                 *delim++ = '\0';        // replace ": " with null
                 *delim++ = '\0';
                 headerParams[headerParamCount].name = soRec;
                 headerParams[headerParamCount].value = delim;
                 INFO("%d: headerParams[%s] = {%s}", headerParamCount,
-                           headerParams[headerParamCount].name, headerParams[headerParamCount].value);
+                     headerParams[headerParamCount].name, headerParams[headerParamCount].value);
                 headerParamCount++;
             }
             soRec = eoRec + 1;
             eoRec = strchr(soRec, '\n');
         }
+        
         if (queryString) {
             // We have enough to try to reply
             INFO("create reply queryType{%s}, queryString{%s}", "GET", queryString);
@@ -606,12 +622,12 @@
             if (paramDelim) {
                 *paramDelim++ = '\0';
                 UnescapeString(paramDelim);   // everything after the '?'
-                ParseParameters(paramDelim);    // pointing at the NULL, but there are queryParams beyond
+                ParseParameters(paramDelim);  // pointing at the NULL, but there are queryParams beyond
             }
         } else {
-            ERR("queryString not found in (%s)", soRec);
+            ERR("queryString not found in (%s) [this should never happen]", soRec);
         }
-        advanceState = true;
+        advanceState = ACCEPT_COMPLETE;
         buffer[0] = 0;
 
         // This part parses the extra data on a POST method.
@@ -619,8 +635,6 @@
         // it would make sense to move some of this responsibility to
         // that handler. It could then choose if it wanted to allocate
         // the requested 'Content-Length' amount of memory.
-        // Should we check the 'Content-Type' to see if it is
-        //   'application/x-www-form-urlencoded'?
         int postBytes = atoi(GetHeaderValue("Content-Length"));
         CallBackResults acceptIt = ACCEPT_ERROR;
         if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) {
@@ -633,7 +647,7 @@
                     if (strcmp(handlers[ndxHandler].path, queryString) == 0) {
                         acceptIt = (*handlers[ndxHandler].callback)(this, CONTENT_LENGTH_REQUEST, queryString, queryParams, queryParamCount);
                         regHandled = true;
-                        break;      // we only execute the first one
+                        break;      // only one callback per path allowed
                     }
                 }
 
@@ -665,7 +679,7 @@
                                 offset[n] = '\0';
                                 INFO("HTTPd: %d of %d: [%s]", len, postBytes, offset);
                             } else if (n < 0) {
-                                INFO("HTTPd; n=%d", n);
+                                INFO("*** receive returned %d ***", n);
                                 break;      // no more data, before the plan
                             }
                         }
@@ -685,6 +699,7 @@
                     // Simply copy it to the bitbucket
                     int bytesToDump = postBytes;
                     char * bitbucket = (char *)mymalloc(201);
+                    
                     dblCR += 4;
                     while (*dblCR && *dblCR <= ' ')
                         dblCR++;
@@ -692,6 +707,10 @@
                     while (bytesToDump > 0) {
                         int n = (bytesToDump > 200) ? 200 : bytesToDump;
                         n = client.receive(bitbucket, n);
+                        if (n < 0) {
+                            ERR("to the bitbucket.");
+                            break;
+                        }
                         bytesToDump -= n;
                     }
                     myfree(bitbucket);
@@ -707,12 +726,11 @@
 const char * HTTPServer::GetHeaderValue(const char * hdr)
 {
     int i;
-    
-    for (i=0; i<headerParamCount; i++)
-        {
+
+    for (i=0; i<headerParamCount; i++) {
         if (strcmp(hdr, headerParams[i].name) == 0)
             return headerParams[i].value;
-        }
+    }
     return NULL;
 }