A simple web server mainly based on ideas from Jasper Schuurmans Netduino web server

Dependents:   RdBlindsServer SpideyWallWeb RdGasUseMonitor

A fast and reliable web server for MBED! http://robdobson.com/2015/08/a-reliable-mbed-webserver/

It has a very neat way to implement REST commands and can serve files from local storage (on LPC1768 for instance) and from SD cards. It also has a caching facility which is particularly useful for serving files from local storage.

The server can be run in the main() thread (and has a sub-2ms response time if this is done) or in a mbed-rtos thread which increases the response time to (a still respectable) 30ms or so.

The latest project that uses this is here - https://developer.mbed.org/users/Bobty/code/SpideyWallWeb/

int main (void)
{
    // Ethernet interface
    EthernetInterface::init();

    // Connect ethernet
    EthernetInterface::connect();

    // Init the web server
    pc.printf("Starting web server\r\n");
    char* baseWebFolder = "/sd/";  // should be /sd/ for SDcard files - not used for local file system
    RdWebServer webServer;
    
    // Add commands to handle the home page and favicon
    webServer.addCommand("", RdWebServerCmdDef::CMD_LOCALFILE, NULL, "index.htm", true);
    webServer.addCommand("favicon.ico", RdWebServerCmdDef::CMD_LOCALFILE, NULL, NULL, true);
    
    // Add the lightwall control commands
    webServer.addCommand("name", RdWebServerCmdDef::CMD_CALLBACK, &lightwallGetSystemName);
    webServer.addCommand("clear", RdWebServerCmdDef::CMD_CALLBACK, &lightwallClear);
    webServer.addCommand("rawfill", RdWebServerCmdDef::CMD_CALLBACK, &lightwallRawFill);
    webServer.addCommand("fill", RdWebServerCmdDef::CMD_CALLBACK, &lightwallFill);
    webServer.addCommand("showleds", RdWebServerCmdDef::CMD_CALLBACK, &lightwallShowLeds);
    
    // Start the server
    webServer.init(WEBPORT, &led4, baseWebFolder);
    webServer.run();

}

// Get system name - No arguments required
char* lightwallGetSystemName(int method, char*cmdStr, char* argStr, char* msgBuffer, int msgLen, 
                int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
{
    // Perform any required actions here ....

    // ...

    // Return the system name
    return systemName;
}

This server was originally based on a Netduino web server from Jasper Schuurmans but has been optimised for speed.

Revision:
11:cec51b430b20
Parent:
10:b4b9d4d5e5be
Child:
12:c14ffd4ec125
--- a/RdWebServer.cpp	Sun Feb 22 22:08:13 2015 +0000
+++ b/RdWebServer.cpp	Sun May 03 18:59:42 2015 +0000
@@ -33,23 +33,23 @@
     //setup tcp socket
     if(_socketSrv.bind(port)< 0) 
     {
-        pc.printf("TCP server bind fail\n\r");
+        RD_WARN("TCP server bind fail\n\r");
         return false;
     }
     else 
     {
-//        pc.printf("TCP server bind success\n\r");
+        RD_DBG("TCP server bind success\n\r");
         _serverIsListening = true;
     }
 
     if(_socketSrv.listen(1) < 0)
     {
-        pc.printf("TCP server listen fail\n\r");
+        RD_WARN("TCP server listen fail\n\r");
         return false;
     }
     else 
     {
-        pc.printf("TCP server is listening...\r\n");
+        RD_INFO("TCP server is listening...\r\n");
     }
     
     return true;
@@ -57,7 +57,7 @@
 
 void RdWebServer::handleReceivedHttp(TCPSocketConnection &client)
 {
-//    pc.printf("Received Data: %d\n\r\n\r%.*s\n\r",strlen(_buffer),strlen(_buffer),_buffer);
+    RD_DBG("Received Data: %d\n\r\n\r%.*s\n\r",strlen(_buffer),strlen(_buffer),_buffer);
     int method = METHOD_OTHER;
     if (strncmp(_buffer, "GET ", 4) == 0)
         method = METHOD_GET;
@@ -68,15 +68,15 @@
     char argStr[MAX_ARGSTR_LEN];
     if (extractCmdArgs(_buffer+3, cmdStr, MAX_CMDSTR_LEN, argStr, MAX_ARGSTR_LEN))
     {
-//        pc.printf("CmdStr %s\n\r", cmdStr);
-//        pc.printf("ArgStr %s\n\r", argStr);
+        RD_DBG("CmdStr %s\n\r", cmdStr);
+        RD_DBG("ArgStr %s\n\r", argStr);
         bool cmdFound = false;
         for (std::vector<RdWebServerCmdDef*>::iterator it = _commands.begin() ; it != _commands.end(); ++it)
         {
-//                            pc.printf("Testing <<%s>> with <<%s>>\r\n", (*it)->_pCmdStr, cmdStr);
+            RD_DBG("Testing <<%s>> with <<%s>>\r\n", (*it)->_pCmdStr, cmdStr);
             if (strcasecmp((*it)->_pCmdStr, cmdStr) == 0)
             {                                    
-//                pc.printf("FoundCmd <%s> Type %d\n\r", cmdStr, (*it)->_cmdType);
+                RD_DBG("FoundCmd <%s> Type %d\n\r", cmdStr, (*it)->_cmdType);
                 cmdFound = true;
                 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
                 {
@@ -113,15 +113,15 @@
 
     while (isListening())
     {
-        pc.printf("Waiting for TCP connection\r\n");
+        RD_INFO("Waiting for TCP connection\r\n");
         if(_socketSrv.accept(client)<0) 
         {
-            pc.printf("TCP Socket failed to accept connection\n\r");
+            RD_WARN("TCP Socket failed to accept connection\n\r");
         }
         else
         {
             client.set_blocking(false, 2000);
-            pc.printf("Connection from IP: %s\n\r",client.get_address());
+            RD_INFO("Connection from IP: %s\n\r",client.get_address());
             if (_pStatusLed != NULL)
                 *_pStatusLed = true;
             
@@ -132,7 +132,7 @@
                 // Check connection timer - 10 seconds timeout on HTTP operation
                 if (connectLimitTimer.read() >= 10)
                 {
-                    pc.printf("Connection timed out\n\r");
+                    RD_WARN("Connection timed out\n\r");
                     break;
                 }
                 // Get received data
@@ -143,7 +143,7 @@
                 }
                 else if (rxLen == 0)
                 {
-                    pc.printf("received buffer is empty.\n\r");
+                    RD_WARN("received buffer is empty.\n\r");
                     continue;                
                 }
                 else if (rxLen > HTTPD_MAX_REQ_LENGTH)
@@ -163,7 +163,7 @@
             }
             
             // Connection now closed
-            printf("Connection closed ...\r\n");
+            RD_INFO("Connection closed ...\r\n");
             client.close();
             if (_pStatusLed != NULL)
                 *_pStatusLed = false;
@@ -182,46 +182,48 @@
     const int HTTPD_MAX_FNAME_LENGTH = 127;
     char filename[HTTPD_MAX_FNAME_LENGTH+1];
     
-    pc.printf("Requesting file %s\n\r", inFileName);
+    RD_INFO("Requesting file %s\n\r", inFileName);
     
     if ((strlen(inFileName) > 0) && (inFileName[strlen(inFileName)-1] == '/'))
     {
-        pc.printf("Request directory %s%s\r\n", _pBaseWebFolder, inFileName);
+        RD_INFO("Request directory %s%s\r\n", _pBaseWebFolder, inFileName);
         sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
         DIR *d = opendir(filename);
-        if (d != NULL) {
+        if (d != NULL) 
+        {
             sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
             client.send(httpHeader,strlen(httpHeader));
             sprintf(httpHeader,"<html><head><title>Directory Listing</title></head><body><h1>%s</h1><ul>", inFileName);
             client.send(httpHeader,strlen(httpHeader));
             struct dirent *p;
-            while((p = readdir(d)) != NULL) {
-                pc.printf("%s\r\n", p->d_name);
+            while((p = readdir(d)) != NULL) 
+            {
+                RD_INFO("%s\r\n", p->d_name);
                 sprintf(httpHeader,"<li>%s</li>", p->d_name);
                 client.send(httpHeader,strlen(httpHeader));
             }
         }
         closedir(d);
-        // pc.printf("Directory closed\n");
+        RD_DBG("Directory closed\n");
         sprintf(httpHeader,"</ul></body></html>");
         client.send(httpHeader,strlen(httpHeader));
     }
     else
     {
         sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
-        pc.printf ("Filename %s\r\n", filename);
+        RD_INFO("Filename %s\r\n", filename);
             
         FILE* fp = fopen(filename, "r");
         if (fp == NULL)
         {
-            pc.printf ("Filename %s not found\r\n", filename);
+            RD_WARN("Filename %s not found\r\n", filename);
             sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
             client.send(httpHeader,strlen(httpHeader));
             client.send(inFileName,strlen(inFileName));
         }
         else
         {
-            pc.printf ("Sending file %s\r\n", filename);
+            RD_INFO("Sending file %s\r\n", filename);
             sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
             client.send(httpHeader,strlen(httpHeader));
             int rdCnt = 0;
@@ -238,7 +240,7 @@
 
 void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &client, char* httpHeader)
 {
-    pc.printf ("Sending file %s from cache %d bytes\r\n", pCacheEntry->_fileName, pCacheEntry->_nFileLen);
+    RD_INFO("Sending file %s from cache %d bytes\r\n", pCacheEntry->_fileName, pCacheEntry->_nFileLen);
     sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
     client.send(httpHeader,strlen(httpHeader));
     char* pMem = pCacheEntry->_pFileContent;
@@ -260,7 +262,7 @@
     char localFilename[HTTPD_MAX_FNAME_LENGTH+1];
     char reqFileNameStr[HTTPD_MAX_FNAME_LENGTH+1];
 
-    pc.printf("Requesting local file %s\n\r", inFileName);
+    RD_INFO("Requesting local file %s\n\r", inFileName);
     sprintf(reqFileNameStr, "/%s", inFileName);
     sprintf(localFilename, "/local/%s", inFileName);
         
@@ -304,14 +306,14 @@
     FILE* fp = fopen(localFilename, "r");
     if (fp == NULL)
     {
-        pc.printf ("Local file %s not found\r\n", localFilename);
+        RD_WARN("Local file %s not found\r\n", localFilename);
         sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
         client.send(httpHeader,strlen(httpHeader));
         client.send(reqFileNameStr,strlen(reqFileNameStr));
     }
     else
     {
-        pc.printf ("Sending file %s from disk\r\n", localFilename);
+        RD_INFO("Sending file %s from disk\r\n", localFilename);
         sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
         client.send(httpHeader,strlen(httpHeader));
         int rdCnt = 0;
@@ -327,33 +329,33 @@
 
 bool RdFileCacheEntry::readLocalFileIntoCache(char* fileName)
 {
-    pc.printf("Reading into cache %s\n\r", fileName);
+    RD_INFO("Reading into cache %s\n\r", fileName);
     LocalFileSystem local("local");
     FILE* fp = fopen(fileName, "r");
     if (fp == NULL)
     {
-        pc.printf("Failed to open file\n\r");
+        RD_WARN("Failed to open file\n\r");
         return false;
     }
-    pc.printf("Seeking\n\r");
+    RD_DBG("Seeking\n\r");
     fseek(fp, 0, SEEK_END);
     _nFileLen = (int)ftell(fp);
     _pFileContent = new char[_nFileLen];
-    pc.printf("Len %d Buf %08x\n\r", _nFileLen, _pFileContent);
+    RD_DBG("Len %d Buf %08x\n\r", _nFileLen, _pFileContent);
     if (!_pFileContent)
     {
-        pc.printf("Failed to allocate %lu\n\r", _nFileLen);
+        RD_WARN("Failed to allocate %lu\n\r", _nFileLen);
         fclose(fp);
         return false;
     }
-    pc.printf("Allocated\n\r");
+    RD_DBG("Allocated\n\r");
     memset(_pFileContent, 0, _nFileLen);
     fseek(fp, 0, SEEK_SET);
     char* pMem = _pFileContent;
     char fileBuf[100];
     int totCnt = 0;
     int rdCnt = 0;
-    pc.printf("Reading\n\r");
+    RD_DBG("Reading\n\r");
     while (true)
     {
         int toRead = _nFileLen - totCnt;
@@ -364,14 +366,14 @@
         rdCnt = fread(fileBuf, sizeof(char), toRead, fp);
         if (rdCnt <= 0)
             break;
-        pc.printf("Read %d tot %d of %d\n\r", rdCnt, totCnt, _nFileLen);
+        RD_DBG("Read %d tot %d of %d\n\r", rdCnt, totCnt, _nFileLen);
         memcpy(pMem, fileBuf, rdCnt);
         pMem += rdCnt;
         totCnt += rdCnt;
     }
-    pc.printf("Done read\n\r");
+    RD_DBG("Done read\n\r");
     fclose(fp);
-    pc.printf("Success in caching %d bytes (read %d)\n\r", _nFileLen, totCnt);
+    RD_DBG("Success in caching %d bytes (read %d)\n\r", _nFileLen, totCnt);
     _bCacheValid = true;
     return true;
 }