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.
Diff: RdWebServer.cpp
- Revision:
- 18:5de680c4cfcb
- Parent:
- 17:080f2bed8b36
- Child:
- 19:f67ac231b570
--- a/RdWebServer.cpp Mon May 11 11:17:15 2015 +0000 +++ b/RdWebServer.cpp Mon May 11 14:26:54 2015 +0000 @@ -4,20 +4,31 @@ More details at http://robdobson.com/2013/10/moving-my-window-shades-control-to-mbed/ */ -//#define RDWEB_DEBUG 5 +// Setting RDWEB_DEBUG to 4 causes all debugging to be shown +#define RDWEB_DEBUG 1 + +// Change the settings below to support a local file system (not available on some MBEDs) +//#define SUPPORT_LOCAL_FILESYSTEM 1 +//#define SUPPORT_LOCAL_FILE_CACHE 1 + +// Change this to support display of files on the server +//#define SUPPORT_FOLDER_VIEW 1 #include "RdWebServer.h" +// Limits - note particularly the MAX_FILENAME_LEN const int MAX_CMDSTR_LEN = 100; const int MAX_ARGSTR_LEN = 100; const int MAX_FILENAME_LEN = (MAX_ARGSTR_LEN + 20); +// Constructor RdWebServer::RdWebServer() { _initOk = false; _pStatusLed = NULL; } +// Destructor RdWebServer::~RdWebServer() { // Clean-up - probably never called as we're on a microcontroller! @@ -25,6 +36,7 @@ delete (*it); } +// Init bool RdWebServer::init(int port, DigitalOut* pStatusLed, char* pBaseWebFolder) { // Settings @@ -66,6 +78,7 @@ return true; } +// Run - never returns so run in a thread void RdWebServer::run() { // Check initialised ok @@ -117,32 +130,22 @@ { RD_DBG("clientSocketConn.receive() returned %d - too long\r\n", rxLen); formHTTPHeader("413 Request Entity Too Large", "text/plain", 0); -// sprintf(_httpHeader,"HTTP/1.1 413 Request Entity Too Large \r\nContent-Type: text\r\nContent-Length: 0\r\n%s\r\n", closeConnAfterSend ? closeConnStr : ""); int sentRet = clientSocketConn.send(_httpHeader,strlen(_httpHeader)); continue; } - + + // Handle received message RD_DBG("Received len %d\r\n", rxLen); _buffer[rxLen] = '\0'; RD_DBG("%s\r\n", _buffer); - - // Send a response -// char* content = "HELLO\r\n"; -// int sz = strlen(content); -// sprintf(_httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n%s\r\n", sz, closeConnAfterSend ? closeConnStr : ""); -// int sentRet = clientSocketConn.send(_httpHeader,strlen(_httpHeader)); -// int sentRet2 = clientSocketConn.send(content, strlen(content)); -// RD_DBG("Sent %s header ret %d content ret %d now connected %d\r\n", content, sentRet, sentRet2, clientSocketConn.is_connected()); - - // Handle received message - if (handleReceivedHttp(clientSocketConn)) - { - // OK - } - else - { - // FAIL - } + if (handleReceivedHttp(clientSocketConn)) + { + // OK + } + else + { + // FAIL + } // Close connection if required if (_closeConnAfterSend) @@ -154,60 +157,8 @@ } } } - -// connectLimitTimer.reset(); -// connectLimitTimer.start(); -// while(true) -// { -// // Check connection timer - 10 seconds timeout on HTTP operation -// if (connectLimitTimer.read() >= 10) -// { -// RD_WARN("Connection timed out\n\r"); -// break; -// } -// // Get received data -// int rxLen = clientSocketConn.receive(_buffer, HTTPD_MAX_REQ_LENGTH); -// if (rxLen == -1) -// { -// RD_WARN("Nothing received\n\r"); -// continue; -// } -// else if (rxLen == 0) -// { -// RD_WARN("received buffer is empty.\n\r"); -// break; -// } -// else if (rxLen > HTTPD_MAX_REQ_LENGTH) -// { -// sprintf(_httpHeader,"HTTP/1.1 413 Request Entity Too Large \r\nContent-Type: text\r\nConnection: Close\r\n\r\n"); -// clientSocketConn.send(_httpHeader,strlen(_httpHeader)); -// clientSocketConn.send(_buffer, rxLen); -// break; -// } -// _buffer[rxLen] = '\0'; -// -// // Handle buffer -// if (handleReceivedHttp(clientSocketConn)) -// { -// break; -// } -// else -// { -// connectLimitTimer.reset(); -// connectLimitTimer.start(); -// } -// } -// -// // Connection now closed -// RD_INFO("Connection closed ...\r\n"); -// clientSocketConn.close(); -// if (_pStatusLed != NULL) -// *_pStatusLed = false; -// } -// -// } -//} -// + +// Handle a request bool RdWebServer::handleReceivedHttp(TCPSocketConnection &clientSocketConn) { bool handledOk = false; @@ -268,11 +219,13 @@ return handledOk; } +// Add a command to the server void RdWebServer::addCommand(char* pCmdStr, int cmdType, CmdCallbackType callback, char* substFileName, bool cacheIfPossible) { _commands.push_back(new RdWebServerCmdDef(pCmdStr, cmdType, callback, substFileName, cacheIfPossible)); } +// Form a header to respond void RdWebServer::formHTTPHeader(const char* rsltCode, const char* contentType, int contentLen) { const char* closeConnStr = "\r\nConnection: Close"; @@ -281,7 +234,8 @@ else sprintf(_httpHeader, "HTTP/1.1 %s\r\nContent-Type: %s%s\r\n\r\n", rsltCode, contentType, _closeConnAfterSend ? closeConnStr : ""); } - + +// Use file extension to determine MIME type char* getMimeTypeStr(char* filename) { char* mimeType = "text/plain"; @@ -298,7 +252,8 @@ mimeType = "image/jpeg"; return mimeType; } - + +// Handle request for files/listing from SDCard bool RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn) { bool handledOk = false; @@ -356,7 +311,7 @@ fseek(fp, 0L, SEEK_SET); // Get mime type char* mimeType = getMimeTypeStr(filename); - RD_INFO("MIME TYPE %s", mimeType); + RD_DBG("MIME TYPE %s", mimeType); // Form header formHTTPHeader("200 OK", mimeType, sz); clientSocketConn.send(_httpHeader,strlen(_httpHeader)); @@ -375,8 +330,8 @@ return handledOk; } +// Send a file from cache - used for local files only #ifdef SUPPORT_LOCAL_FILE_CACHE - void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &clientSocketConn) { RD_INFO("Sending file %s from cache %d bytes\r\n", pCacheEntry->_fileName, pCacheEntry->_nFileLen); @@ -400,6 +355,7 @@ } #endif +// Handle a request for a local file bool RdWebServer::handleLocalFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn, bool bCacheIfPossible) { @@ -467,7 +423,7 @@ fseek(fp, 0L, SEEK_SET); // Get mime type char* mimeType = getMimeTypeStr(localFilename); - RD_INFO("MIME TYPE %s", mimeType); + RD_DBG("MIME TYPE %s", mimeType); // Form header formHTTPHeader("200 OK", mimeType, sz); clientSocketConn.send(_httpHeader,strlen(_httpHeader)); @@ -492,8 +448,8 @@ #endif } +// Read file into cache #ifdef SUPPORT_LOCAL_FILE_CACHE - bool RdFileCacheEntry::readLocalFileIntoCache(char* fileName) { #ifdef SUPPORT_LOCAL_FILESYSTEM @@ -552,6 +508,7 @@ #endif +// Extract arguments from command bool RdWebServer::extractCmdArgs(char* buf, char* pCmdStr, int maxCmdStrLen, char* pArgStr, int maxArgStrLen) { *pCmdStr = '\0';