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.

Files at this revision

API Documentation at this revision

Comitter:
Bobty
Date:
Thu Sep 19 12:06:18 2013 +0000
Parent:
0:b5b4d07f7827
Child:
2:9d8793c23b46
Commit message:
Added code to support commands - still a lot of test code in place though

Changed in this revision

RdWebServer.cpp Show annotated file Show diff for this revision Revisions of this file
RdWebServer.h Show annotated file Show diff for this revision Revisions of this file
--- a/RdWebServer.cpp	Wed Sep 18 21:36:50 2013 +0000
+++ b/RdWebServer.cpp	Thu Sep 19 12:06:18 2013 +0000
@@ -1,5 +1,8 @@
 #include "RdWebServer.h"
 
+#define MAX_CMDSTR_LEN 100
+#define MAX_ARGSTR_LEN 100
+
 RdWebServer::RdWebServer()
 {
     _serverIsListening = false;
@@ -43,6 +46,43 @@
     return true;
 }
 
+bool RdWebServer::extractCmdArgs(char* buf, char* pCmdStr, int maxCmdStrLen, char* pArgStr, int maxArgStrLen)
+{
+    *pCmdStr = '\0';
+    *pArgStr = '\0';
+    int cmdStrLen = 0;
+    int argStrLen = 0;
+    if (buf == NULL)
+        return false;
+    char* pSlash1 = strchr(buf, '/');
+    if (pSlash1 == NULL)
+        return false;
+    pSlash1++;
+    while(*pSlash1)
+    {
+        if (cmdStrLen >= maxCmdStrLen-1)
+            break;
+        if ((*pSlash1 == '/') || (*pSlash1 == ' ') || (*pSlash1 == '\n'))
+            break;
+        *pCmdStr++ = *pSlash1++;
+        *pCmdStr = '\0';
+        cmdStrLen++;
+    }
+    if ((*pSlash1 == '\0') || (*pSlash1 == ' ') || (*pSlash1 == '\n'))
+        return true;
+    *pSlash1++;
+    while(*pSlash1)
+    {
+        if (argStrLen >= maxArgStrLen-1)
+            break;
+        if ((*pSlash1 == ' ') || (*pSlash1 == '\n'))
+            break;
+        *pArgStr++ = *pSlash1++;
+        *pArgStr = '\0';
+        argStrLen++;
+    }
+    return true;
+}
   
 void RdWebServer::run()
 {
@@ -82,15 +122,29 @@
                         if(buffer[0] == 'G' && buffer[1] == 'E' && buffer[2] == 'T' ) 
                         {
                             printf("GET request incomming.\n\r");
-                            
+                            char cmdStr[MAX_CMDSTR_LEN];
+                            char argStr[MAX_ARGSTR_LEN];
+                            if (extractCmdArgs(buffer+3, cmdStr, MAX_CMDSTR_LEN, argStr, MAX_ARGSTR_LEN))
+                            {
+                                printf("CmdStr %s\n\r", cmdStr);
+                                printf("ArgStr %s\n\r", argStr);
+                                for (std::vector<RdWebServerCmdDef*>::iterator it = _commands.begin() ; it != _commands.end(); ++it)
+                                {
+                                    if (strcasecmp((*it)->_pCmdStr, cmdStr) == 0)
+                                    {
+                                        printf("FoundCmd %s\n\r", cmdStr);
+                                        ((*it)->_callback)(argStr);
+                                    }
+                                }
+                            }
                             
                             //setup http response header & data
                             char echoHeader[256] = {};
                             sprintf(echoHeader,"HTTP/1.1 200 OK\n\rContent-Length: %d\n\rContent-Type: text\n\rConnection: Close\n\r\n\r",strlen(buffer));
                             client.send(echoHeader,strlen(echoHeader));
-                            client.send(buffer,strlen(buffer));
+                            client.send(cmdStr,strlen(cmdStr));
                             clientIsConnected = false;
-                            printf("echo back done.\n\r");
+                            printf("done.\n\r");
                         }
                         break;
                 }
@@ -103,3 +157,8 @@
     }
 }
 
+void RdWebServer::addCommand(char* pCmdStr, CmdCallbackType callback)
+{
+    _commands.push_back(new RdWebServerCmdDef(pCmdStr, callback));
+}
+
--- a/RdWebServer.h	Wed Sep 18 21:36:50 2013 +0000
+++ b/RdWebServer.h	Thu Sep 19 12:06:18 2013 +0000
@@ -1,11 +1,25 @@
 #ifndef RD_WEB_SERVER
 #define RD_WEB_SERVER
 
-#include <map>
+#include <vector>
 
 #include "mbed.h"
 #include "EthernetInterface.h"
 
+typedef void (*CmdCallbackType)(char* argStr);
+
+class RdWebServerCmdDef
+{
+    public:
+        RdWebServerCmdDef(char* pStr, CmdCallbackType callback)
+        {
+            _pCmdStr = pStr;
+            _callback = callback;
+        };
+        char* _pCmdStr;
+        CmdCallbackType _callback;
+};
+
 class RdWebServer
 {
     public :
@@ -20,11 +34,17 @@
             return _serverIsListening;
         };
         
+        void addCommand(char* pCmdStr, CmdCallbackType callback);
+        
     private :
         int _port;
         DigitalOut* _pStatusLed;
         TCPSocketServer _socketSrv;
         bool _serverIsListening;
+        std::vector<RdWebServerCmdDef*> _commands;
+        bool extractCmdArgs(char* buf, char* pCmdStr, int maxCmdStrLen, char* pArgStr, int maxArgStrLen);
+
+        
 };
 
 #endif