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:
Sat Feb 07 10:34:09 2015 +0000
Parent:
6:46285c519af2
Child:
8:de915bd70ec1
Commit message:
Working with gas count - not tested on actual meter reed switch

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	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)
     {
--- a/RdWebServer.h	Fri Feb 06 14:17:47 2015 +0000
+++ b/RdWebServer.h	Sat Feb 07 10:34:09 2015 +0000
@@ -35,7 +35,7 @@
         int _nFileLen;
 };
 
-typedef char* (*CmdCallbackType)(char*cmdStr, char* argStr);
+typedef char* (*CmdCallbackType)(int method, char*cmdStr, char* argStr);
 
 class RdWebServerCmdDef
 {
@@ -69,7 +69,9 @@
 class RdWebServer
 {
     public :
-    
+        static const int METHOD_OTHER = 0;
+        static const int METHOD_GET = 1;
+        static const int METHOD_POST = 2;
         RdWebServer();
         virtual ~RdWebServer();