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.

Committer:
Bobty
Date:
Mon Aug 31 15:00:41 2015 +0000
Revision:
24:27800de38eab
Parent:
23:0fc3d7b5e596
Child:
26:3c4c10e989b1
Implemented handling of split-payloads in HTTP requests. This is done by sending a registered command each chunk of payload data as it arrives with an index indicating where the data fits in the original message.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Bobty 20:e6c7db867593 1 // RdWebServer - Simple Web Server for MBED
Bobty 20:e6c7db867593 2 // Copyright (C) Rob Dobson 2013-2015, MIT License
Bobty 20:e6c7db867593 3 // Inspired by Jasper Schuurmans multi-threaded web server for Netduino which now seems to have gone from ...
Bobty 20:e6c7db867593 4 // http://www.schuurmans.cc/multi-threaded-web-server-for-netduino-plus
Bobty 20:e6c7db867593 5 // More details at http://robdobson.com/2013/10/moving-my-window-shades-control-to-mbed/
Bobty 20:e6c7db867593 6
Bobty 0:b5b4d07f7827 7 #ifndef RD_WEB_SERVER
Bobty 0:b5b4d07f7827 8 #define RD_WEB_SERVER
Bobty 0:b5b4d07f7827 9
Bobty 1:75bb184de749 10 #include <vector>
Bobty 0:b5b4d07f7827 11
Bobty 0:b5b4d07f7827 12 #include "mbed.h"
Bobty 0:b5b4d07f7827 13 #include "EthernetInterface.h"
Bobty 0:b5b4d07f7827 14
Bobty 18:5de680c4cfcb 15 // Debug level
Bobty 12:c14ffd4ec125 16 #ifdef RDWEB_DEBUG
Bobty 12:c14ffd4ec125 17 #if (RDWEB_DEBUG > 3)
Bobty 11:cec51b430b20 18 #define RD_DBG(x, ...) std::printf("[RD_DBG: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
Bobty 11:cec51b430b20 19 #else
Bobty 11:cec51b430b20 20 #define RD_DBG(x, ...)
Bobty 11:cec51b430b20 21 #endif
Bobty 12:c14ffd4ec125 22 #else
Bobty 12:c14ffd4ec125 23 #define RD_DBG(x, ...)
Bobty 12:c14ffd4ec125 24 #endif
Bobty 12:c14ffd4ec125 25
Bobty 12:c14ffd4ec125 26 #ifdef RDWEB_DEBUG
Bobty 12:c14ffd4ec125 27 #if (RDWEB_DEBUG > 2)
Bobty 11:cec51b430b20 28 #define RD_INFO(x, ...) std::printf("[RD_INFO: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
Bobty 11:cec51b430b20 29 #else
Bobty 11:cec51b430b20 30 #define RD_INFO(x, ...)
Bobty 11:cec51b430b20 31 #endif
Bobty 12:c14ffd4ec125 32 #else
Bobty 12:c14ffd4ec125 33 #define RD_INFO(x, ...)
Bobty 12:c14ffd4ec125 34 #endif
Bobty 12:c14ffd4ec125 35
Bobty 12:c14ffd4ec125 36 #ifdef RDWEB_DEBUG
Bobty 12:c14ffd4ec125 37 #if (RDWEB_DEBUG > 1)
Bobty 11:cec51b430b20 38 #define RD_WARN(x, ...) std::printf("[RD_WARNING: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
Bobty 11:cec51b430b20 39 #else
Bobty 11:cec51b430b20 40 #define RD_WARN(x, ...)
Bobty 11:cec51b430b20 41 #endif
Bobty 12:c14ffd4ec125 42 #else
Bobty 12:c14ffd4ec125 43 #define RD_WARN(x, ...)
Bobty 12:c14ffd4ec125 44 #endif
Bobty 12:c14ffd4ec125 45
Bobty 12:c14ffd4ec125 46 #ifdef RDWEB_DEBUG
Bobty 12:c14ffd4ec125 47 #if (RDWEB_DEBUG > 0)
Bobty 11:cec51b430b20 48 #define RD_ERR(x, ...) std::printf("[RD_ERR: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
Bobty 11:cec51b430b20 49 #else
Bobty 11:cec51b430b20 50 #define RD_ERR(x, ...)
Bobty 11:cec51b430b20 51 #endif
Bobty 12:c14ffd4ec125 52 #else
Bobty 12:c14ffd4ec125 53 #define RD_ERR(x, ...)
Bobty 12:c14ffd4ec125 54 #endif
Bobty 12:c14ffd4ec125 55
Bobty 24:27800de38eab 56 // Length of strings
Bobty 24:27800de38eab 57 const int MAX_CMDSTR_LEN = 100;
Bobty 24:27800de38eab 58 const int MAX_ARGSTR_LEN = 100;
Bobty 17:080f2bed8b36 59 const int SUBST_MAX_FNAME_LEN = 40; // note local files on MBED are 8.3 but this might include other files
Bobty 11:cec51b430b20 60
Bobty 6:46285c519af2 61 extern RawSerial pc;
Bobty 6:46285c519af2 62
Bobty 2:9d8793c23b46 63 class RdFileCacheEntry
Bobty 2:9d8793c23b46 64 {
Bobty 2:9d8793c23b46 65 public:
Bobty 2:9d8793c23b46 66 RdFileCacheEntry(char* pFileName)
Bobty 2:9d8793c23b46 67 {
Bobty 2:9d8793c23b46 68 _bCacheValid = false;
Bobty 17:080f2bed8b36 69 strncpy(_fileName, pFileName, SUBST_MAX_FNAME_LEN-1);
Bobty 17:080f2bed8b36 70 _fileName[SUBST_MAX_FNAME_LEN-1] = '\0';
Bobty 2:9d8793c23b46 71 _pFileContent = NULL;
Bobty 2:9d8793c23b46 72 }
Bobty 2:9d8793c23b46 73 ~RdFileCacheEntry()
Bobty 2:9d8793c23b46 74 {
Bobty 2:9d8793c23b46 75 delete _pFileContent;
Bobty 2:9d8793c23b46 76 }
Bobty 2:9d8793c23b46 77 bool readLocalFileIntoCache(char* fileName);
Bobty 2:9d8793c23b46 78 public:
Bobty 2:9d8793c23b46 79 bool _bCacheValid;
Bobty 17:080f2bed8b36 80 char _fileName[SUBST_MAX_FNAME_LEN];
Bobty 2:9d8793c23b46 81 char* _pFileContent;
Bobty 2:9d8793c23b46 82 int _nFileLen;
Bobty 2:9d8793c23b46 83 };
Bobty 2:9d8793c23b46 84
Bobty 24:27800de38eab 85 typedef char* (*CmdCallbackType)(int method, char*cmdStr, char* argStr, char* msgBuffer, int msgLen,
Bobty 24:27800de38eab 86 int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos);
Bobty 1:75bb184de749 87
Bobty 1:75bb184de749 88 class RdWebServerCmdDef
Bobty 1:75bb184de749 89 {
Bobty 1:75bb184de749 90 public:
Bobty 2:9d8793c23b46 91 static const int CMD_LOCALFILE = 1;
Bobty 2:9d8793c23b46 92 static const int CMD_CALLBACK = 2;
Bobty 4:6afb3bbf20a4 93 static const int CMD_SDORUSBFILE = 3;
Bobty 17:080f2bed8b36 94 RdWebServerCmdDef(char* pStr, int cmdType, CmdCallbackType callback, char* substFileName = NULL, bool bCacheIfPossible = false)
Bobty 1:75bb184de749 95 {
Bobty 1:75bb184de749 96 _pCmdStr = pStr;
Bobty 2:9d8793c23b46 97 _cmdType = cmdType;
Bobty 1:75bb184de749 98 _callback = callback;
Bobty 17:080f2bed8b36 99 _substFileName[0] = '\0';
Bobty 17:080f2bed8b36 100 if (substFileName != NULL)
Bobty 2:9d8793c23b46 101 {
Bobty 17:080f2bed8b36 102 strncpy(_substFileName, substFileName, SUBST_MAX_FNAME_LEN);
Bobty 17:080f2bed8b36 103 _substFileName[SUBST_MAX_FNAME_LEN-1] = '\0';
Bobty 2:9d8793c23b46 104 }
Bobty 2:9d8793c23b46 105 _bCacheIfPossible = bCacheIfPossible;
Bobty 1:75bb184de749 106 };
Bobty 1:75bb184de749 107 char* _pCmdStr;
Bobty 2:9d8793c23b46 108 int _cmdType;
Bobty 1:75bb184de749 109 CmdCallbackType _callback;
Bobty 17:080f2bed8b36 110 char _substFileName[SUBST_MAX_FNAME_LEN];
Bobty 2:9d8793c23b46 111 bool _bCacheIfPossible;
Bobty 1:75bb184de749 112 };
Bobty 1:75bb184de749 113
Bobty 6:46285c519af2 114 const int HTTPD_MAX_HDR_LENGTH = 255;
Bobty 23:0fc3d7b5e596 115 // The FRDM-K64F has more memory than the LPC1768 so allow a larger buffer
Bobty 19:f67ac231b570 116 #ifdef MCU_MK64F12
Bobty 19:f67ac231b570 117 const int HTTPD_MAX_REQ_LENGTH = 2048;
Bobty 19:f67ac231b570 118 #warning("TCP Request Length 2048")
Bobty 19:f67ac231b570 119 #else
Bobty 19:f67ac231b570 120 const int HTTPD_MAX_REQ_LENGTH = 1024;
Bobty 19:f67ac231b570 121 #warning("TCP Request Length 1024")
Bobty 19:f67ac231b570 122 #endif
Bobty 6:46285c519af2 123
Bobty 0:b5b4d07f7827 124 class RdWebServer
Bobty 0:b5b4d07f7827 125 {
Bobty 0:b5b4d07f7827 126 public :
Bobty 7:fe7c33f7fbb8 127 static const int METHOD_OTHER = 0;
Bobty 7:fe7c33f7fbb8 128 static const int METHOD_GET = 1;
Bobty 7:fe7c33f7fbb8 129 static const int METHOD_POST = 2;
Bobty 23:0fc3d7b5e596 130 static const int METHOD_OPTIONS = 3;
Bobty 0:b5b4d07f7827 131 RdWebServer();
Bobty 0:b5b4d07f7827 132 virtual ~RdWebServer();
Bobty 0:b5b4d07f7827 133
Bobty 4:6afb3bbf20a4 134 bool init(int port, DigitalOut* pStatusLed, char* pBaseWebFolder);
Bobty 0:b5b4d07f7827 135 void run();
Bobty 2:9d8793c23b46 136 void addCommand(char* pCmdStr, int cmdType, CmdCallbackType callback = NULL, char* substFileName = NULL, bool cacheIfPossible = false);
Bobty 1:75bb184de749 137
Bobty 24:27800de38eab 138 static unsigned char* getPayloadDataFromMsg(char* msgBuf, int msgLen, int& payloadLen);
Bobty 24:27800de38eab 139 static int getContentLengthFromMsg(char* msgBuf);
Bobty 19:f67ac231b570 140
Bobty 0:b5b4d07f7827 141 private :
Bobty 0:b5b4d07f7827 142 int _port;
Bobty 0:b5b4d07f7827 143 DigitalOut* _pStatusLed;
Bobty 15:0865fa4b046a 144 TCPSocketServer _serverSocket;
Bobty 15:0865fa4b046a 145 bool _initOk;
Bobty 1:75bb184de749 146 std::vector<RdWebServerCmdDef*> _commands;
Bobty 2:9d8793c23b46 147 std::vector<RdFileCacheEntry*> _cachedFiles;
Bobty 24:27800de38eab 148 bool extractCmdArgs(char* buf, char* pCmdStr, int maxCmdStrLen, char* pArgStr, int maxArgStrLen, int& contentLen);
Bobty 4:6afb3bbf20a4 149 char* _pBaseWebFolder;
Bobty 6:46285c519af2 150 char _httpHeader[HTTPD_MAX_HDR_LENGTH+1];
Bobty 6:46285c519af2 151 char _buffer[HTTPD_MAX_REQ_LENGTH+1];
Bobty 24:27800de38eab 152 int _bufferReceivedLen;
Bobty 1:75bb184de749 153
Bobty 17:080f2bed8b36 154 bool handleLocalFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client, bool bCacheIfPossible);
Bobty 17:080f2bed8b36 155 bool handleSDFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client);
Bobty 2:9d8793c23b46 156 void handleCGIRequest(char* pUriStr, char* pQueryStr);
Bobty 17:080f2bed8b36 157 void sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &client);
Bobty 14:4b83670854f0 158 bool handleReceivedHttp(TCPSocketConnection &client);
Bobty 17:080f2bed8b36 159 void formHTTPHeader(const char* rsltCode, const char* contentType, int contentLen);
Bobty 17:080f2bed8b36 160
Bobty 17:080f2bed8b36 161 // Settings - see the constructor
Bobty 17:080f2bed8b36 162 bool _blockingOnAccept;
Bobty 17:080f2bed8b36 163 bool _blockingOnReceive;
Bobty 17:080f2bed8b36 164 int _timeoutOnBlocking;
Bobty 17:080f2bed8b36 165 bool _closeConnAfterSend;
Bobty 17:080f2bed8b36 166 bool _closeConnOnReceiveFail;
Bobty 24:27800de38eab 167
Bobty 24:27800de38eab 168 // Handling of split payloads on receipt (e.g. POST)
Bobty 24:27800de38eab 169 int _curSplitPayloadPos;
Bobty 24:27800de38eab 170 char _curCmdStr[MAX_CMDSTR_LEN];
Bobty 24:27800de38eab 171 char _curArgStr[MAX_ARGSTR_LEN];
Bobty 24:27800de38eab 172 int _curContentLen;
Bobty 24:27800de38eab 173 int _curHttpMethod;
Bobty 24:27800de38eab 174 RdWebServerCmdDef* _curWebServerCmdDef;
Bobty 2:9d8793c23b46 175
Bobty 0:b5b4d07f7827 176 };
Bobty 0:b5b4d07f7827 177
Bobty 0:b5b4d07f7827 178 #endif