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

Dependents:   Smart-WiFly-WebServer WattEye X10Svr SSDP_Server

Revision:
58:5303e962f711
Parent:
57:fb81057b4d77
Child:
59:9a71ac02c782
--- a/SW_HTTPServer.cpp	Tue Jul 10 23:13:55 2018 +0000
+++ b/SW_HTTPServer.cpp	Sun Nov 18 04:04:16 2018 +0000
@@ -11,14 +11,15 @@
 // author David Smart, Smartware Computing
 //
 #include "mbed.h"
+#include "SW_String.h"      // Secure methods and others that were missing
 
-//#define DEBUG "HTTP"
+#define DEBUG "HTTP"
 #include <cstdio>
 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
-#define DBG(x, ...)  std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define ERR(x, ...)  std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
-#define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#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__);
 #else
 #define DBG(x, ...)
 #define WARN(x, ...)
@@ -26,6 +27,7 @@
 #define INFO(x, ...)
 #endif
 
+extern void ShowSignOfLife(int which);
 
 #include "SW_HTTPServer.h"      // define DEBUG before this
 
@@ -112,6 +114,8 @@
 #define myfree(x) free(x)
 #endif
 
+
+
 HTTPServer::HTTPServer(
     int port,
     const char * _webroot,
@@ -124,8 +128,10 @@
     int _allocforfile
     )
 {
-    webroot = (char *)malloc(strlen(_webroot)+1);
-    strcpy(webroot, _webroot);
+    int wrSize = strlen(_webroot)+1;
+    
+    webroot = (char *)malloc(wrSize);
+    strcpy_s(webroot, wrSize, _webroot);
     if (strlen(webroot)>1 && webroot[strlen(webroot)-1] == '/') // remove trailing '/'
         webroot[strlen(webroot)-1] = '\0';
     filenameAliasList = NULL;
@@ -223,7 +229,7 @@
     static state lastOp = Reset;
     if (lastOp != op) {
         const char *states[] = {"Idle", "ReceivingHeader", "ReceivingPayload", "Sending", "WaitingToClose", "Reset"};
-        INFO("Poll: %s", states[op]);
+        INFO("Poll: %-30s   ######## %8u us", states[op], (unsigned int)PerformanceTimer.read_us()-t_ref);
         lastOp = op;
     }
 #endif
@@ -233,13 +239,15 @@
             break;
 
         case Idle:
+            ShowSignOfLife(3);
             PerformanceTimer.reset();
             t_ref = (unsigned int)PerformanceTimer.read_us();
             bPtr = headerbuffer;
             if (0 == server->accept(client)) {
                 op = ReceivingHeader;
                 t_ref = RecordPerformanceData(&perfData.ConnectionAccepted, t_ref);
-                INFO("Accepted at %u", (unsigned int)PerformanceTimer.read_us());
+                INFO("Accepted at                                     %8u us", 
+                    (unsigned int)PerformanceTimer.read_us()-t_ref);
             } else {
                 //INFO("Timeout waiting for accept()");
             }
@@ -247,12 +255,12 @@
 
         case ReceivingHeader:
             n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer));
+            INFO("client.receive() returned %d, from %s", n, client.get_address());
             if (n == -2) {
-                ;   // timeout, so hang here waiting for traffic
                 //   was hang here waiting ... op = Sending; which causes misses
+                op = WaitingToClose;
             } else if (n < 0) {
-                // some error like a closed/crashed interface
-                INFO("%sclient.receive() returned %d, from %s", (n<0) ? "*** " : "", n, client.get_address());
+                op = WaitingToClose;    // bail out...
             } else if (n) {
                 bPtr[n] = '\0';
                 switch (ParseHeader(headerbuffer)) {
@@ -261,7 +269,8 @@
                     case ACCEPT_COMPLETE:
                         op = Sending;
                         t_ref = RecordPerformanceData(&perfData.HeaderParsed, t_ref);
-                        INFO("Header Parsed at %u", (unsigned int)PerformanceTimer.read_us());
+                        INFO("Header Parsed at                                %8u us", 
+                            (unsigned int)PerformanceTimer.read_us()-t_ref);
                         break;
                     case ACCEPT_CONTINUE:
                         op = ReceivingPayload;
@@ -274,6 +283,8 @@
         case ReceivingPayload:
             // After the header, there is a payload that will be handled
 #if 1
+            INFO("ReceivingPayload                                %8u us", 
+                (unsigned int)PerformanceTimer.read_us()-t_ref);
             n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer));
             if (n < 0) {
                 op = Sending;
@@ -288,17 +299,23 @@
             break;
 
         case Sending:
+            INFO("SendResponse at                                 %8u us", 
+                (unsigned int)PerformanceTimer.read_us()-t_ref);
             SendResponse();
             op = WaitingToClose;
             t_ref = RecordPerformanceData(&perfData.ResponseSent, t_ref);
-            INFO("Response Sent at %u", (unsigned int)PerformanceTimer.read_us());
+            INFO("Response Sent at                                %8u us", 
+                (unsigned int)PerformanceTimer.read_us()-t_ref);
             break;
 
         case WaitingToClose:
+            INFO("close_connection                                %8u us", 
+                (unsigned int)PerformanceTimer.read_us()-t_ref);
             close_connection();
             op = Idle;
             RecordPerformanceData(&perfData.ConnectionClosed, t_ref);
-            INFO("Connection closed exit: %u\r\n\r\n", (unsigned int)PerformanceTimer.read_us());
+            INFO("Connection closed exit:                         %8u us\r\n\r\n", 
+                (unsigned int)PerformanceTimer.read_us()-t_ref);
             break;
     }
 }
@@ -326,7 +343,7 @@
         bytes = strlen(msg);
     INFO("Sending %d bytes", bytes);
     //INFO("send:\r\n%s", msg);
-    int r = client.send((char *)msg, bytes);
+    int r = client.send_all((char *)msg, bytes);
     INFO("client.send returned: %d", r);
     return r;
 }
@@ -368,22 +385,29 @@
         char *fbuffer = (char *)mymalloc(FILESEND_BUF_SIZE);
         int bytes;
 
-        client.set_blocking(false, 450);
-        //server->set_blocking(true, 240);
         if (fbuffer) {
             char ContentLen[30];
             snprintf(ContentLen, sizeof(ContentLen), "Content-Length: %u\r\n", FileSize(filename));
             header(OK, "OK", filetype, ContentLen);
             header("");
-            bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp);
+            bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE-1,fp);
             INFO("Start with %d bytes", bytes);
+            int retryCount = 2;
             while (bytes > 0) {
                 int r = send(fbuffer, bytes);
-                INFO("sent %d", r);
                 if (r >= 0) {
-                    bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp);
+                    bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE-1,fp);
                     INFO("      Next %d bytes", bytes);
                     //INFO("::%s", fbuffer);
+                } else if (r == -2) {
+                    if (retryCount-- == 0) {
+                        ERR("send failed with %d [retries] - terminating SendFile().", r);
+                        break;
+                    }
+                    INFO("send failed with %d (timeout), hopefully it will recover.", r);
+                } else {
+                    ERR("send failed with %d, but why? - terminating SendFile().", r);
+                    break;
                 }
             }
             myfree(fbuffer);
@@ -542,10 +566,10 @@
         send(optional_text);
     } else {
         if (strlen(hdr_age) + strlen(hdr_server) + strlen(hdr_close) + strlen(nl) < sizeof(http)) {
-            strcpy(http, hdr_age);
-            strcat(http, hdr_server);
-            strcat(http, hdr_close);
-            strcat(http, nl);
+            strcpy_s(http, 100, hdr_age);
+            strcat_s(http, 100, hdr_server);
+            strcat_s(http, 100, hdr_close);
+            strcat_s(http, 100, nl);
             send(http);
         } else {
             send(hdr_age);
@@ -592,9 +616,10 @@
         // /QueryString\0HTTP/1.1\0\0
         if (*string)            // recycle old string when working a new one
             myfree(*string);
-        container = (char *)mymalloc(strlen(qs));
+        int ctSize = strlen(qs);
+        container = (char *)mymalloc(ctSize);
         if (container) {
-            strcpy(container, qs);
+            strcpy_s(container, ctSize, qs);
             eqs = strchr(container, ' ');
             if (eqs)
                 *eqs = '\0';
@@ -611,12 +636,13 @@
 
 char * HTTPServer::rewriteWithDefaultFile(char * queryString)
 {
-    char * temp = (char *)mymalloc(strlen(queryString) + strlen(DEFAULT_FILENAME) + 1);
+    int tSize = strlen(queryString) + strlen(DEFAULT_FILENAME) + 1;
+    char * temp = (char *)mymalloc(tSize);
 
     if (temp) {
         *temp = '\0';
-        strcpy(temp, queryString);
-        strcat(temp, DEFAULT_FILENAME);
+        strcpy_s(temp, tSize, queryString);
+        strcat_s(temp, tSize, DEFAULT_FILENAME);
         myfree(queryString);
         return temp;
     } else {
@@ -627,18 +653,19 @@
 
 char * HTTPServer::rewritePrependWebroot(char * queryString)
 {
-    char * temp = (char *)mymalloc(strlen(webroot) + strlen(queryString) + 2);  // save room for '/'
+    int tSize = strlen(webroot) + strlen(queryString) + 2;
+    char * temp = (char *)mymalloc(tSize);  // save room for '/'
 
     if (temp) {
         char *lastC;
         *temp = '\0';
-        strcpy(temp, webroot);
+        strcpy_s(temp, tSize, webroot);
         lastC = &temp[strlen(temp)-1];
         if (*lastC == '/' && *queryString == '/')
             queryString++;  // don't create two '/'
         else if (*lastC != '/' && *queryString != '/')
-            strcat(temp, "/");
-        strcat(temp, queryString);
+            strcat_s(temp, tSize, "/");
+        strcat_s(temp, tSize, queryString);
         myfree(queryString);
         return temp;
     } else {
@@ -666,7 +693,8 @@
 
 void HTTPServer::SendResponse()
 {
-    INFO("SendResponse(%s) at %u", queryType, (unsigned int)PerformanceTimer.read_us());
+    INFO("SendResponse(%5s) at                          %8u us", 
+        queryType, (unsigned int)PerformanceTimer.read_us());
     if (strcmp(queryType, "GET ") == 0 ||  strcmp(queryType, "POST ") == 0) {
         if (!(queryString[0] == '.' && queryString[1] == '.')) {
             const char * fType;
@@ -696,7 +724,8 @@
         header(Bad_Request, "Bad Request", NULL, "Pragma: err - Unsupported query type\r\n");
         header("");
     }
-    INFO("  SendResponse complete at %u", (unsigned int)PerformanceTimer.read_us());
+    INFO("  SendResponse complete at                      %8u us", 
+        (unsigned int)PerformanceTimer.read_us());
 
     if (queryType) {
         myfree(queryType);
@@ -710,7 +739,8 @@
         myfree(postQueryString);
         postQueryString = NULL;
     }
-    INFO("  SendResponse free at %u", (unsigned int)PerformanceTimer.read_us());
+    INFO("  SendResponse free at                          %8u us", 
+        (unsigned int)PerformanceTimer.read_us());
 }
 
 
@@ -746,8 +776,9 @@
                 if (strstr(soRec, qm->queryType) == soRec) {
                     Extract(soRec, qm->queryType, &queryString);
                     if (queryString) {
-                        queryType = (char *)mymalloc(strlen(qm->queryType)+1);
-                        strcpy(queryType, qm->queryType);
+                        int qSize = strlen(qm->queryType)+1;
+                        queryType = (char *)mymalloc(qSize);
+                        strcpy_s(queryType, qSize, qm->queryType);
                     }
                 }
                 qm++;
@@ -758,14 +789,16 @@
             if (strstr(soRec, "GET ") == soRec) {
                 Extract(soRec, "GET", &queryString);
                 if (queryString) {
-                    queryType = (char *)mymalloc(strlen("GET")+1);
-                    strcpy(queryType, "GET");
+                    int qSize = strlen("GET")+1;
+                    queryType = (char *)mymalloc(qSize);
+                    strcpy_s(queryType, qSize, "GET");
                 }
             } else if (strstr(soRec, "POST ") == soRec) {
                 Extract(soRec, "POST", &queryString);
                 if (queryString) {
-                    queryType = (char *)mymalloc(strlen("POST")+1);
-                    strcpy(queryType, "POST");
+                    int qSize = strlen("POST")+1;
+                    queryType = (char *)mymalloc(qSize);
+                    strcpy_s(queryType, qSize, "POST");
                 }
             }
             #endif
@@ -856,7 +889,7 @@
                     escapePlan.start();
                     dblCR += 4;     // There may be some after the double CR that we need
                     ttlCount = strlen(dblCR);
-                    strcpy(postQueryString, dblCR);
+                    strcpy_s(postQueryString, CHUNK_SIZE, dblCR);
                     //INFO("  {%s}", postQueryString);
                     while ((!postBytes || ttlCount < postBytes) && !escape) {
                         INFO("ttlCount: %d < postBytes: %d, of max chunk alloc %d", ttlCount, postBytes, CHUNK_SIZE);