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:
Tue May 12 22:16:25 2015 +0000
Revision:
19:f67ac231b570
Parent:
18:5de680c4cfcb
Child:
20:e6c7db867593
Allow larger buffer on K64F (more RAM), tidied up diagnostics and added code to get message payload

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 19:f67ac231b570 8 #define RDWEB_DEBUG 4
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 19:f67ac231b570 68 RD_WARN("TCP server bind fail");
Bobty 0:b5b4d07f7827 69 return false;
Bobty 0:b5b4d07f7827 70 }
Bobty 15:0865fa4b046a 71 if(_serverSocket.listen(1) < 0)
Bobty 0:b5b4d07f7827 72 {
Bobty 19:f67ac231b570 73 RD_WARN("TCP server listen fail");
Bobty 0:b5b4d07f7827 74 return false;
Bobty 0:b5b4d07f7827 75 }
Bobty 19:f67ac231b570 76 RD_INFO("TCP server is listening...");
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 19:f67ac231b570 93 RD_INFO("Waiting for TCP connection");
Bobty 17:080f2bed8b36 94 clientSocketConn.set_blocking(_blockingOnAccept, _timeoutOnBlocking);
Bobty 15:0865fa4b046a 95 if(_serverSocket.accept(clientSocketConn)<0)
Bobty 15:0865fa4b046a 96 {
Bobty 19:f67ac231b570 97 RD_WARN("TCP Socket failed to accept connection");
Bobty 15:0865fa4b046a 98 continue;
Bobty 15:0865fa4b046a 99 }
Bobty 15:0865fa4b046a 100
Bobty 15:0865fa4b046a 101 // Connection
Bobty 19:f67ac231b570 102 RD_INFO("Connection from IP: %s", 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 19:f67ac231b570 115 RD_DBG("clientSocketConn.receive() returned %d", rxLen);
Bobty 17:080f2bed8b36 116 if (_closeConnOnReceiveFail)
Bobty 15:0865fa4b046a 117 {
Bobty 15:0865fa4b046a 118 int closeRet = clientSocketConn.close();
Bobty 19:f67ac231b570 119 RD_DBG("Failed receive connection close() ret %d is connected %d", 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 19:f67ac231b570 126 RD_DBG("clientSocketConn.receive() returned %d - ignoring - is connected %d", 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 19:f67ac231b570 131 RD_DBG("clientSocketConn.receive() returned %d - too long", 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 _buffer[rxLen] = '\0';
Bobty 18:5de680c4cfcb 139 if (handleReceivedHttp(clientSocketConn))
Bobty 18:5de680c4cfcb 140 {
Bobty 18:5de680c4cfcb 141 // OK
Bobty 18:5de680c4cfcb 142 }
Bobty 18:5de680c4cfcb 143 else
Bobty 18:5de680c4cfcb 144 {
Bobty 18:5de680c4cfcb 145 // FAIL
Bobty 18:5de680c4cfcb 146 }
Bobty 15:0865fa4b046a 147
Bobty 17:080f2bed8b36 148 // Close connection if required
Bobty 17:080f2bed8b36 149 if (_closeConnAfterSend)
Bobty 15:0865fa4b046a 150 {
Bobty 15:0865fa4b046a 151 int closeRet = clientSocketConn.close();
Bobty 19:f67ac231b570 152 RD_DBG("After send connection close() ret %d is connected %d", closeRet, clientSocketConn.is_connected());
Bobty 16:0248bbfdb6c1 153 forcedClosed = true;
Bobty 15:0865fa4b046a 154 }
Bobty 15:0865fa4b046a 155 }
Bobty 15:0865fa4b046a 156 }
Bobty 15:0865fa4b046a 157 }
Bobty 18:5de680c4cfcb 158
Bobty 18:5de680c4cfcb 159 // Handle a request
Bobty 15:0865fa4b046a 160 bool RdWebServer::handleReceivedHttp(TCPSocketConnection &clientSocketConn)
Bobty 6:46285c519af2 161 {
Bobty 17:080f2bed8b36 162 bool handledOk = false;
Bobty 19:f67ac231b570 163 RD_DBG("Received Data: %d\n\r\n\r%.*s",strlen(_buffer),strlen(_buffer),_buffer);
Bobty 7:fe7c33f7fbb8 164 int method = METHOD_OTHER;
Bobty 6:46285c519af2 165 if (strncmp(_buffer, "GET ", 4) == 0)
Bobty 7:fe7c33f7fbb8 166 method = METHOD_GET;
Bobty 7:fe7c33f7fbb8 167 if (strncmp(_buffer, "POST", 4) == 0)
Bobty 7:fe7c33f7fbb8 168 method = METHOD_POST;
Bobty 7:fe7c33f7fbb8 169
Bobty 7:fe7c33f7fbb8 170 char cmdStr[MAX_CMDSTR_LEN];
Bobty 7:fe7c33f7fbb8 171 char argStr[MAX_ARGSTR_LEN];
Bobty 7:fe7c33f7fbb8 172 if (extractCmdArgs(_buffer+3, cmdStr, MAX_CMDSTR_LEN, argStr, MAX_ARGSTR_LEN))
Bobty 6:46285c519af2 173 {
Bobty 19:f67ac231b570 174 RD_DBG("CmdStr %s", cmdStr);
Bobty 19:f67ac231b570 175 RD_DBG("ArgStr %s", argStr);
Bobty 7:fe7c33f7fbb8 176 bool cmdFound = false;
Bobty 7:fe7c33f7fbb8 177 for (std::vector<RdWebServerCmdDef*>::iterator it = _commands.begin() ; it != _commands.end(); ++it)
Bobty 6:46285c519af2 178 {
Bobty 19:f67ac231b570 179 RD_DBG("Testing <<%s>> with <<%s>>", (*it)->_pCmdStr, cmdStr);
Bobty 7:fe7c33f7fbb8 180 if (strcasecmp((*it)->_pCmdStr, cmdStr) == 0)
Bobty 7:fe7c33f7fbb8 181 {
Bobty 19:f67ac231b570 182 RD_DBG("FoundCmd <%s> Type %d", cmdStr, (*it)->_cmdType);
Bobty 7:fe7c33f7fbb8 183 cmdFound = true;
Bobty 7:fe7c33f7fbb8 184 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
Bobty 7:fe7c33f7fbb8 185 {
Bobty 19:f67ac231b570 186 char* respStr = ((*it)->_callback)(method, cmdStr, argStr, _buffer);
Bobty 17:080f2bed8b36 187 clientSocketConn.send(respStr, strlen(respStr));
Bobty 6:46285c519af2 188 }
Bobty 17:080f2bed8b36 189 else if ( ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE) ||
Bobty 17:080f2bed8b36 190 ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE) )
Bobty 7:fe7c33f7fbb8 191 {
Bobty 17:080f2bed8b36 192 char combinedFileName[MAX_FILENAME_LEN];
Bobty 17:080f2bed8b36 193 strcpy(combinedFileName, (*it)->_substFileName);
Bobty 17:080f2bed8b36 194 if (strlen(combinedFileName) == 0)
Bobty 17:080f2bed8b36 195 strcpy(combinedFileName, cmdStr);
Bobty 17:080f2bed8b36 196 else if (combinedFileName[strlen(combinedFileName)-1] == '*')
Bobty 17:080f2bed8b36 197 strcpy(combinedFileName+strlen(combinedFileName)-1, argStr);
Bobty 17:080f2bed8b36 198 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
Bobty 17:080f2bed8b36 199 handledOk = handleLocalFileRequest(combinedFileName, argStr, clientSocketConn, (*it)->_bCacheIfPossible);
Bobty 7:fe7c33f7fbb8 200 else
Bobty 17:080f2bed8b36 201 handledOk = handleSDFileRequest(combinedFileName, argStr, clientSocketConn);
Bobty 17:080f2bed8b36 202
Bobty 7:fe7c33f7fbb8 203 }
Bobty 7:fe7c33f7fbb8 204 break;
Bobty 6:46285c519af2 205 }
Bobty 6:46285c519af2 206 }
Bobty 7:fe7c33f7fbb8 207 // If command not found see if it is a local file
Bobty 7:fe7c33f7fbb8 208 if (!cmdFound)
Bobty 17:080f2bed8b36 209 {
Bobty 17:080f2bed8b36 210 char combinedFileName[MAX_FILENAME_LEN];
Bobty 17:080f2bed8b36 211 strcpy(combinedFileName, cmdStr);
Bobty 17:080f2bed8b36 212 strcat(combinedFileName, "/");
Bobty 17:080f2bed8b36 213 strcat(combinedFileName, argStr);
Bobty 17:080f2bed8b36 214 handledOk = handleSDFileRequest(cmdStr, argStr, clientSocketConn);
Bobty 17:080f2bed8b36 215 }
Bobty 6:46285c519af2 216 }
Bobty 17:080f2bed8b36 217 return handledOk;
Bobty 6:46285c519af2 218 }
Bobty 6:46285c519af2 219
Bobty 18:5de680c4cfcb 220 // Add a command to the server
Bobty 6:46285c519af2 221 void RdWebServer::addCommand(char* pCmdStr, int cmdType, CmdCallbackType callback, char* substFileName, bool cacheIfPossible)
Bobty 6:46285c519af2 222 {
Bobty 6:46285c519af2 223 _commands.push_back(new RdWebServerCmdDef(pCmdStr, cmdType, callback, substFileName, cacheIfPossible));
Bobty 6:46285c519af2 224 }
Bobty 6:46285c519af2 225
Bobty 18:5de680c4cfcb 226 // Form a header to respond
Bobty 17:080f2bed8b36 227 void RdWebServer::formHTTPHeader(const char* rsltCode, const char* contentType, int contentLen)
Bobty 17:080f2bed8b36 228 {
Bobty 17:080f2bed8b36 229 const char* closeConnStr = "\r\nConnection: Close";
Bobty 17:080f2bed8b36 230 if(contentLen != -1)
Bobty 17:080f2bed8b36 231 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 232 else
Bobty 17:080f2bed8b36 233 sprintf(_httpHeader, "HTTP/1.1 %s\r\nContent-Type: %s%s\r\n\r\n", rsltCode, contentType, _closeConnAfterSend ? closeConnStr : "");
Bobty 17:080f2bed8b36 234 }
Bobty 18:5de680c4cfcb 235
Bobty 18:5de680c4cfcb 236 // Use file extension to determine MIME type
Bobty 17:080f2bed8b36 237 char* getMimeTypeStr(char* filename)
Bobty 6:46285c519af2 238 {
Bobty 17:080f2bed8b36 239 char* mimeType = "text/plain";
Bobty 17:080f2bed8b36 240 char *pDot = strrchr(filename, '.');
Bobty 17:080f2bed8b36 241 if (pDot && (!strcmp(pDot, ".html") || !strcmp(pDot, ".htm")))
Bobty 17:080f2bed8b36 242 mimeType = "text/html";
Bobty 17:080f2bed8b36 243 else if (pDot && !strcmp(pDot, ".css"))
Bobty 17:080f2bed8b36 244 mimeType = "text/css";
Bobty 17:080f2bed8b36 245 else if (pDot && !strcmp(pDot, ".js"))
Bobty 17:080f2bed8b36 246 mimeType = "application/javascript";
Bobty 17:080f2bed8b36 247 else if (pDot && !strcmp(pDot, ".png"))
Bobty 17:080f2bed8b36 248 mimeType = "image/png";
Bobty 17:080f2bed8b36 249 else if (pDot && (!strcmp(pDot, ".jpeg") || !strcmp(pDot, ".jpeg")))
Bobty 17:080f2bed8b36 250 mimeType = "image/jpeg";
Bobty 17:080f2bed8b36 251 return mimeType;
Bobty 17:080f2bed8b36 252 }
Bobty 18:5de680c4cfcb 253
Bobty 18:5de680c4cfcb 254 // Handle request for files/listing from SDCard
Bobty 17:080f2bed8b36 255 bool RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn)
Bobty 17:080f2bed8b36 256 {
Bobty 17:080f2bed8b36 257 bool handledOk = false;
Bobty 6:46285c519af2 258 const int HTTPD_MAX_FNAME_LENGTH = 127;
Bobty 6:46285c519af2 259 char filename[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 260
Bobty 19:f67ac231b570 261 RD_INFO("Requesting file %s", inFileName);
Bobty 6:46285c519af2 262
Bobty 12:c14ffd4ec125 263 #ifdef SUPPORT_FOLDER_VIEW
Bobty 10:b4b9d4d5e5be 264 if ((strlen(inFileName) > 0) && (inFileName[strlen(inFileName)-1] == '/'))
Bobty 6:46285c519af2 265 {
Bobty 19:f67ac231b570 266 RD_INFO("Request directory %s%s", _pBaseWebFolder, inFileName);
Bobty 6:46285c519af2 267 sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
Bobty 6:46285c519af2 268 DIR *d = opendir(filename);
Bobty 11:cec51b430b20 269 if (d != NULL)
Bobty 11:cec51b430b20 270 {
Bobty 17:080f2bed8b36 271 formHTTPHeader("200 OK", "text/html", -1);
Bobty 17:080f2bed8b36 272 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 273 sprintf(_httpHeader,"<html><head><title>Directory Listing</title></head><body><h1>%s</h1><ul>", inFileName);
Bobty 17:080f2bed8b36 274 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 275 struct dirent *p;
Bobty 11:cec51b430b20 276 while((p = readdir(d)) != NULL)
Bobty 11:cec51b430b20 277 {
Bobty 19:f67ac231b570 278 RD_INFO("%s", p->d_name);
Bobty 17:080f2bed8b36 279 sprintf(_httpHeader,"<li>%s</li>", p->d_name);
Bobty 17:080f2bed8b36 280 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 281 }
Bobty 6:46285c519af2 282 }
Bobty 6:46285c519af2 283 closedir(d);
Bobty 11:cec51b430b20 284 RD_DBG("Directory closed\n");
Bobty 17:080f2bed8b36 285 sprintf(_httpHeader,"</ul></body></html>");
Bobty 17:080f2bed8b36 286 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 287 handledOk = true;
Bobty 6:46285c519af2 288 }
Bobty 6:46285c519af2 289 else
Bobty 12:c14ffd4ec125 290 #endif
Bobty 6:46285c519af2 291 {
Bobty 6:46285c519af2 292 sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
Bobty 19:f67ac231b570 293 RD_INFO("Filename %s", filename);
Bobty 6:46285c519af2 294
Bobty 6:46285c519af2 295 FILE* fp = fopen(filename, "r");
Bobty 6:46285c519af2 296 if (fp == NULL)
Bobty 6:46285c519af2 297 {
Bobty 19:f67ac231b570 298 RD_WARN("Filename %s not found", filename);
Bobty 17:080f2bed8b36 299 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 17:080f2bed8b36 300 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 301 handledOk = true;
Bobty 6:46285c519af2 302 }
Bobty 6:46285c519af2 303 else
Bobty 6:46285c519af2 304 {
Bobty 19:f67ac231b570 305 RD_INFO("Sending file %s", filename);
Bobty 17:080f2bed8b36 306 // Find file length
Bobty 14:4b83670854f0 307 fseek(fp, 0L, SEEK_END);
Bobty 14:4b83670854f0 308 int sz = ftell(fp);
Bobty 14:4b83670854f0 309 fseek(fp, 0L, SEEK_SET);
Bobty 17:080f2bed8b36 310 // Get mime type
Bobty 17:080f2bed8b36 311 char* mimeType = getMimeTypeStr(filename);
Bobty 18:5de680c4cfcb 312 RD_DBG("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 313 // Form header
Bobty 17:080f2bed8b36 314 formHTTPHeader("200 OK", mimeType, sz);
Bobty 17:080f2bed8b36 315 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 14:4b83670854f0 316 // Read file in blocks and send
Bobty 6:46285c519af2 317 int rdCnt = 0;
Bobty 6:46285c519af2 318 char fileBuf[1024];
Bobty 6:46285c519af2 319 while ((rdCnt = fread(fileBuf, sizeof( char ), 1024, fp)) == 1024)
Bobty 6:46285c519af2 320 {
Bobty 17:080f2bed8b36 321 clientSocketConn.send(fileBuf, rdCnt);
Bobty 6:46285c519af2 322 }
Bobty 17:080f2bed8b36 323 clientSocketConn.send(fileBuf, rdCnt);
Bobty 6:46285c519af2 324 fclose(fp);
Bobty 17:080f2bed8b36 325 handledOk = true;
Bobty 6:46285c519af2 326 }
Bobty 6:46285c519af2 327 }
Bobty 17:080f2bed8b36 328 return handledOk;
Bobty 6:46285c519af2 329 }
Bobty 6:46285c519af2 330
Bobty 18:5de680c4cfcb 331 // Send a file from cache - used for local files only
Bobty 15:0865fa4b046a 332 #ifdef SUPPORT_LOCAL_FILE_CACHE
Bobty 17:080f2bed8b36 333 void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &clientSocketConn)
Bobty 6:46285c519af2 334 {
Bobty 19:f67ac231b570 335 RD_INFO("Sending file %s from cache %d bytes", pCacheEntry->_fileName, pCacheEntry->_nFileLen);
Bobty 17:080f2bed8b36 336 // Get mime type
Bobty 17:080f2bed8b36 337 char* mimeType = getMimeTypeStr(pCacheEntry->_fileName);
Bobty 17:080f2bed8b36 338 RD_INFO("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 339 // Form header
Bobty 17:080f2bed8b36 340 formHTTPHeader("200 OK", mimeType, pCacheEntry->_nFileLen);
Bobty 17:080f2bed8b36 341 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 342 char* pMem = pCacheEntry->_pFileContent;
Bobty 6:46285c519af2 343 int nLenLeft = pCacheEntry->_nFileLen;
Bobty 6:46285c519af2 344 int blkSize = 2048;
Bobty 6:46285c519af2 345 while(nLenLeft > 0)
Bobty 6:46285c519af2 346 {
Bobty 6:46285c519af2 347 if (blkSize > nLenLeft)
Bobty 6:46285c519af2 348 blkSize = nLenLeft;
Bobty 17:080f2bed8b36 349 clientSocketConn.send(pMem, blkSize);
Bobty 6:46285c519af2 350 pMem += blkSize;
Bobty 6:46285c519af2 351 nLenLeft -= blkSize;
Bobty 6:46285c519af2 352 }
Bobty 6:46285c519af2 353 }
Bobty 15:0865fa4b046a 354 #endif
Bobty 6:46285c519af2 355
Bobty 18:5de680c4cfcb 356 // Handle a request for a local file
Bobty 17:080f2bed8b36 357 bool RdWebServer::handleLocalFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn, bool bCacheIfPossible)
Bobty 6:46285c519af2 358 {
Bobty 12:c14ffd4ec125 359
Bobty 12:c14ffd4ec125 360 #ifdef SUPPORT_LOCAL_FILESYSTEM
Bobty 12:c14ffd4ec125 361
Bobty 6:46285c519af2 362 const int HTTPD_MAX_FNAME_LENGTH = 127;
Bobty 6:46285c519af2 363 char localFilename[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 364 char reqFileNameStr[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 365
Bobty 19:f67ac231b570 366 RD_INFO("Requesting local file %s", inFileName);
Bobty 6:46285c519af2 367 sprintf(reqFileNameStr, "/%s", inFileName);
Bobty 6:46285c519af2 368 sprintf(localFilename, "/local/%s", inFileName);
Bobty 6:46285c519af2 369
Bobty 6:46285c519af2 370 if (bCacheIfPossible)
Bobty 6:46285c519af2 371 {
Bobty 6:46285c519af2 372 // Check if file already cached
Bobty 6:46285c519af2 373 bool bTryToCache = true;
Bobty 6:46285c519af2 374 for (std::vector<RdFileCacheEntry*>::iterator it = _cachedFiles.begin() ; it != _cachedFiles.end(); ++it)
Bobty 6:46285c519af2 375 {
Bobty 6:46285c519af2 376 if (strcmp(inFileName, (*it)->_fileName) == 0)
Bobty 6:46285c519af2 377 {
Bobty 6:46285c519af2 378 if ((*it)->_bCacheValid)
Bobty 6:46285c519af2 379 {
Bobty 15:0865fa4b046a 380 sendFromCache(*it, clientSocketConn, httpHeader);
Bobty 17:080f2bed8b36 381 return true;
Bobty 6:46285c519af2 382 }
Bobty 6:46285c519af2 383 bTryToCache = false; // don't try to cache as cacheing must have already failed
Bobty 6:46285c519af2 384 }
Bobty 6:46285c519af2 385 }
Bobty 6:46285c519af2 386
Bobty 6:46285c519af2 387 // See if we can cache the file
Bobty 6:46285c519af2 388 if (bTryToCache)
Bobty 6:46285c519af2 389 {
Bobty 6:46285c519af2 390 RdFileCacheEntry* pCacheEntry = new RdFileCacheEntry(inFileName);
Bobty 6:46285c519af2 391 if (pCacheEntry)
Bobty 6:46285c519af2 392 {
Bobty 6:46285c519af2 393 bool bCacheSuccess = pCacheEntry->readLocalFileIntoCache(localFilename);
Bobty 6:46285c519af2 394 // 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 395 _cachedFiles.push_back(pCacheEntry);
Bobty 6:46285c519af2 396 if (bCacheSuccess)
Bobty 6:46285c519af2 397 {
Bobty 15:0865fa4b046a 398 sendFromCache(pCacheEntry, clientSocketConn, httpHeader);
Bobty 17:080f2bed8b36 399 return true;
Bobty 6:46285c519af2 400 }
Bobty 6:46285c519af2 401 }
Bobty 6:46285c519af2 402 }
Bobty 6:46285c519af2 403 }
Bobty 6:46285c519af2 404
Bobty 6:46285c519af2 405 LocalFileSystem local("local");
Bobty 6:46285c519af2 406
Bobty 6:46285c519af2 407 FILE* fp = fopen(localFilename, "r");
Bobty 6:46285c519af2 408 if (fp == NULL)
Bobty 6:46285c519af2 409 {
Bobty 19:f67ac231b570 410 RD_WARN("Local file %s not found", localFilename);
Bobty 17:080f2bed8b36 411 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 17:080f2bed8b36 412 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 413 return true;
Bobty 6:46285c519af2 414 }
Bobty 6:46285c519af2 415 else
Bobty 6:46285c519af2 416 {
Bobty 19:f67ac231b570 417 RD_INFO("Sending file %s from disk", localFilename);
Bobty 17:080f2bed8b36 418 // Find file length
Bobty 17:080f2bed8b36 419 fseek(fp, 0L, SEEK_END);
Bobty 17:080f2bed8b36 420 int sz = ftell(fp);
Bobty 17:080f2bed8b36 421 fseek(fp, 0L, SEEK_SET);
Bobty 17:080f2bed8b36 422 // Get mime type
Bobty 17:080f2bed8b36 423 char* mimeType = getMimeTypeStr(localFilename);
Bobty 18:5de680c4cfcb 424 RD_DBG("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 425 // Form header
Bobty 17:080f2bed8b36 426 formHTTPHeader("200 OK", mimeType, sz);
Bobty 17:080f2bed8b36 427 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 428 int rdCnt = 0;
Bobty 17:080f2bed8b36 429 while ((rdCnt = fread(_httpHeader, sizeof( char ), sizeof(_httpHeader), fp)) == sizeof(_httpHeader))
Bobty 6:46285c519af2 430 {
Bobty 17:080f2bed8b36 431 clientSocketConn.send(_httpHeader, rdCnt);
Bobty 6:46285c519af2 432 }
Bobty 17:080f2bed8b36 433 if (rdCnt != 0)
Bobty 17:080f2bed8b36 434 clientSocketConn.send(_httpHeader, rdCnt);
Bobty 6:46285c519af2 435 fclose(fp);
Bobty 17:080f2bed8b36 436 return true;
Bobty 6:46285c519af2 437 }
Bobty 12:c14ffd4ec125 438
Bobty 12:c14ffd4ec125 439 #else
Bobty 12:c14ffd4ec125 440
Bobty 19:f67ac231b570 441 RD_WARN("Local file system not supported");
Bobty 17:080f2bed8b36 442 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 17:080f2bed8b36 443 clientSocketConn.send(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 444 return true;
Bobty 12:c14ffd4ec125 445
Bobty 12:c14ffd4ec125 446 #endif
Bobty 6:46285c519af2 447 }
Bobty 6:46285c519af2 448
Bobty 18:5de680c4cfcb 449 // Read file into cache
Bobty 15:0865fa4b046a 450 #ifdef SUPPORT_LOCAL_FILE_CACHE
Bobty 6:46285c519af2 451 bool RdFileCacheEntry::readLocalFileIntoCache(char* fileName)
Bobty 6:46285c519af2 452 {
Bobty 12:c14ffd4ec125 453 #ifdef SUPPORT_LOCAL_FILESYSTEM
Bobty 12:c14ffd4ec125 454
Bobty 19:f67ac231b570 455 RD_INFO("Reading into cache %s", fileName);
Bobty 6:46285c519af2 456 LocalFileSystem local("local");
Bobty 6:46285c519af2 457 FILE* fp = fopen(fileName, "r");
Bobty 6:46285c519af2 458 if (fp == NULL)
Bobty 6:46285c519af2 459 {
Bobty 19:f67ac231b570 460 RD_WARN("Failed to open file");
Bobty 6:46285c519af2 461 return false;
Bobty 6:46285c519af2 462 }
Bobty 19:f67ac231b570 463 RD_DBG("Seeking");
Bobty 6:46285c519af2 464 fseek(fp, 0, SEEK_END);
Bobty 6:46285c519af2 465 _nFileLen = (int)ftell(fp);
Bobty 6:46285c519af2 466 _pFileContent = new char[_nFileLen];
Bobty 19:f67ac231b570 467 RD_DBG("Len %d Buf %08x", _nFileLen, _pFileContent);
Bobty 6:46285c519af2 468 if (!_pFileContent)
Bobty 6:46285c519af2 469 {
Bobty 19:f67ac231b570 470 RD_WARN("Failed to allocate %lu", _nFileLen);
Bobty 6:46285c519af2 471 fclose(fp);
Bobty 6:46285c519af2 472 return false;
Bobty 6:46285c519af2 473 }
Bobty 19:f67ac231b570 474 RD_DBG("Allocated");
Bobty 6:46285c519af2 475 memset(_pFileContent, 0, _nFileLen);
Bobty 6:46285c519af2 476 fseek(fp, 0, SEEK_SET);
Bobty 6:46285c519af2 477 char* pMem = _pFileContent;
Bobty 6:46285c519af2 478 char fileBuf[100];
Bobty 6:46285c519af2 479 int totCnt = 0;
Bobty 6:46285c519af2 480 int rdCnt = 0;
Bobty 19:f67ac231b570 481 RD_DBG("Reading");
Bobty 6:46285c519af2 482 while (true)
Bobty 6:46285c519af2 483 {
Bobty 6:46285c519af2 484 int toRead = _nFileLen - totCnt;
Bobty 6:46285c519af2 485 if (toRead > sizeof(fileBuf))
Bobty 6:46285c519af2 486 toRead = sizeof(fileBuf);
Bobty 6:46285c519af2 487 if (toRead <= 0)
Bobty 6:46285c519af2 488 break;
Bobty 6:46285c519af2 489 rdCnt = fread(fileBuf, sizeof(char), toRead, fp);
Bobty 6:46285c519af2 490 if (rdCnt <= 0)
Bobty 6:46285c519af2 491 break;
Bobty 19:f67ac231b570 492 RD_DBG("Read %d tot %d of %d", rdCnt, totCnt, _nFileLen);
Bobty 6:46285c519af2 493 memcpy(pMem, fileBuf, rdCnt);
Bobty 6:46285c519af2 494 pMem += rdCnt;
Bobty 6:46285c519af2 495 totCnt += rdCnt;
Bobty 6:46285c519af2 496 }
Bobty 19:f67ac231b570 497 RD_DBG("Done read");
Bobty 6:46285c519af2 498 fclose(fp);
Bobty 19:f67ac231b570 499 RD_DBG("Success in caching %d bytes (read %d)", _nFileLen, totCnt);
Bobty 6:46285c519af2 500 _bCacheValid = true;
Bobty 0:b5b4d07f7827 501 return true;
Bobty 12:c14ffd4ec125 502 #else
Bobty 12:c14ffd4ec125 503 return false;
Bobty 12:c14ffd4ec125 504 #endif
Bobty 0:b5b4d07f7827 505 }
Bobty 0:b5b4d07f7827 506
Bobty 15:0865fa4b046a 507 #endif
Bobty 15:0865fa4b046a 508
Bobty 18:5de680c4cfcb 509 // Extract arguments from command
Bobty 1:75bb184de749 510 bool RdWebServer::extractCmdArgs(char* buf, char* pCmdStr, int maxCmdStrLen, char* pArgStr, int maxArgStrLen)
Bobty 1:75bb184de749 511 {
Bobty 1:75bb184de749 512 *pCmdStr = '\0';
Bobty 1:75bb184de749 513 *pArgStr = '\0';
Bobty 1:75bb184de749 514 int cmdStrLen = 0;
Bobty 1:75bb184de749 515 int argStrLen = 0;
Bobty 1:75bb184de749 516 if (buf == NULL)
Bobty 1:75bb184de749 517 return false;
Bobty 7:fe7c33f7fbb8 518 // Check for first slash
Bobty 1:75bb184de749 519 char* pSlash1 = strchr(buf, '/');
Bobty 1:75bb184de749 520 if (pSlash1 == NULL)
Bobty 1:75bb184de749 521 return false;
Bobty 1:75bb184de749 522 pSlash1++;
Bobty 7:fe7c33f7fbb8 523 // Extract command
Bobty 1:75bb184de749 524 while(*pSlash1)
Bobty 1:75bb184de749 525 {
Bobty 1:75bb184de749 526 if (cmdStrLen >= maxCmdStrLen-1)
Bobty 1:75bb184de749 527 break;
Bobty 7:fe7c33f7fbb8 528 if ((*pSlash1 == '/') || (*pSlash1 == ' ') || (*pSlash1 == '\n') || (*pSlash1 == '?') || (*pSlash1 == '&'))
Bobty 1:75bb184de749 529 break;
Bobty 1:75bb184de749 530 *pCmdStr++ = *pSlash1++;
Bobty 1:75bb184de749 531 *pCmdStr = '\0';
Bobty 1:75bb184de749 532 cmdStrLen++;
Bobty 1:75bb184de749 533 }
Bobty 1:75bb184de749 534 if ((*pSlash1 == '\0') || (*pSlash1 == ' ') || (*pSlash1 == '\n'))
Bobty 1:75bb184de749 535 return true;
Bobty 7:fe7c33f7fbb8 536 // Now args
Bobty 1:75bb184de749 537 *pSlash1++;
Bobty 1:75bb184de749 538 while(*pSlash1)
Bobty 1:75bb184de749 539 {
Bobty 1:75bb184de749 540 if (argStrLen >= maxArgStrLen-1)
Bobty 1:75bb184de749 541 break;
Bobty 1:75bb184de749 542 if ((*pSlash1 == ' ') || (*pSlash1 == '\n'))
Bobty 1:75bb184de749 543 break;
Bobty 1:75bb184de749 544 *pArgStr++ = *pSlash1++;
Bobty 1:75bb184de749 545 *pArgStr = '\0';
Bobty 1:75bb184de749 546 argStrLen++;
Bobty 1:75bb184de749 547 }
Bobty 1:75bb184de749 548 return true;
Bobty 1:75bb184de749 549 }
Bobty 19:f67ac231b570 550
Bobty 19:f67ac231b570 551 char* RdWebServer::getPayloadDataFromMsg(char* msgBuf)
Bobty 19:f67ac231b570 552 {
Bobty 19:f67ac231b570 553 char* ptr = strstr(msgBuf, "\r\n\r\n");
Bobty 19:f67ac231b570 554 if (ptr)
Bobty 19:f67ac231b570 555 return ptr+4;
Bobty 19:f67ac231b570 556 return "\0";
Bobty 19:f67ac231b570 557 }