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:
7:fe7c33f7fbb8
Parent:
6:46285c519af2
Child:
8:de915bd70ec1
--- a/RdWebServer.cpp	Fri Feb 06 14:17:47 2015 +0000
+++ b/RdWebServer.cpp	Sat Feb 07 10:34:09 2015 +0000
@@ -57,50 +57,52 @@
 
 void RdWebServer::handleReceivedHttp(TCPSocketConnection &client)
 {
-    pc.printf("Received Data: %d\n\r\n\r%.*s\n\r",strlen(_buffer),strlen(_buffer),_buffer);
+//    pc.printf("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;
+    if (strncmp(_buffer, "POST", 4) == 0)
+        method = METHOD_POST;
+
+    char cmdStr[MAX_CMDSTR_LEN];
+    char argStr[MAX_ARGSTR_LEN];
+    if (extractCmdArgs(_buffer+3, cmdStr, MAX_CMDSTR_LEN, argStr, MAX_ARGSTR_LEN))
     {
-        pc.printf("GET request\n\r");
-        char cmdStr[MAX_CMDSTR_LEN];
-        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);
+        bool cmdFound = false;
+        for (std::vector<RdWebServerCmdDef*>::iterator it = _commands.begin() ; it != _commands.end(); ++it)
         {
-            pc.printf("CmdStr %s\n\r", cmdStr);
-            pc.printf("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);
-                if (strcasecmp((*it)->_pCmdStr, cmdStr) == 0)
-                {                                    
-                    pc.printf("FoundCmd <%s> Type %d\n\r", cmdStr, (*it)->_cmdType);
-                    cmdFound = true;
-                    if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
-                    {
-                        char* respStr = ((*it)->_callback)(cmdStr, argStr);
-                        client.send(respStr, strlen(respStr));
-                    }
-                    else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
-                    {
-                        if ((*it)->_substFileName[0] != '\0')
-                            handleLocalFileRequest((*it)->_substFileName, argStr, client, _httpHeader, (*it)->_bCacheIfPossible);
-                        else
-                            handleLocalFileRequest(cmdStr, argStr, client, _httpHeader, (*it)->_bCacheIfPossible);
-                    }
-                    else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE)
-                    {
-                        if ((*it)->_substFileName[0] != '\0')
-                            handleSDFileRequest((*it)->_substFileName, argStr, client, _httpHeader);
-                        else
-                            handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
-                    }
-                    break;
+//                            pc.printf("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);
+                cmdFound = true;
+                if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
+                {
+                    char* respStr = ((*it)->_callback)(method, cmdStr, argStr);
+                    client.send(respStr, strlen(respStr));
                 }
+                else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
+                {
+                    if ((*it)->_substFileName[0] != '\0')
+                        handleLocalFileRequest((*it)->_substFileName, argStr, client, _httpHeader, (*it)->_bCacheIfPossible);
+                    else
+                        handleLocalFileRequest(cmdStr, argStr, client, _httpHeader, (*it)->_bCacheIfPossible);
+                }
+                else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE)
+                {
+                    if ((*it)->_substFileName[0] != '\0')
+                        handleSDFileRequest((*it)->_substFileName, argStr, client, _httpHeader);
+                    else
+                        handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
+                }
+                break;
             }
-            // If command not found see if it is a local file
-            if (!cmdFound)
-                handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
         }
+        // If command not found see if it is a local file
+        if (!cmdFound)
+            handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
     }
 }
 
@@ -143,133 +145,8 @@
 
             // Handle buffer
             handleReceivedHttp(client);
-//            const char * msg = "Hello World\n\r\n\r";
-//            sprintf(_buffer,"HTTP/1.1 200 OK\n\rContent-Length: %d\n\rContent-Type: text/html\n\rConnection: Close\n\r\n\r", strlen(msg));
-//            client.send(_buffer,strlen(_buffer));
-//            client.send( const_cast<char*>(msg), strlen(msg));
         }
     }
-    
-//    while (1) 
-//    {
-//        TCPSocketConnection Clnt;
-//        printf("Accept\n");
-//        int ret = _socketSrv.accept(Clnt);
-//        if ( ret < 0) {
-//            printf("no connection\n");            
-//        } else {
-//            printf("Client (IP=%s) is connected\n", Clnt.get_address());
-//            char buff[512];            
-//            int result = Clnt.receive(buff, 511);
-//            if(result < 0 )
-//                printf("The horror\n ");
-//            else {
-//               if(result >= 511)
-//                 buff[511] = 0;
-//               else
-//                 buff[result + 1] = 0;
-//                 
-//               printf("%s\n",buff);
-//            }
-//            const char * msg = "Hello World\n\r\n\r";
-//            sprintf(buff,"HTTP/1.1 200 OK\n\rContent-Length: %d\n\rContent-Type: text/html\n\rConnection: Close\n\r\n\r", strlen(msg));
-//            Clnt.send(buff,strlen(buff));
-//            Clnt.send( const_cast<char*>(msg), strlen(msg));
-//        }        
-//    }
-//
-//    //listening for http GET request
-//    while (isListening())
-//    {
-//        //blocking mode(never timeout)
-//        if(_socketSrv.accept(client)<0) 
-//        {
-//            pc.printf("TCP Socket failed to accept connection\n\r");
-//        }
-//        else
-//        {
-//            client.set_blocking(false, 1000);
-//            pc.printf("Connection from IP: %s\n\r",client.get_address());
-//            if (_pStatusLed != NULL)
-//                *_pStatusLed = true;
-//            Timer connectLimitTimer;
-//            connectLimitTimer.start();
-//            
-//            while(connectLimitTimer.read() < 5)  // 5 seconds timeout on HTTP operation
-//            {
-//                int rxLen = client.receive(buffer, 1023);
-//                if (rxLen == -1)
-//                {
-//                    continue;
-//                }
-//                else if (rxLen == 0)
-//                {
-//                    pc.printf("received buffer is empty.\n\r");
-//                    break;                
-//                }
-//                else if (rxLen >= 1024)
-//                {
-//                    sprintf(httpHeader,"HTTP/1.1 413 Request Entity Too Large \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
-//                    client.send(httpHeader,strlen(httpHeader));
-//                    client.send(buffer, rxLen);
-//                    break;
-//                }
-//                buffer[rxLen] = '\0';
-////                pc.printf("Received Data: %d\n\r\n\r%.*s\n\r",strlen(buffer),strlen(buffer),buffer);
-//                if (strncmp(buffer, "GET ", 4) == 0)
-//                {
-////                    pc.printf("GET request\n\r");
-//                    char cmdStr[MAX_CMDSTR_LEN];
-//                    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);
-//                        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);
-//                            if (strcasecmp((*it)->_pCmdStr, cmdStr) == 0)
-//                            {                                    
-//                                pc.printf("FoundCmd <%s> Type %d\n\r", cmdStr, (*it)->_cmdType);
-//                                cmdFound = true;
-//                                if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
-//                                {
-//                                    char* respStr = ((*it)->_callback)(cmdStr, argStr);
-//                                    client.send(respStr, strlen(respStr));
-//                                }
-//                                else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
-//                                {
-//                                    if ((*it)->_substFileName[0] != '\0')
-//                                        handleLocalFileRequest((*it)->_substFileName, argStr, &client, httpHeader, (*it)->_bCacheIfPossible);
-//                                    else
-//                                        handleLocalFileRequest(cmdStr, argStr, &client, httpHeader, (*it)->_bCacheIfPossible);
-//                                }
-//                                else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE)
-//                                {
-//                                    if ((*it)->_substFileName[0] != '\0')
-//                                        handleSDFileRequest((*it)->_substFileName, argStr, &client, httpHeader);
-//                                    else
-//                                        handleSDFileRequest(cmdStr, argStr, &client, httpHeader);
-//                                }
-//                                break;
-//                            }
-//                        }
-//                        // If command not found see if it is a local file
-//                        if (!cmdFound)
-//                            handleLocalFileRequest(cmdStr, argStr, &client, httpHeader, false);
-//                    }
-//
-//                    break;
-//                }
-//                
-//            }
-//            pc.printf("Connection closed ... TCP server is listening...\r\n");
-//            client.close();
-//            if (_pStatusLed != NULL)
-//                *_pStatusLed = false;
-//        }
-//    }
 }
 
 void RdWebServer::addCommand(char* pCmdStr, int cmdType, CmdCallbackType callback, char* substFileName, bool cacheIfPossible)
@@ -336,21 +213,6 @@
             fclose(fp);
         }
     }
-//    FILE* fp = fopen("/sd/index.htm", "r");
-//    if (fp == NULL)
-//    {
-//        pc.printf ("Filename %s not found\r\n", inFileName);
-//    }
-//    else
-//    {
-//        pc.printf ("Sending file %s\r\n", inFileName);
-//        fclose(fp);
-//    }
-    
-//    const char * msg = "Hello World\n\r\n\r";
-//    sprintf(_buffer,"HTTP/1.1 200 OK\n\rContent-Length: %d\n\rContent-Type: text/html\n\rConnection: Close\n\r\n\r", strlen(msg));
-//    client.send(_buffer,strlen(_buffer));
-//    client.send( const_cast<char*>(msg), strlen(msg));
 }
 
 void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &client, char* httpHeader)
@@ -501,15 +363,17 @@
     int argStrLen = 0;
     if (buf == NULL)
         return false;
+    // Check for first slash
     char* pSlash1 = strchr(buf, '/');
     if (pSlash1 == NULL)
         return false;
     pSlash1++;
+    // Extract command
     while(*pSlash1)
     {
         if (cmdStrLen >= maxCmdStrLen-1)
             break;
-        if ((*pSlash1 == '/') || (*pSlash1 == ' ') || (*pSlash1 == '\n'))
+        if ((*pSlash1 == '/') || (*pSlash1 == ' ') || (*pSlash1 == '\n') || (*pSlash1 == '?') || (*pSlash1 == '&'))
             break;
         *pCmdStr++ = *pSlash1++;
         *pCmdStr = '\0';
@@ -517,6 +381,7 @@
     }
     if ((*pSlash1 == '\0') || (*pSlash1 == ' ') || (*pSlash1 == '\n'))
         return true;
+    // Now args
     *pSlash1++;
     while(*pSlash1)
     {