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 May 11 14:26:54 2015 +0000
Revision:
18:5de680c4cfcb
Parent:
17:080f2bed8b36
Child:
19:f67ac231b570
Tidied up - still resets connection when it receives two requests in very short succession - not sure if this is a bug in lwip or something to do with socket limits or lack of threading

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Bobty 3:594136d34a32 1 /* RdWebServer.cpp
Bobty 15:0865fa4b046a 2 Rob Dobson 2013-2015
Bobty 3:594136d34a32 3 Inspired by Jasper Schuurmans multi-threaded web server for Netduino http://www.schuurmans.cc/multi-threaded-web-server-for-netduino-plus
Bobty 3:594136d34a32 4 More details at http://robdobson.com/2013/10/moving-my-window-shades-control-to-mbed/
Bobty 3:594136d34a32 5 */
Bobty 3:594136d34a32 6
Bobty 18:5de680c4cfcb 7 // Setting RDWEB_DEBUG to 4 causes all debugging to be shown
Bobty 18:5de680c4cfcb 8 #define RDWEB_DEBUG 1
Bobty 18:5de680c4cfcb 9
Bobty 18:5de680c4cfcb 10 // Change the settings below to support a local file system (not available on some MBEDs)
Bobty 18:5de680c4cfcb 11 //#define SUPPORT_LOCAL_FILESYSTEM 1
Bobty 18:5de680c4cfcb 12 //#define SUPPORT_LOCAL_FILE_CACHE 1
Bobty 18:5de680c4cfcb 13
Bobty 18:5de680c4cfcb 14 // Change this to support display of files on the server
Bobty 18:5de680c4cfcb 15 //#define SUPPORT_FOLDER_VIEW 1
Bobty 12:c14ffd4ec125 16
Bobty 0:b5b4d07f7827 17 #include "RdWebServer.h"
Bobty 0:b5b4d07f7827 18
Bobty 18:5de680c4cfcb 19 // Limits - note particularly the MAX_FILENAME_LEN
Bobty 15:0865fa4b046a 20 const int MAX_CMDSTR_LEN = 100;
Bobty 15:0865fa4b046a 21 const int MAX_ARGSTR_LEN = 100;
Bobty 15:0865fa4b046a 22 const int MAX_FILENAME_LEN = (MAX_ARGSTR_LEN + 20);
Bobty 1:75bb184de749 23
Bobty 18:5de680c4cfcb 24 // Constructor
Bobty 0:b5b4d07f7827 25 RdWebServer::RdWebServer()
Bobty 0:b5b4d07f7827 26 {
Bobty 15:0865fa4b046a 27 _initOk = false;
Bobty 0:b5b4d07f7827 28 _pStatusLed = NULL;
Bobty 0:b5b4d07f7827 29 }
Bobty 0:b5b4d07f7827 30
Bobty 18:5de680c4cfcb 31 // Destructor
Bobty 0:b5b4d07f7827 32 RdWebServer::~RdWebServer()
Bobty 0:b5b4d07f7827 33 {
Bobty 2:9d8793c23b46 34 // Clean-up - probably never called as we're on a microcontroller!
Bobty 2:9d8793c23b46 35 for (std::vector<RdWebServerCmdDef*>::iterator it = _commands.begin() ; it != _commands.end(); ++it)
Bobty 2:9d8793c23b46 36 delete (*it);
Bobty 0:b5b4d07f7827 37 }
Bobty 0:b5b4d07f7827 38
Bobty 18:5de680c4cfcb 39 // Init
Bobty 4:6afb3bbf20a4 40 bool RdWebServer::init(int port, DigitalOut* pStatusLed, char* pBaseWebFolder)
Bobty 0:b5b4d07f7827 41 {
Bobty 15:0865fa4b046a 42 // Settings
Bobty 0:b5b4d07f7827 43 _port = port;
Bobty 0:b5b4d07f7827 44 _pStatusLed = pStatusLed;
Bobty 4:6afb3bbf20a4 45 _pBaseWebFolder = pBaseWebFolder;
Bobty 0:b5b4d07f7827 46
Bobty 17:080f2bed8b36 47 // The sample web server on MBED site turns off blocking after the accept has happened
Bobty 17:080f2bed8b36 48 // I've tried this but it doesn't seem to yield a reliable server
Bobty 17:080f2bed8b36 49 _blockingOnAccept = true;
Bobty 17:080f2bed8b36 50 _blockingOnReceive = true;
Bobty 17:080f2bed8b36 51
Bobty 17:080f2bed8b36 52 // This is the same as the default in the socket.cpp file
Bobty 17:080f2bed8b36 53 _timeoutOnBlocking = 1500;
Bobty 17:080f2bed8b36 54
Bobty 17:080f2bed8b36 55 // Currently tested using close connection after send with a single file which works fine
Bobty 17:080f2bed8b36 56 // If closeConnAfterSend is set false then the socket connection remains open and only
Bobty 17:080f2bed8b36 57 // one client can access the server at a time
Bobty 17:080f2bed8b36 58 // Need to test with the closeConnAfterSend seetting true when trying to download multiple files
Bobty 17:080f2bed8b36 59 // for a website as previous experience would indicate that requests might be missed in this scenario
Bobty 17:080f2bed8b36 60 // although all other settings may not have been the same
Bobty 17:080f2bed8b36 61 _closeConnAfterSend = true;
Bobty 17:080f2bed8b36 62 _closeConnOnReceiveFail = true;
Bobty 17:080f2bed8b36 63
Bobty 15:0865fa4b046a 64 // Setup tcp socket
Bobty 15:0865fa4b046a 65 _serverSocket.set_blocking(true);
Bobty 15:0865fa4b046a 66 if(_serverSocket.bind(port)< 0)
Bobty 0:b5b4d07f7827 67 {
Bobty 11:cec51b430b20 68 RD_WARN("TCP server bind fail\n\r");
Bobty 0:b5b4d07f7827 69 return false;
Bobty 0:b5b4d07f7827 70 }
Bobty 15:0865fa4b046a 71 if(_serverSocket.listen(1) < 0)
Bobty 0:b5b4d07f7827 72 {
Bobty 11:cec51b430b20 73 RD_WARN("TCP server listen fail\n\r");
Bobty 0:b5b4d07f7827 74 return false;
Bobty 0:b5b4d07f7827 75 }
Bobty 15:0865fa4b046a 76 RD_INFO("TCP server is listening...\r\n");
Bobty 15:0865fa4b046a 77 _initOk = true;
Bobty 6:46285c519af2 78 return true;
Bobty 6:46285c519af2 79 }
Bobty 6:46285c519af2 80
Bobty 18:5de680c4cfcb 81 // Run - never returns so run in a thread
Bobty 15:0865fa4b046a 82 void RdWebServer::run()
Bobty 15:0865fa4b046a 83 {
Bobty 15:0865fa4b046a 84 // Check initialised ok
Bobty 15:0865fa4b046a 85 if (!_initOk)
Bobty 15:0865fa4b046a 86 return;
Bobty 15:0865fa4b046a 87
Bobty 15:0865fa4b046a 88 // Start accepting connections
Bobty 15:0865fa4b046a 89 while (true)
Bobty 15:0865fa4b046a 90 {
Bobty 15:0865fa4b046a 91 TCPSocketConnection clientSocketConn;
Bobty 15:0865fa4b046a 92 // Accept connection if available
Bobty 15:0865fa4b046a 93 RD_INFO("Waiting for TCP connection\r\n");
Bobty 17:080f2bed8b36 94 clientSocketConn.set_blocking(_blockingOnAccept, _timeoutOnBlocking);
Bobty 15:0865fa4b046a 95 if(_serverSocket.accept(clientSocketConn)<0)
Bobty 15:0865fa4b046a 96 {
Bobty 15:0865fa4b046a 97 RD_WARN("TCP Socket failed to accept connection\n\r");
Bobty 15:0865fa4b046a 98 continue;
Bobty 15:0865fa4b046a 99 }
Bobty 15:0865fa4b046a 100
Bobty 15:0865fa4b046a 101 // Connection
Bobty 15:0865fa4b046a 102 RD_INFO("Connection from IP: %s\n\r", clientSocketConn.get_address());
Bobty 15:0865fa4b046a 103 if (_pStatusLed != NULL)
Bobty 15:0865fa4b046a 104 *_pStatusLed = true;
Bobty 15:0865fa4b046a 105
Bobty 15:0865fa4b046a 106 // While connected
Bobty 15:0865fa4b046a 107 bool forcedClosed = false;
Bobty 15:0865fa4b046a 108 while(clientSocketConn.is_connected() && !forcedClosed)
Bobty 15:0865fa4b046a 109 {
Bobty 15:0865fa4b046a 110 // Receive data
Bobty 17:080f2bed8b36 111 clientSocketConn.set_blocking(_blockingOnReceive, _timeoutOnBlocking);
Bobty 15:0865fa4b046a 112 int rxLen = clientSocketConn.receive(_buffer, HTTPD_MAX_REQ_LENGTH);
Bobty 15:0865fa4b046a 113 if (rxLen == -1)
Bobty 15:0865fa4b046a 114 {
Bobty 15:0865fa4b046a 115 RD_DBG("clientSocketConn.receive() returned %d\r\n", rxLen);
Bobty 17:080f2bed8b36 116 if (_closeConnOnReceiveFail)
Bobty 15:0865fa4b046a 117 {
Bobty 15:0865fa4b046a 118 int closeRet = clientSocketConn.close();
Bobty 15:0865fa4b046a 119 RD_DBG("Failed receive connection close() ret %d is connected %d\r\n", closeRet, clientSocketConn.is_connected());
Bobty 15:0865fa4b046a 120 forcedClosed = true;
Bobty 15:0865fa4b046a 121 }
Bobty 15:0865fa4b046a 122 continue;
Bobty 15:0865fa4b046a 123 }
Bobty 15:0865fa4b046a 124 if (rxLen == 0)
Bobty 15:0865fa4b046a 125 {
Bobty 16:0248bbfdb6c1 126 RD_DBG("clientSocketConn.receive() returned %d - ignoring - is connected %d\r\n", rxLen, clientSocketConn.is_connected());
Bobty 15:0865fa4b046a 127 continue;
Bobty 15:0865fa4b046a 128 }
Bobty 15:0865fa4b046a 129 if (rxLen > HTTPD_MAX_REQ_LENGTH)
Bobty 15:0865fa4b046a 130 {
Bobty 15:0865fa4b046a 131 RD_DBG("clientSocketConn.receive() returned %d - too long\r\n", rxLen);
Bobty 17:080f2bed8b36 132 formHTTPHeader("413 Request Entity Too Large", "text/plain", 0);
Bobty 17:080f2bed8b36 133 int sentRet = clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 15:0865fa4b046a 134 continue;
Bobty 15:0865fa4b046a 135 }
Bobty 18:5de680c4cfcb 136
Bobty 18:5de680c4cfcb 137 // Handle received message
Bobty 15:0865fa4b046a 138 RD_DBG("Received len %d\r\n", rxLen);
Bobty 15:0865fa4b046a 139 _buffer[rxLen] = '\0';
Bobty 15:0865fa4b046a 140 RD_DBG("%s\r\n", _buffer);
Bobty 18:5de680c4cfcb 141 if (handleReceivedHttp(clientSocketConn))
Bobty 18:5de680c4cfcb 142 {
Bobty 18:5de680c4cfcb 143 // OK
Bobty 18:5de680c4cfcb 144 }
Bobty 18:5de680c4cfcb 145 else
Bobty 18:5de680c4cfcb 146 {
Bobty 18:5de680c4cfcb 147 // FAIL
Bobty 18:5de680c4cfcb 148 }
Bobty 15:0865fa4b046a 149
Bobty 17:080f2bed8b36 150 // Close connection if required
Bobty 17:080f2bed8b36 151 if (_closeConnAfterSend)
Bobty 15:0865fa4b046a 152 {
Bobty 15:0865fa4b046a 153 int closeRet = clientSocketConn.close();
Bobty 16:0248bbfdb6c1 154 RD_DBG("After send connection close() ret %d is connected %d\r\n", closeRet, clientSocketConn.is_connected());
Bobty 16:0248bbfdb6c1 155 forcedClosed = true;
Bobty 15:0865fa4b046a 156 }
Bobty 15:0865fa4b046a 157 }
Bobty 15:0865fa4b046a 158 }
Bobty 15:0865fa4b046a 159 }
Bobty 18:5de680c4cfcb 160
Bobty 18:5de680c4cfcb 161 // Handle a request
Bobty 15:0865fa4b046a 162 bool RdWebServer::handleReceivedHttp(TCPSocketConnection &clientSocketConn)
Bobty 6:46285c519af2 163 {
Bobty 17:080f2bed8b36 164 bool handledOk = false;
Bobty 11:cec51b430b20 165 RD_DBG("Received Data: %d\n\r\n\r%.*s\n\r",strlen(_buffer),strlen(_buffer),_buffer);
Bobty 7:fe7c33f7fbb8 166 int method = METHOD_OTHER;
Bobty 6:46285c519af2 167 if (strncmp(_buffer, "GET ", 4) == 0)
Bobty 7:fe7c33f7fbb8 168 method = METHOD_GET;
Bobty 7:fe7c33f7fbb8 169 if (strncmp(_buffer, "POST", 4) == 0)
Bobty 7:fe7c33f7fbb8 170 method = METHOD_POST;
Bobty 7:fe7c33f7fbb8 171
Bobty 7:fe7c33f7fbb8 172 char cmdStr[MAX_CMDSTR_LEN];
Bobty 7:fe7c33f7fbb8 173 char argStr[MAX_ARGSTR_LEN];
Bobty 7:fe7c33f7fbb8 174 if (extractCmdArgs(_buffer+3, cmdStr, MAX_CMDSTR_LEN, argStr, MAX_ARGSTR_LEN))
Bobty 6:46285c519af2 175 {
Bobty 11:cec51b430b20 176 RD_DBG("CmdStr %s\n\r", cmdStr);
Bobty 11:cec51b430b20 177 RD_DBG("ArgStr %s\n\r", argStr);
Bobty 7:fe7c33f7fbb8 178 bool cmdFound = false;
Bobty 7:fe7c33f7fbb8 179 for (std::vector<RdWebServerCmdDef*>::iterator it = _commands.begin() ; it != _commands.end(); ++it)
Bobty 6:46285c519af2 180 {
Bobty 11:cec51b430b20 181 RD_DBG("Testing <<%s>> with <<%s>>\r\n", (*it)->_pCmdStr, cmdStr);
Bobty 7:fe7c33f7fbb8 182 if (strcasecmp((*it)->_pCmdStr, cmdStr) == 0)
Bobty 7:fe7c33f7fbb8 183 {
Bobty 11:cec51b430b20 184 RD_DBG("FoundCmd <%s> Type %d\n\r", cmdStr, (*it)->_cmdType);
Bobty 7:fe7c33f7fbb8 185 cmdFound = true;
Bobty 7:fe7c33f7fbb8 186 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
Bobty 7:fe7c33f7fbb8 187 {
Bobty 7:fe7c33f7fbb8 188 char* respStr = ((*it)->_callback)(method, cmdStr, argStr);
Bobty 17:080f2bed8b36 189 clientSocketConn.send(respStr, strlen(respStr));
Bobty 6:46285c519af2 190 }
Bobty 17:080f2bed8b36 191 else if ( ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE) ||
Bobty 17:080f2bed8b36 192 ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE) )
Bobty 7:fe7c33f7fbb8 193 {
Bobty 17:080f2bed8b36 194 char combinedFileName[MAX_FILENAME_LEN];
Bobty 17:080f2bed8b36 195 strcpy(combinedFileName, (*it)->_substFileName);
Bobty 17:080f2bed8b36 196 if (strlen(combinedFileName) == 0)
Bobty 17:080f2bed8b36 197 strcpy(combinedFileName, cmdStr);
Bobty 17:080f2bed8b36 198 else if (combinedFileName[strlen(combinedFileName)-1] == '*')
Bobty 17:080f2bed8b36 199 strcpy(combinedFileName+strlen(combinedFileName)-1, argStr);
Bobty 17:080f2bed8b36 200 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
Bobty 17:080f2bed8b36 201 handledOk = handleLocalFileRequest(combinedFileName, argStr, clientSocketConn, (*it)->_bCacheIfPossible);
Bobty 7:fe7c33f7fbb8 202 else
Bobty 17:080f2bed8b36 203 handledOk = handleSDFileRequest(combinedFileName, argStr, clientSocketConn);
Bobty 17:080f2bed8b36 204
Bobty 7:fe7c33f7fbb8 205 }
Bobty 7:fe7c33f7fbb8 206 break;
Bobty 6:46285c519af2 207 }
Bobty 6:46285c519af2 208 }
Bobty 7:fe7c33f7fbb8 209 // If command not found see if it is a local file
Bobty 7:fe7c33f7fbb8 210 if (!cmdFound)
Bobty 17:080f2bed8b36 211 {
Bobty 17:080f2bed8b36 212 char combinedFileName[MAX_FILENAME_LEN];
Bobty 17:080f2bed8b36 213 strcpy(combinedFileName, cmdStr);
Bobty 17:080f2bed8b36 214 strcat(combinedFileName, "/");
Bobty 17:080f2bed8b36 215 strcat(combinedFileName, argStr);
Bobty 17:080f2bed8b36 216 handledOk = handleSDFileRequest(cmdStr, argStr, clientSocketConn);
Bobty 17:080f2bed8b36 217 }
Bobty 6:46285c519af2 218 }
Bobty 17:080f2bed8b36 219 return handledOk;
Bobty 6:46285c519af2 220 }
Bobty 6:46285c519af2 221
Bobty 18:5de680c4cfcb 222 // Add a command to the server
Bobty 6:46285c519af2 223 void RdWebServer::addCommand(char* pCmdStr, int cmdType, CmdCallbackType callback, char* substFileName, bool cacheIfPossible)
Bobty 6:46285c519af2 224 {
Bobty 6:46285c519af2 225 _commands.push_back(new RdWebServerCmdDef(pCmdStr, cmdType, callback, substFileName, cacheIfPossible));
Bobty 6:46285c519af2 226 }
Bobty 6:46285c519af2 227
Bobty 18:5de680c4cfcb 228 // Form a header to respond
Bobty 17:080f2bed8b36 229 void RdWebServer::formHTTPHeader(const char* rsltCode, const char* contentType, int contentLen)
Bobty 17:080f2bed8b36 230 {
Bobty 17:080f2bed8b36 231 const char* closeConnStr = "\r\nConnection: Close";
Bobty 17:080f2bed8b36 232 if(contentLen != -1)
Bobty 17:080f2bed8b36 233 sprintf(_httpHeader, "HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %d%s\r\n\r\n", rsltCode, contentType, contentLen, _closeConnAfterSend ? closeConnStr : "");
Bobty 17:080f2bed8b36 234 else
Bobty 17:080f2bed8b36 235 sprintf(_httpHeader, "HTTP/1.1 %s\r\nContent-Type: %s%s\r\n\r\n", rsltCode, contentType, _closeConnAfterSend ? closeConnStr : "");
Bobty 17:080f2bed8b36 236 }
Bobty 18:5de680c4cfcb 237
Bobty 18:5de680c4cfcb 238 // Use file extension to determine MIME type
Bobty 17:080f2bed8b36 239 char* getMimeTypeStr(char* filename)
Bobty 6:46285c519af2 240 {
Bobty 17:080f2bed8b36 241 char* mimeType = "text/plain";
Bobty 17:080f2bed8b36 242 char *pDot = strrchr(filename, '.');
Bobty 17:080f2bed8b36 243 if (pDot && (!strcmp(pDot, ".html") || !strcmp(pDot, ".htm")))
Bobty 17:080f2bed8b36 244 mimeType = "text/html";
Bobty 17:080f2bed8b36 245 else if (pDot && !strcmp(pDot, ".css"))
Bobty 17:080f2bed8b36 246 mimeType = "text/css";
Bobty 17:080f2bed8b36 247 else if (pDot && !strcmp(pDot, ".js"))
Bobty 17:080f2bed8b36 248 mimeType = "application/javascript";
Bobty 17:080f2bed8b36 249 else if (pDot && !strcmp(pDot, ".png"))
Bobty 17:080f2bed8b36 250 mimeType = "image/png";
Bobty 17:080f2bed8b36 251 else if (pDot && (!strcmp(pDot, ".jpeg") || !strcmp(pDot, ".jpeg")))
Bobty 17:080f2bed8b36 252 mimeType = "image/jpeg";
Bobty 17:080f2bed8b36 253 return mimeType;
Bobty 17:080f2bed8b36 254 }
Bobty 18:5de680c4cfcb 255
Bobty 18:5de680c4cfcb 256 // Handle request for files/listing from SDCard
Bobty 17:080f2bed8b36 257 bool RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn)
Bobty 17:080f2bed8b36 258 {
Bobty 17:080f2bed8b36 259 bool handledOk = false;
Bobty 6:46285c519af2 260 const int HTTPD_MAX_FNAME_LENGTH = 127;
Bobty 6:46285c519af2 261 char filename[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 262
Bobty 11:cec51b430b20 263 RD_INFO("Requesting file %s\n\r", inFileName);
Bobty 6:46285c519af2 264
Bobty 12:c14ffd4ec125 265 #ifdef SUPPORT_FOLDER_VIEW
Bobty 10:b4b9d4d5e5be 266 if ((strlen(inFileName) > 0) && (inFileName[strlen(inFileName)-1] == '/'))
Bobty 6:46285c519af2 267 {
Bobty 11:cec51b430b20 268 RD_INFO("Request directory %s%s\r\n", _pBaseWebFolder, inFileName);
Bobty 6:46285c519af2 269 sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
Bobty 6:46285c519af2 270 DIR *d = opendir(filename);
Bobty 11:cec51b430b20 271 if (d != NULL)
Bobty 11:cec51b430b20 272 {
Bobty 17:080f2bed8b36 273 formHTTPHeader("200 OK", "text/html", -1);
Bobty 17:080f2bed8b36 274 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 275 sprintf(_httpHeader,"<html><head><title>Directory Listing</title></head><body><h1>%s</h1><ul>", inFileName);
Bobty 17:080f2bed8b36 276 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 277 struct dirent *p;
Bobty 11:cec51b430b20 278 while((p = readdir(d)) != NULL)
Bobty 11:cec51b430b20 279 {
Bobty 11:cec51b430b20 280 RD_INFO("%s\r\n", p->d_name);
Bobty 17:080f2bed8b36 281 sprintf(_httpHeader,"<li>%s</li>", p->d_name);
Bobty 17:080f2bed8b36 282 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 283 }
Bobty 6:46285c519af2 284 }
Bobty 6:46285c519af2 285 closedir(d);
Bobty 11:cec51b430b20 286 RD_DBG("Directory closed\n");
Bobty 17:080f2bed8b36 287 sprintf(_httpHeader,"</ul></body></html>");
Bobty 17:080f2bed8b36 288 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 289 handledOk = true;
Bobty 6:46285c519af2 290 }
Bobty 6:46285c519af2 291 else
Bobty 12:c14ffd4ec125 292 #endif
Bobty 6:46285c519af2 293 {
Bobty 6:46285c519af2 294 sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
Bobty 11:cec51b430b20 295 RD_INFO("Filename %s\r\n", filename);
Bobty 6:46285c519af2 296
Bobty 6:46285c519af2 297 FILE* fp = fopen(filename, "r");
Bobty 6:46285c519af2 298 if (fp == NULL)
Bobty 6:46285c519af2 299 {
Bobty 11:cec51b430b20 300 RD_WARN("Filename %s not found\r\n", filename);
Bobty 17:080f2bed8b36 301 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 17:080f2bed8b36 302 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 303 handledOk = true;
Bobty 6:46285c519af2 304 }
Bobty 6:46285c519af2 305 else
Bobty 6:46285c519af2 306 {
Bobty 11:cec51b430b20 307 RD_INFO("Sending file %s\r\n", filename);
Bobty 17:080f2bed8b36 308 // Find file length
Bobty 14:4b83670854f0 309 fseek(fp, 0L, SEEK_END);
Bobty 14:4b83670854f0 310 int sz = ftell(fp);
Bobty 14:4b83670854f0 311 fseek(fp, 0L, SEEK_SET);
Bobty 17:080f2bed8b36 312 // Get mime type
Bobty 17:080f2bed8b36 313 char* mimeType = getMimeTypeStr(filename);
Bobty 18:5de680c4cfcb 314 RD_DBG("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 315 // Form header
Bobty 17:080f2bed8b36 316 formHTTPHeader("200 OK", mimeType, sz);
Bobty 17:080f2bed8b36 317 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 14:4b83670854f0 318 // Read file in blocks and send
Bobty 6:46285c519af2 319 int rdCnt = 0;
Bobty 6:46285c519af2 320 char fileBuf[1024];
Bobty 6:46285c519af2 321 while ((rdCnt = fread(fileBuf, sizeof( char ), 1024, fp)) == 1024)
Bobty 6:46285c519af2 322 {
Bobty 17:080f2bed8b36 323 clientSocketConn.send(fileBuf, rdCnt);
Bobty 6:46285c519af2 324 }
Bobty 17:080f2bed8b36 325 clientSocketConn.send(fileBuf, rdCnt);
Bobty 6:46285c519af2 326 fclose(fp);
Bobty 17:080f2bed8b36 327 handledOk = true;
Bobty 6:46285c519af2 328 }
Bobty 6:46285c519af2 329 }
Bobty 17:080f2bed8b36 330 return handledOk;
Bobty 6:46285c519af2 331 }
Bobty 6:46285c519af2 332
Bobty 18:5de680c4cfcb 333 // Send a file from cache - used for local files only
Bobty 15:0865fa4b046a 334 #ifdef SUPPORT_LOCAL_FILE_CACHE
Bobty 17:080f2bed8b36 335 void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &clientSocketConn)
Bobty 6:46285c519af2 336 {
Bobty 11:cec51b430b20 337 RD_INFO("Sending file %s from cache %d bytes\r\n", pCacheEntry->_fileName, pCacheEntry->_nFileLen);
Bobty 17:080f2bed8b36 338 // Get mime type
Bobty 17:080f2bed8b36 339 char* mimeType = getMimeTypeStr(pCacheEntry->_fileName);
Bobty 17:080f2bed8b36 340 RD_INFO("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 341 // Form header
Bobty 17:080f2bed8b36 342 formHTTPHeader("200 OK", mimeType, pCacheEntry->_nFileLen);
Bobty 17:080f2bed8b36 343 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 344 char* pMem = pCacheEntry->_pFileContent;
Bobty 6:46285c519af2 345 int nLenLeft = pCacheEntry->_nFileLen;
Bobty 6:46285c519af2 346 int blkSize = 2048;
Bobty 6:46285c519af2 347 while(nLenLeft > 0)
Bobty 6:46285c519af2 348 {
Bobty 6:46285c519af2 349 if (blkSize > nLenLeft)
Bobty 6:46285c519af2 350 blkSize = nLenLeft;
Bobty 17:080f2bed8b36 351 clientSocketConn.send(pMem, blkSize);
Bobty 6:46285c519af2 352 pMem += blkSize;
Bobty 6:46285c519af2 353 nLenLeft -= blkSize;
Bobty 6:46285c519af2 354 }
Bobty 6:46285c519af2 355 }
Bobty 15:0865fa4b046a 356 #endif
Bobty 6:46285c519af2 357
Bobty 18:5de680c4cfcb 358 // Handle a request for a local file
Bobty 17:080f2bed8b36 359 bool RdWebServer::handleLocalFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn, bool bCacheIfPossible)
Bobty 6:46285c519af2 360 {
Bobty 12:c14ffd4ec125 361
Bobty 12:c14ffd4ec125 362 #ifdef SUPPORT_LOCAL_FILESYSTEM
Bobty 12:c14ffd4ec125 363
Bobty 6:46285c519af2 364 const int HTTPD_MAX_FNAME_LENGTH = 127;
Bobty 6:46285c519af2 365 char localFilename[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 366 char reqFileNameStr[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 367
Bobty 11:cec51b430b20 368 RD_INFO("Requesting local file %s\n\r", inFileName);
Bobty 6:46285c519af2 369 sprintf(reqFileNameStr, "/%s", inFileName);
Bobty 6:46285c519af2 370 sprintf(localFilename, "/local/%s", inFileName);
Bobty 6:46285c519af2 371
Bobty 6:46285c519af2 372 if (bCacheIfPossible)
Bobty 6:46285c519af2 373 {
Bobty 6:46285c519af2 374 // Check if file already cached
Bobty 6:46285c519af2 375 bool bTryToCache = true;
Bobty 6:46285c519af2 376 for (std::vector<RdFileCacheEntry*>::iterator it = _cachedFiles.begin() ; it != _cachedFiles.end(); ++it)
Bobty 6:46285c519af2 377 {
Bobty 6:46285c519af2 378 if (strcmp(inFileName, (*it)->_fileName) == 0)
Bobty 6:46285c519af2 379 {
Bobty 6:46285c519af2 380 if ((*it)->_bCacheValid)
Bobty 6:46285c519af2 381 {
Bobty 15:0865fa4b046a 382 sendFromCache(*it, clientSocketConn, httpHeader);
Bobty 17:080f2bed8b36 383 return true;
Bobty 6:46285c519af2 384 }
Bobty 6:46285c519af2 385 bTryToCache = false; // don't try to cache as cacheing must have already failed
Bobty 6:46285c519af2 386 }
Bobty 6:46285c519af2 387 }
Bobty 6:46285c519af2 388
Bobty 6:46285c519af2 389 // See if we can cache the file
Bobty 6:46285c519af2 390 if (bTryToCache)
Bobty 6:46285c519af2 391 {
Bobty 6:46285c519af2 392 RdFileCacheEntry* pCacheEntry = new RdFileCacheEntry(inFileName);
Bobty 6:46285c519af2 393 if (pCacheEntry)
Bobty 6:46285c519af2 394 {
Bobty 6:46285c519af2 395 bool bCacheSuccess = pCacheEntry->readLocalFileIntoCache(localFilename);
Bobty 6:46285c519af2 396 // Store the cache entry even if reading failed (mem alloc or file not found, etc) so we don't try to cache again
Bobty 6:46285c519af2 397 _cachedFiles.push_back(pCacheEntry);
Bobty 6:46285c519af2 398 if (bCacheSuccess)
Bobty 6:46285c519af2 399 {
Bobty 15:0865fa4b046a 400 sendFromCache(pCacheEntry, clientSocketConn, httpHeader);
Bobty 17:080f2bed8b36 401 return true;
Bobty 6:46285c519af2 402 }
Bobty 6:46285c519af2 403 }
Bobty 6:46285c519af2 404 }
Bobty 6:46285c519af2 405 }
Bobty 6:46285c519af2 406
Bobty 6:46285c519af2 407 LocalFileSystem local("local");
Bobty 6:46285c519af2 408
Bobty 6:46285c519af2 409 FILE* fp = fopen(localFilename, "r");
Bobty 6:46285c519af2 410 if (fp == NULL)
Bobty 6:46285c519af2 411 {
Bobty 11:cec51b430b20 412 RD_WARN("Local file %s not found\r\n", localFilename);
Bobty 17:080f2bed8b36 413 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 17:080f2bed8b36 414 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 415 return true;
Bobty 6:46285c519af2 416 }
Bobty 6:46285c519af2 417 else
Bobty 6:46285c519af2 418 {
Bobty 11:cec51b430b20 419 RD_INFO("Sending file %s from disk\r\n", localFilename);
Bobty 17:080f2bed8b36 420 // Find file length
Bobty 17:080f2bed8b36 421 fseek(fp, 0L, SEEK_END);
Bobty 17:080f2bed8b36 422 int sz = ftell(fp);
Bobty 17:080f2bed8b36 423 fseek(fp, 0L, SEEK_SET);
Bobty 17:080f2bed8b36 424 // Get mime type
Bobty 17:080f2bed8b36 425 char* mimeType = getMimeTypeStr(localFilename);
Bobty 18:5de680c4cfcb 426 RD_DBG("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 427 // Form header
Bobty 17:080f2bed8b36 428 formHTTPHeader("200 OK", mimeType, sz);
Bobty 17:080f2bed8b36 429 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 430 int rdCnt = 0;
Bobty 17:080f2bed8b36 431 while ((rdCnt = fread(_httpHeader, sizeof( char ), sizeof(_httpHeader), fp)) == sizeof(_httpHeader))
Bobty 6:46285c519af2 432 {
Bobty 17:080f2bed8b36 433 clientSocketConn.send(_httpHeader, rdCnt);
Bobty 6:46285c519af2 434 }
Bobty 17:080f2bed8b36 435 if (rdCnt != 0)
Bobty 17:080f2bed8b36 436 clientSocketConn.send(_httpHeader, rdCnt);
Bobty 6:46285c519af2 437 fclose(fp);
Bobty 17:080f2bed8b36 438 return true;
Bobty 6:46285c519af2 439 }
Bobty 12:c14ffd4ec125 440
Bobty 12:c14ffd4ec125 441 #else
Bobty 12:c14ffd4ec125 442
Bobty 12:c14ffd4ec125 443 RD_WARN("Local file system not supported\r\n");
Bobty 17:080f2bed8b36 444 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 17:080f2bed8b36 445 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 446 return true;
Bobty 12:c14ffd4ec125 447
Bobty 12:c14ffd4ec125 448 #endif
Bobty 6:46285c519af2 449 }
Bobty 6:46285c519af2 450
Bobty 18:5de680c4cfcb 451 // Read file into cache
Bobty 15:0865fa4b046a 452 #ifdef SUPPORT_LOCAL_FILE_CACHE
Bobty 6:46285c519af2 453 bool RdFileCacheEntry::readLocalFileIntoCache(char* fileName)
Bobty 6:46285c519af2 454 {
Bobty 12:c14ffd4ec125 455 #ifdef SUPPORT_LOCAL_FILESYSTEM
Bobty 12:c14ffd4ec125 456
Bobty 11:cec51b430b20 457 RD_INFO("Reading into cache %s\n\r", fileName);
Bobty 6:46285c519af2 458 LocalFileSystem local("local");
Bobty 6:46285c519af2 459 FILE* fp = fopen(fileName, "r");
Bobty 6:46285c519af2 460 if (fp == NULL)
Bobty 6:46285c519af2 461 {
Bobty 11:cec51b430b20 462 RD_WARN("Failed to open file\n\r");
Bobty 6:46285c519af2 463 return false;
Bobty 6:46285c519af2 464 }
Bobty 11:cec51b430b20 465 RD_DBG("Seeking\n\r");
Bobty 6:46285c519af2 466 fseek(fp, 0, SEEK_END);
Bobty 6:46285c519af2 467 _nFileLen = (int)ftell(fp);
Bobty 6:46285c519af2 468 _pFileContent = new char[_nFileLen];
Bobty 11:cec51b430b20 469 RD_DBG("Len %d Buf %08x\n\r", _nFileLen, _pFileContent);
Bobty 6:46285c519af2 470 if (!_pFileContent)
Bobty 6:46285c519af2 471 {
Bobty 11:cec51b430b20 472 RD_WARN("Failed to allocate %lu\n\r", _nFileLen);
Bobty 6:46285c519af2 473 fclose(fp);
Bobty 6:46285c519af2 474 return false;
Bobty 6:46285c519af2 475 }
Bobty 11:cec51b430b20 476 RD_DBG("Allocated\n\r");
Bobty 6:46285c519af2 477 memset(_pFileContent, 0, _nFileLen);
Bobty 6:46285c519af2 478 fseek(fp, 0, SEEK_SET);
Bobty 6:46285c519af2 479 char* pMem = _pFileContent;
Bobty 6:46285c519af2 480 char fileBuf[100];
Bobty 6:46285c519af2 481 int totCnt = 0;
Bobty 6:46285c519af2 482 int rdCnt = 0;
Bobty 11:cec51b430b20 483 RD_DBG("Reading\n\r");
Bobty 6:46285c519af2 484 while (true)
Bobty 6:46285c519af2 485 {
Bobty 6:46285c519af2 486 int toRead = _nFileLen - totCnt;
Bobty 6:46285c519af2 487 if (toRead > sizeof(fileBuf))
Bobty 6:46285c519af2 488 toRead = sizeof(fileBuf);
Bobty 6:46285c519af2 489 if (toRead <= 0)
Bobty 6:46285c519af2 490 break;
Bobty 6:46285c519af2 491 rdCnt = fread(fileBuf, sizeof(char), toRead, fp);
Bobty 6:46285c519af2 492 if (rdCnt <= 0)
Bobty 6:46285c519af2 493 break;
Bobty 11:cec51b430b20 494 RD_DBG("Read %d tot %d of %d\n\r", rdCnt, totCnt, _nFileLen);
Bobty 6:46285c519af2 495 memcpy(pMem, fileBuf, rdCnt);
Bobty 6:46285c519af2 496 pMem += rdCnt;
Bobty 6:46285c519af2 497 totCnt += rdCnt;
Bobty 6:46285c519af2 498 }
Bobty 11:cec51b430b20 499 RD_DBG("Done read\n\r");
Bobty 6:46285c519af2 500 fclose(fp);
Bobty 11:cec51b430b20 501 RD_DBG("Success in caching %d bytes (read %d)\n\r", _nFileLen, totCnt);
Bobty 6:46285c519af2 502 _bCacheValid = true;
Bobty 0:b5b4d07f7827 503 return true;
Bobty 12:c14ffd4ec125 504 #else
Bobty 12:c14ffd4ec125 505 return false;
Bobty 12:c14ffd4ec125 506 #endif
Bobty 0:b5b4d07f7827 507 }
Bobty 0:b5b4d07f7827 508
Bobty 15:0865fa4b046a 509 #endif
Bobty 15:0865fa4b046a 510
Bobty 18:5de680c4cfcb 511 // Extract arguments from command
Bobty 1:75bb184de749 512 bool RdWebServer::extractCmdArgs(char* buf, char* pCmdStr, int maxCmdStrLen, char* pArgStr, int maxArgStrLen)
Bobty 1:75bb184de749 513 {
Bobty 1:75bb184de749 514 *pCmdStr = '\0';
Bobty 1:75bb184de749 515 *pArgStr = '\0';
Bobty 1:75bb184de749 516 int cmdStrLen = 0;
Bobty 1:75bb184de749 517 int argStrLen = 0;
Bobty 1:75bb184de749 518 if (buf == NULL)
Bobty 1:75bb184de749 519 return false;
Bobty 7:fe7c33f7fbb8 520 // Check for first slash
Bobty 1:75bb184de749 521 char* pSlash1 = strchr(buf, '/');
Bobty 1:75bb184de749 522 if (pSlash1 == NULL)
Bobty 1:75bb184de749 523 return false;
Bobty 1:75bb184de749 524 pSlash1++;
Bobty 7:fe7c33f7fbb8 525 // Extract command
Bobty 1:75bb184de749 526 while(*pSlash1)
Bobty 1:75bb184de749 527 {
Bobty 1:75bb184de749 528 if (cmdStrLen >= maxCmdStrLen-1)
Bobty 1:75bb184de749 529 break;
Bobty 7:fe7c33f7fbb8 530 if ((*pSlash1 == '/') || (*pSlash1 == ' ') || (*pSlash1 == '\n') || (*pSlash1 == '?') || (*pSlash1 == '&'))
Bobty 1:75bb184de749 531 break;
Bobty 1:75bb184de749 532 *pCmdStr++ = *pSlash1++;
Bobty 1:75bb184de749 533 *pCmdStr = '\0';
Bobty 1:75bb184de749 534 cmdStrLen++;
Bobty 1:75bb184de749 535 }
Bobty 1:75bb184de749 536 if ((*pSlash1 == '\0') || (*pSlash1 == ' ') || (*pSlash1 == '\n'))
Bobty 1:75bb184de749 537 return true;
Bobty 7:fe7c33f7fbb8 538 // Now args
Bobty 1:75bb184de749 539 *pSlash1++;
Bobty 1:75bb184de749 540 while(*pSlash1)
Bobty 1:75bb184de749 541 {
Bobty 1:75bb184de749 542 if (argStrLen >= maxArgStrLen-1)
Bobty 1:75bb184de749 543 break;
Bobty 1:75bb184de749 544 if ((*pSlash1 == ' ') || (*pSlash1 == '\n'))
Bobty 1:75bb184de749 545 break;
Bobty 1:75bb184de749 546 *pArgStr++ = *pSlash1++;
Bobty 1:75bb184de749 547 *pArgStr = '\0';
Bobty 1:75bb184de749 548 argStrLen++;
Bobty 1:75bb184de749 549 }
Bobty 1:75bb184de749 550 return true;
Bobty 1:75bb184de749 551 }