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:
Mon May 11 11:17:15 2015 +0000
Parent:
16:0248bbfdb6c1
Child:
18:5de680c4cfcb
Commit message:
Improved settings

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	Tue May 05 22:14:16 2015 +0000
+++ b/RdWebServer.cpp	Mon May 11 11:17:15 2015 +0000
@@ -4,14 +4,13 @@
    More details at http://robdobson.com/2013/10/moving-my-window-shades-control-to-mbed/
 */
 
-#define RDWEB_DEBUG 5
+//#define RDWEB_DEBUG 5
 
 #include "RdWebServer.h"
 
 const int MAX_CMDSTR_LEN = 100;
 const int MAX_ARGSTR_LEN = 100;
 const int MAX_FILENAME_LEN = (MAX_ARGSTR_LEN + 20);
-const int MAX_MIMETYPE_LEN = 50;
 
 RdWebServer::RdWebServer()
 {
@@ -33,6 +32,23 @@
     _pStatusLed = pStatusLed;
     _pBaseWebFolder = pBaseWebFolder;
 
+    // The sample web server on MBED site turns off blocking after the accept has happened
+    // I've tried this but it doesn't seem to yield a reliable server
+    _blockingOnAccept = true;
+    _blockingOnReceive = true;
+    
+    // This is the same as the default in the socket.cpp file
+    _timeoutOnBlocking = 1500;
+    
+    // Currently tested using close connection after send with a single file which works fine
+    // If closeConnAfterSend is set false then the socket connection remains open and only
+    // one client can access the server at a time
+    // Need to test with the closeConnAfterSend seetting true when trying to download multiple files
+    // for a website as previous experience would indicate that requests might be missed in this scenario
+    // although all other settings may not have been the same
+    _closeConnAfterSend = true;
+    _closeConnOnReceiveFail = true;
+
     // Setup tcp socket
     _serverSocket.set_blocking(true);
     if(_serverSocket.bind(port)< 0) 
@@ -56,31 +72,13 @@
     if (!_initOk)
         return;
     
-    // The sample web server on MBED site turns off blocking after the accept has happened
-    // I've tried this but it doesn't seem to yield a reliable server
-    bool blockingOnAccept = true;
-    bool blockingOnReceive = true;
-    
-    // This is the same as the default in the socket.cpp file
-    int timeoutOnBlocking = 1500;
-    
-    // Currently tested using close connection after send with a single file which works fine
-    // If closeConnAfterSend is set false then the socket connection remains open and only
-    // one client can access the server at a time
-    // Need to test with the closeConnAfterSend seetting true when trying to download multiple files
-    // for a website as previous experience would indicate that requests might be missed in this scenario
-    // although all other settings may not have been the same
-    bool closeConnAfterSend = true;
-    bool closeConnOnReceiveFail = true;
-    const char* closeConnStr = "Connection: Close\r\n";
-    
     // Start accepting connections
     while (true)
     {
         TCPSocketConnection clientSocketConn;
         // Accept connection if available
         RD_INFO("Waiting for TCP connection\r\n");
-        clientSocketConn.set_blocking(blockingOnAccept, timeoutOnBlocking);
+        clientSocketConn.set_blocking(_blockingOnAccept, _timeoutOnBlocking);
         if(_serverSocket.accept(clientSocketConn)<0) 
         {
             RD_WARN("TCP Socket failed to accept connection\n\r");
@@ -97,12 +95,12 @@
         while(clientSocketConn.is_connected() && !forcedClosed)
         {
             // Receive data
-            clientSocketConn.set_blocking(blockingOnReceive, timeoutOnBlocking);
+            clientSocketConn.set_blocking(_blockingOnReceive, _timeoutOnBlocking);
             int rxLen = clientSocketConn.receive(_buffer, HTTPD_MAX_REQ_LENGTH);
             if (rxLen == -1)
             {
                 RD_DBG("clientSocketConn.receive() returned %d\r\n", rxLen);
-                if (closeConnOnReceiveFail)
+                if (_closeConnOnReceiveFail)
                 {
                     int closeRet = clientSocketConn.close();
                     RD_DBG("Failed receive connection close() ret %d is connected %d\r\n", closeRet, clientSocketConn.is_connected());
@@ -118,6 +116,9 @@
             if (rxLen > HTTPD_MAX_REQ_LENGTH)
             {
                 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;
             }
         
@@ -126,15 +127,25 @@
             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));
+//            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
+                }
             
-            RD_DBG("Sent %s header ret %d content ret %d now connected %d\r\n", content, sentRet, sentRet2, clientSocketConn.is_connected());
-            
-            if (closeConnAfterSend)
+            // Close connection if required
+            if (_closeConnAfterSend)
             {
                 int closeRet = clientSocketConn.close();
                 RD_DBG("After send connection close() ret %d is connected %d\r\n", closeRet, clientSocketConn.is_connected());
@@ -169,8 +180,8 @@
 //                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_all(_httpHeader,strlen(_httpHeader));
-//                    clientSocketConn.send_all(_buffer, rxLen);
+//                    clientSocketConn.send(_httpHeader,strlen(_httpHeader));
+//                    clientSocketConn.send(_buffer, rxLen);
 //                    break;
 //                }
 //                _buffer[rxLen] = '\0';
@@ -199,7 +210,7 @@
 //
 bool RdWebServer::handleReceivedHttp(TCPSocketConnection &clientSocketConn)
 {
-    bool closeConn = true;
+    bool handledOk = false;
     RD_DBG("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)
@@ -224,50 +235,37 @@
                 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
                 {
                     char* respStr = ((*it)->_callback)(method, cmdStr, argStr);
-                    clientSocketConn.send_all(respStr, strlen(respStr));
+                    clientSocketConn.send(respStr, strlen(respStr));
                 }
-                else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
+                else if ( ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE) ||
+                          ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE) )
                 {
-#ifdef SUPPORT_LOCAL_FILE_CACHE
-                    if ((*it)->_substFileName[0] != '\0')
-                    {
-                        char combinedFileName[MAX_FILENAME_LEN];
-                        strcpy(combinedFileName, (*it)->_substFileName);
-                        if (combinedFileName[strlen(combinedFileName)-1] == '*')
-                            strcpy(combinedFileName+strlen(combinedFileName)-1, argStr);
-                        handleLocalFileRequest(combinedFileName, argStr, clientSocketConn, _httpHeader, (*it)->_bCacheIfPossible);
-                    }
+                    char combinedFileName[MAX_FILENAME_LEN];
+                    strcpy(combinedFileName, (*it)->_substFileName);
+                    if (strlen(combinedFileName) == 0)
+                        strcpy(combinedFileName, cmdStr);
+                    else if (combinedFileName[strlen(combinedFileName)-1] == '*')
+                        strcpy(combinedFileName+strlen(combinedFileName)-1, argStr);
+                    if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
+                        handledOk = handleLocalFileRequest(combinedFileName, argStr, clientSocketConn, (*it)->_bCacheIfPossible);
                     else
-#endif
-                    {
-                        handleLocalFileRequest(cmdStr, argStr, clientSocketConn, _httpHeader, (*it)->_bCacheIfPossible);
-                    }
-                }
-                else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE)
-                {
-#ifdef SUPPORT_LOCAL_FILE_CACHE
-                    if ((*it)->_substFileName[0] != '\0')
-                    {
-                        char combinedFileName[MAX_FILENAME_LEN];
-                        strcpy(combinedFileName, (*it)->_substFileName);
-                        if (combinedFileName[strlen(combinedFileName)-1] == '*')
-                            strcpy(combinedFileName+strlen(combinedFileName)-1, argStr);
-                        closeConn = handleSDFileRequest(combinedFileName, argStr, clientSocketConn, _httpHeader);
-                    }
-                    else
-#endif
-                    {
-                        closeConn = handleSDFileRequest(cmdStr, argStr, clientSocketConn, _httpHeader);
-                    }
+                        handledOk = handleSDFileRequest(combinedFileName, argStr, clientSocketConn);
+
                 }
                 break;
             }
         }
         // If command not found see if it is a local file
         if (!cmdFound)
-            closeConn = handleSDFileRequest(cmdStr, argStr, clientSocketConn, _httpHeader);
+        {
+            char combinedFileName[MAX_FILENAME_LEN];
+            strcpy(combinedFileName, cmdStr);
+            strcat(combinedFileName, "/");
+            strcat(combinedFileName, argStr);
+            handledOk = handleSDFileRequest(cmdStr, argStr, clientSocketConn);
+        }
     }
-    return closeConn;
+    return handledOk;
 }
 
 void RdWebServer::addCommand(char* pCmdStr, int cmdType, CmdCallbackType callback, char* substFileName, bool cacheIfPossible)
@@ -275,9 +273,35 @@
     _commands.push_back(new RdWebServerCmdDef(pCmdStr, cmdType, callback, substFileName, cacheIfPossible));
 }
 
-bool RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn, char* httpHeader)
+void RdWebServer::formHTTPHeader(const char* rsltCode, const char* contentType, int contentLen)
+{
+    const char* closeConnStr = "\r\nConnection: Close";
+    if(contentLen != -1)
+        sprintf(_httpHeader, "HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %d%s\r\n\r\n", rsltCode, contentType, contentLen, _closeConnAfterSend ? closeConnStr : "");
+    else
+        sprintf(_httpHeader, "HTTP/1.1 %s\r\nContent-Type: %s%s\r\n\r\n", rsltCode, contentType, _closeConnAfterSend ? closeConnStr : "");
+}
+    
+char* getMimeTypeStr(char* filename)
 {
-    bool closeConn = true;
+    char* mimeType = "text/plain";
+    char *pDot = strrchr(filename, '.');
+    if (pDot && (!strcmp(pDot, ".html") || !strcmp(pDot, ".htm")))
+        mimeType = "text/html";
+    else if (pDot && !strcmp(pDot, ".css"))
+        mimeType = "text/css";
+    else if (pDot && !strcmp(pDot, ".js"))
+        mimeType = "application/javascript";
+    else if (pDot && !strcmp(pDot, ".png"))
+        mimeType = "image/png";
+    else if (pDot && (!strcmp(pDot, ".jpeg") || !strcmp(pDot, ".jpeg")))
+        mimeType = "image/jpeg";
+    return mimeType;
+}
+    
+bool RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn)
+{
+    bool handledOk = false;
     const int HTTPD_MAX_FNAME_LENGTH = 127;
     char filename[HTTPD_MAX_FNAME_LENGTH+1];
     
@@ -291,22 +315,23 @@
         DIR *d = opendir(filename);
         if (d != NULL) 
         {
-            sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
-            clientSocketConn.send_all(httpHeader,strlen(httpHeader));
-            sprintf(httpHeader,"<html><head><title>Directory Listing</title></head><body><h1>%s</h1><ul>", inFileName);
-            clientSocketConn.send_all(httpHeader,strlen(httpHeader));
+            formHTTPHeader("200 OK", "text/html", -1);
+            clientSocketConn.send(_httpHeader,strlen(_httpHeader));
+            sprintf(_httpHeader,"<html><head><title>Directory Listing</title></head><body><h1>%s</h1><ul>", inFileName);
+            clientSocketConn.send(_httpHeader,strlen(_httpHeader));
             struct dirent *p;
             while((p = readdir(d)) != NULL) 
             {
                 RD_INFO("%s\r\n", p->d_name);
-                sprintf(httpHeader,"<li>%s</li>", p->d_name);
-                clientSocketConn.send_all(httpHeader,strlen(httpHeader));
+                sprintf(_httpHeader,"<li>%s</li>", p->d_name);
+                clientSocketConn.send(_httpHeader,strlen(_httpHeader));
             }
         }
         closedir(d);
         RD_DBG("Directory closed\n");
-        sprintf(httpHeader,"</ul></body></html>");
-        clientSocketConn.send_all(httpHeader,strlen(httpHeader));
+        sprintf(_httpHeader,"</ul></body></html>");
+        clientSocketConn.send(_httpHeader,strlen(_httpHeader));
+        handledOk = true;
     }
     else
 #endif
@@ -318,51 +343,49 @@
         if (fp == NULL)
         {
             RD_WARN("Filename %s not found\r\n", filename);
-            sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nContent-Length: 0\r\n\r\n");
-            clientSocketConn.send_all(httpHeader,strlen(httpHeader));
-            closeConn = false;
+            formHTTPHeader("404 Not Found", "text/plain", 0);
+            clientSocketConn.send(_httpHeader,strlen(_httpHeader));
+            handledOk = true;
         }
         else
         {
             RD_INFO("Sending file %s\r\n", filename);
+            // Find file length
             fseek(fp, 0L, SEEK_END);
             int sz = ftell(fp);
             fseek(fp, 0L, SEEK_SET);
-            sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", sz);
-            clientSocketConn.send_all(httpHeader,strlen(httpHeader));
-            // MIME type
-            char *pDot = strrchr(filename, '.');
-            char mimeType[MAX_MIMETYPE_LEN+1];
-            if (pDot && (!strcmp(pDot, ".html") || !strcmp(pDot, ".htm")))
-                strcpy(mimeType, "Content-Type: text/html\r\n");
-            else if (pDot && !strcmp(pDot, ".css"))
-                strcpy(mimeType, "Content-Type: text/css\r\n");
-            if (pDot && !strcmp(pDot, ".js"))
-                strcpy(mimeType, "Content-Type: application/javascript\r\n");
-//            clientSocketConn.send_all(mimeType,strlen(mimeType));
+            // Get mime type
+            char* mimeType = getMimeTypeStr(filename);
             RD_INFO("MIME TYPE %s", mimeType);
+            // Form header   
+            formHTTPHeader("200 OK", mimeType, sz);
+            clientSocketConn.send(_httpHeader,strlen(_httpHeader));
             // Read file in blocks and send
             int rdCnt = 0;
             char fileBuf[1024];
             while ((rdCnt = fread(fileBuf, sizeof( char ), 1024, fp)) == 1024) 
             {
-                clientSocketConn.send_all(fileBuf, rdCnt);
+                clientSocketConn.send(fileBuf, rdCnt);
             }
-            clientSocketConn.send_all(fileBuf, rdCnt);
+            clientSocketConn.send(fileBuf, rdCnt);
             fclose(fp);
-            closeConn = false;
+            handledOk = true;
         }
     }
-    return closeConn;
+    return handledOk;
 }
 
 #ifdef SUPPORT_LOCAL_FILE_CACHE
 
-void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &clientSocketConn, char* httpHeader)
+void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &clientSocketConn)
 {
     RD_INFO("Sending file %s from cache %d bytes\r\n", pCacheEntry->_fileName, pCacheEntry->_nFileLen);
-    sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
-    clientSocketConn.send_all(httpHeader,strlen(httpHeader));
+    // Get mime type
+    char* mimeType = getMimeTypeStr(pCacheEntry->_fileName);
+    RD_INFO("MIME TYPE %s", mimeType);
+    // Form header
+    formHTTPHeader("200 OK", mimeType, pCacheEntry->_nFileLen);
+    clientSocketConn.send(_httpHeader,strlen(_httpHeader));
     char* pMem = pCacheEntry->_pFileContent;
     int nLenLeft = pCacheEntry->_nFileLen;
     int blkSize = 2048;
@@ -370,14 +393,14 @@
     {
         if (blkSize > nLenLeft)
             blkSize = nLenLeft;
-        clientSocketConn.send_all(pMem, blkSize);
+        clientSocketConn.send(pMem, blkSize);
         pMem += blkSize;
         nLenLeft -= blkSize;
     }
 }
 #endif
 
-void RdWebServer::handleLocalFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn, char* httpHeader, bool bCacheIfPossible)
+bool RdWebServer::handleLocalFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn, bool bCacheIfPossible)
 {
 
 #ifdef SUPPORT_LOCAL_FILESYSTEM
@@ -401,7 +424,7 @@
                 if ((*it)->_bCacheValid)
                 {
                     sendFromCache(*it, clientSocketConn, httpHeader);
-                    return;
+                    return true;
                 }
                 bTryToCache = false; // don't try to cache as cacheing must have already failed
             }
@@ -419,7 +442,7 @@
                 if (bCacheSuccess)
                 {
                     sendFromCache(pCacheEntry, clientSocketConn, httpHeader);
-                    return;
+                    return true;
                 }
             }
         }        
@@ -431,31 +454,40 @@
     if (fp == NULL)
     {
         RD_WARN("Local file %s not found\r\n", localFilename);
-        sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
-        clientSocketConn.send_all(httpHeader,strlen(httpHeader));
-        clientSocketConn.send_all(reqFileNameStr,strlen(reqFileNameStr));
+        formHTTPHeader("404 Not Found", "text/plain", 0);
+        clientSocketConn.send(_httpHeader,strlen(_httpHeader));
+        return true;
     }
     else
     {
         RD_INFO("Sending file %s from disk\r\n", localFilename);
-        sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
-        clientSocketConn.send_all(httpHeader,strlen(httpHeader));
+        // Find file length
+        fseek(fp, 0L, SEEK_END);
+        int sz = ftell(fp);
+        fseek(fp, 0L, SEEK_SET);
+        // Get mime type
+        char* mimeType = getMimeTypeStr(localFilename);
+        RD_INFO("MIME TYPE %s", mimeType);
+        // Form header   
+        formHTTPHeader("200 OK", mimeType, sz);
+        clientSocketConn.send(_httpHeader,strlen(_httpHeader));
         int rdCnt = 0;
-        char fileBuf[2000];
-        while ((rdCnt = fread(fileBuf, sizeof( char ), 2000, fp)) == 2000) 
+        while ((rdCnt = fread(_httpHeader, sizeof( char ), sizeof(_httpHeader), fp)) == sizeof(_httpHeader)) 
         {
-            clientSocketConn.send_all(fileBuf, rdCnt);
+            clientSocketConn.send(_httpHeader, rdCnt);
         }
-        clientSocketConn.send_all(fileBuf, rdCnt);
+        if (rdCnt != 0)
+            clientSocketConn.send(_httpHeader, rdCnt);
         fclose(fp);
+        return true;
     }
     
 #else
 
     RD_WARN("Local file system not supported\r\n");
-    sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
-    clientSocketConn.send_all(httpHeader,strlen(httpHeader));
-    clientSocketConn.send_all(inFileName,strlen(inFileName));
+    formHTTPHeader("404 Not Found", "text/plain", 0);
+    clientSocketConn.send(_httpHeader,strlen(_httpHeader));
+    return true;
 
 #endif
 }
--- a/RdWebServer.h	Tue May 05 22:14:16 2015 +0000
+++ b/RdWebServer.h	Mon May 11 11:17:15 2015 +0000
@@ -53,6 +53,7 @@
 //#define SUPPORT_LOCAL_FILESYSTEM 1
 //#define SUPPORT_FOLDER_VIEW 1
 //#define SUPPORT_LOCAL_FILE_CACHE 1
+const int SUBST_MAX_FNAME_LEN = 40;  // note local files on MBED are 8.3 but this might include other files
 
 extern RawSerial pc;
 
@@ -61,12 +62,11 @@
 class RdFileCacheEntry
 {
     public:
-        static const int CACHE_MAX_FNAME_LEN = 15;  // note local files on MBED are 8.3
         RdFileCacheEntry(char* pFileName)
         {
             _bCacheValid = false;
-            strncpy(_fileName, pFileName, CACHE_MAX_FNAME_LEN-1);
-            _fileName[CACHE_MAX_FNAME_LEN-1] = '\0';
+            strncpy(_fileName, pFileName, SUBST_MAX_FNAME_LEN-1);
+            _fileName[SUBST_MAX_FNAME_LEN-1] = '\0';
             _pFileContent = NULL;
         }
         ~RdFileCacheEntry()
@@ -76,7 +76,7 @@
         bool readLocalFileIntoCache(char* fileName);
     public:
         bool _bCacheValid;
-        char _fileName[CACHE_MAX_FNAME_LEN];
+        char _fileName[SUBST_MAX_FNAME_LEN];
         char* _pFileContent;
         int _nFileLen;
 };
@@ -91,27 +91,23 @@
         static const int CMD_LOCALFILE = 1;
         static const int CMD_CALLBACK = 2;
         static const int CMD_SDORUSBFILE = 3;
-        RdWebServerCmdDef(char* pStr, int cmdType, CmdCallbackType callback, char* subst8dot3FileName = NULL, bool bCacheIfPossible = false)
+        RdWebServerCmdDef(char* pStr, int cmdType, CmdCallbackType callback, char* substFileName = NULL, bool bCacheIfPossible = false)
         {
             _pCmdStr = pStr;
             _cmdType = cmdType;
             _callback = callback;
-#ifdef SUPPORT_LOCAL_FILE_CACHE
-            _subst8dot3FileName[0] = '\0';
-            if (subst8dot3FileName != NULL)
+            _substFileName[0] = '\0';
+            if (substFileName != NULL)
             {
-                strncpy(_subst8dot3FileName, subst8dot3FileName, RdFileCacheEntry::CACHE_MAX_FNAME_LEN);
-                _subst8dot3FileName[RdFileCacheEntry::CACHE_MAX_FNAME_LEN-1] = '\0';
+                strncpy(_substFileName, substFileName, SUBST_MAX_FNAME_LEN);
+                _substFileName[SUBST_MAX_FNAME_LEN-1] = '\0';
             }
-#endif
             _bCacheIfPossible = bCacheIfPossible;
         };
         char* _pCmdStr;
         int _cmdType;
         CmdCallbackType _callback;
-#ifdef SUPPORT_LOCAL_FILE_CACHE
-        char _subst8dot3FileName[RdFileCacheEntry::CACHE_MAX_FNAME_LEN];
-#endif
+        char _substFileName[SUBST_MAX_FNAME_LEN];
         bool _bCacheIfPossible;
 };
 
@@ -145,13 +141,21 @@
         char _httpHeader[HTTPD_MAX_HDR_LENGTH+1];
         char _buffer[HTTPD_MAX_REQ_LENGTH+1];
 
-        void handleLocalFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client, char* httpHeader, bool bCacheIfPossible);
-        bool handleSDFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client, char* httpHeader);
+        bool handleLocalFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client, bool bCacheIfPossible);
+        bool handleSDFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client);
         void handleCGIRequest(char* pUriStr, char* pQueryStr);
 #ifdef SUPPORT_LOCAL_FILE_CACHE
-        void sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &client, char* httpHeader);
+        void sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &client);
 #endif
         bool handleReceivedHttp(TCPSocketConnection &client);
+        void formHTTPHeader(const char* rsltCode, const char* contentType, int contentLen);
+
+        // Settings - see the constructor
+        bool _blockingOnAccept;
+        bool _blockingOnReceive;
+        int _timeoutOnBlocking;
+        bool _closeConnAfterSend;
+        bool _closeConnOnReceiveFail;
 
 };