A simple web server mainly based on ideas from Jasper Schuurmans Netduino web server

Dependents:   RdBlindsServer SpideyWallWeb RdGasUseMonitor

A fast and reliable web server for MBED! http://robdobson.com/2015/08/a-reliable-mbed-webserver/

It has a very neat way to implement REST commands and can serve files from local storage (on LPC1768 for instance) and from SD cards. It also has a caching facility which is particularly useful for serving files from local storage.

The server can be run in the main() thread (and has a sub-2ms response time if this is done) or in a mbed-rtos thread which increases the response time to (a still respectable) 30ms or so.

The latest project that uses this is here - https://developer.mbed.org/users/Bobty/code/SpideyWallWeb/

int main (void)
{
    // Ethernet interface
    EthernetInterface::init();

    // Connect ethernet
    EthernetInterface::connect();

    // Init the web server
    pc.printf("Starting web server\r\n");
    char* baseWebFolder = "/sd/";  // should be /sd/ for SDcard files - not used for local file system
    RdWebServer webServer;
    
    // Add commands to handle the home page and favicon
    webServer.addCommand("", RdWebServerCmdDef::CMD_LOCALFILE, NULL, "index.htm", true);
    webServer.addCommand("favicon.ico", RdWebServerCmdDef::CMD_LOCALFILE, NULL, NULL, true);
    
    // Add the lightwall control commands
    webServer.addCommand("name", RdWebServerCmdDef::CMD_CALLBACK, &lightwallGetSystemName);
    webServer.addCommand("clear", RdWebServerCmdDef::CMD_CALLBACK, &lightwallClear);
    webServer.addCommand("rawfill", RdWebServerCmdDef::CMD_CALLBACK, &lightwallRawFill);
    webServer.addCommand("fill", RdWebServerCmdDef::CMD_CALLBACK, &lightwallFill);
    webServer.addCommand("showleds", RdWebServerCmdDef::CMD_CALLBACK, &lightwallShowLeds);
    
    // Start the server
    webServer.init(WEBPORT, &led4, baseWebFolder);
    webServer.run();

}

// Get system name - No arguments required
char* lightwallGetSystemName(int method, char*cmdStr, char* argStr, char* msgBuffer, int msgLen, 
                int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
{
    // Perform any required actions here ....

    // ...

    // Return the system name
    return systemName;
}

This server was originally based on a Netduino web server from Jasper Schuurmans but has been optimised for speed.

Committer:
Bobty
Date:
Mon Aug 31 08:54:17 2015 +0000
Revision:
23:0fc3d7b5e596
Parent:
22:598a21373539
Child:
24:27800de38eab
Changed blocking options to default non-blocking; Detected OPTIONS HTTP method; Changed to always use keep-alive

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Bobty 20:e6c7db867593 1 // RdWebServer - Simple Web Server for MBED
Bobty 20:e6c7db867593 2 // Copyright (C) Rob Dobson 2013-2015, MIT License
Bobty 20:e6c7db867593 3 // Inspired by Jasper Schuurmans multi-threaded web server for Netduino which now seems to have gone from ...
Bobty 20:e6c7db867593 4 // http://www.schuurmans.cc/multi-threaded-web-server-for-netduino-plus
Bobty 20:e6c7db867593 5 // More details at http://robdobson.com/2013/10/moving-my-window-shades-control-to-mbed/
Bobty 3:594136d34a32 6
Bobty 18:5de680c4cfcb 7 // Setting RDWEB_DEBUG to 4 causes all debugging to be shown
Bobty 21:2dfb56648b93 8 #define RDWEB_DEBUG 2
Bobty 18:5de680c4cfcb 9
Bobty 18:5de680c4cfcb 10 // Change the settings below to support a local file system (not available on some MBEDs)
Bobty 21:2dfb56648b93 11 #define SUPPORT_LOCAL_FILESYSTEM 1
Bobty 21:2dfb56648b93 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 23:0fc3d7b5e596 47 // Non-blocking by default
Bobty 23:0fc3d7b5e596 48 _blockingOnAccept = false;
Bobty 23:0fc3d7b5e596 49 _blockingOnReceive = false;
Bobty 17:080f2bed8b36 50
Bobty 17:080f2bed8b36 51 // This is the same as the default in the socket.cpp file
Bobty 17:080f2bed8b36 52 _timeoutOnBlocking = 1500;
Bobty 17:080f2bed8b36 53
Bobty 17:080f2bed8b36 54 // If closeConnAfterSend is set false then the socket connection remains open and only
Bobty 23:0fc3d7b5e596 55 // one client can access the server until a time-out occurs - this should be ok for most applications
Bobty 23:0fc3d7b5e596 56 _closeConnAfterSend = false;
Bobty 17:080f2bed8b36 57 _closeConnOnReceiveFail = true;
Bobty 17:080f2bed8b36 58
Bobty 15:0865fa4b046a 59 // Setup tcp socket
Bobty 15:0865fa4b046a 60 if(_serverSocket.bind(port)< 0)
Bobty 0:b5b4d07f7827 61 {
Bobty 19:f67ac231b570 62 RD_WARN("TCP server bind fail");
Bobty 0:b5b4d07f7827 63 return false;
Bobty 0:b5b4d07f7827 64 }
Bobty 23:0fc3d7b5e596 65
Bobty 23:0fc3d7b5e596 66 // Only one connection at a time as the server is single-threaded
Bobty 15:0865fa4b046a 67 if(_serverSocket.listen(1) < 0)
Bobty 0:b5b4d07f7827 68 {
Bobty 19:f67ac231b570 69 RD_WARN("TCP server listen fail");
Bobty 0:b5b4d07f7827 70 return false;
Bobty 0:b5b4d07f7827 71 }
Bobty 19:f67ac231b570 72 RD_INFO("TCP server is listening...");
Bobty 15:0865fa4b046a 73 _initOk = true;
Bobty 6:46285c519af2 74 return true;
Bobty 6:46285c519af2 75 }
Bobty 6:46285c519af2 76
Bobty 18:5de680c4cfcb 77 // Run - never returns so run in a thread
Bobty 15:0865fa4b046a 78 void RdWebServer::run()
Bobty 15:0865fa4b046a 79 {
Bobty 15:0865fa4b046a 80 // Check initialised ok
Bobty 15:0865fa4b046a 81 if (!_initOk)
Bobty 15:0865fa4b046a 82 return;
Bobty 15:0865fa4b046a 83
Bobty 15:0865fa4b046a 84 // Start accepting connections
Bobty 15:0865fa4b046a 85 while (true)
Bobty 15:0865fa4b046a 86 {
Bobty 15:0865fa4b046a 87 TCPSocketConnection clientSocketConn;
Bobty 15:0865fa4b046a 88 // Accept connection if available
Bobty 19:f67ac231b570 89 RD_INFO("Waiting for TCP connection");
Bobty 17:080f2bed8b36 90 clientSocketConn.set_blocking(_blockingOnAccept, _timeoutOnBlocking);
Bobty 15:0865fa4b046a 91 if(_serverSocket.accept(clientSocketConn)<0)
Bobty 15:0865fa4b046a 92 {
Bobty 19:f67ac231b570 93 RD_WARN("TCP Socket failed to accept connection");
Bobty 15:0865fa4b046a 94 continue;
Bobty 15:0865fa4b046a 95 }
Bobty 15:0865fa4b046a 96
Bobty 15:0865fa4b046a 97 // Connection
Bobty 19:f67ac231b570 98 RD_INFO("Connection from IP: %s", clientSocketConn.get_address());
Bobty 15:0865fa4b046a 99 if (_pStatusLed != NULL)
Bobty 15:0865fa4b046a 100 *_pStatusLed = true;
Bobty 15:0865fa4b046a 101
Bobty 15:0865fa4b046a 102 // While connected
Bobty 15:0865fa4b046a 103 bool forcedClosed = false;
Bobty 15:0865fa4b046a 104 while(clientSocketConn.is_connected() && !forcedClosed)
Bobty 15:0865fa4b046a 105 {
Bobty 15:0865fa4b046a 106 // Receive data
Bobty 23:0fc3d7b5e596 107 if (_blockingOnReceive != _blockingOnAccept)
Bobty 23:0fc3d7b5e596 108 clientSocketConn.set_blocking(_blockingOnReceive, _timeoutOnBlocking);
Bobty 15:0865fa4b046a 109 int rxLen = clientSocketConn.receive(_buffer, HTTPD_MAX_REQ_LENGTH);
Bobty 15:0865fa4b046a 110 if (rxLen == -1)
Bobty 15:0865fa4b046a 111 {
Bobty 19:f67ac231b570 112 RD_DBG("clientSocketConn.receive() returned %d", rxLen);
Bobty 17:080f2bed8b36 113 if (_closeConnOnReceiveFail)
Bobty 15:0865fa4b046a 114 {
Bobty 15:0865fa4b046a 115 int closeRet = clientSocketConn.close();
Bobty 19:f67ac231b570 116 RD_DBG("Failed receive connection close() ret %d is connected %d", closeRet, clientSocketConn.is_connected());
Bobty 15:0865fa4b046a 117 forcedClosed = true;
Bobty 15:0865fa4b046a 118 }
Bobty 15:0865fa4b046a 119 continue;
Bobty 15:0865fa4b046a 120 }
Bobty 15:0865fa4b046a 121 if (rxLen == 0)
Bobty 15:0865fa4b046a 122 {
Bobty 19:f67ac231b570 123 RD_DBG("clientSocketConn.receive() returned %d - ignoring - is connected %d", rxLen, clientSocketConn.is_connected());
Bobty 15:0865fa4b046a 124 continue;
Bobty 15:0865fa4b046a 125 }
Bobty 15:0865fa4b046a 126 if (rxLen > HTTPD_MAX_REQ_LENGTH)
Bobty 15:0865fa4b046a 127 {
Bobty 19:f67ac231b570 128 RD_DBG("clientSocketConn.receive() returned %d - too long", rxLen);
Bobty 17:080f2bed8b36 129 formHTTPHeader("413 Request Entity Too Large", "text/plain", 0);
Bobty 23:0fc3d7b5e596 130 int sentRet = clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 15:0865fa4b046a 131 continue;
Bobty 15:0865fa4b046a 132 }
Bobty 18:5de680c4cfcb 133
Bobty 18:5de680c4cfcb 134 // Handle received message
Bobty 15:0865fa4b046a 135 _buffer[rxLen] = '\0';
Bobty 18:5de680c4cfcb 136 if (handleReceivedHttp(clientSocketConn))
Bobty 18:5de680c4cfcb 137 {
Bobty 18:5de680c4cfcb 138 // OK
Bobty 18:5de680c4cfcb 139 }
Bobty 18:5de680c4cfcb 140 else
Bobty 18:5de680c4cfcb 141 {
Bobty 18:5de680c4cfcb 142 // FAIL
Bobty 18:5de680c4cfcb 143 }
Bobty 15:0865fa4b046a 144
Bobty 17:080f2bed8b36 145 // Close connection if required
Bobty 17:080f2bed8b36 146 if (_closeConnAfterSend)
Bobty 15:0865fa4b046a 147 {
Bobty 15:0865fa4b046a 148 int closeRet = clientSocketConn.close();
Bobty 19:f67ac231b570 149 RD_DBG("After send connection close() ret %d is connected %d", closeRet, clientSocketConn.is_connected());
Bobty 16:0248bbfdb6c1 150 forcedClosed = true;
Bobty 15:0865fa4b046a 151 }
Bobty 15:0865fa4b046a 152 }
Bobty 15:0865fa4b046a 153 }
Bobty 15:0865fa4b046a 154 }
Bobty 18:5de680c4cfcb 155
Bobty 18:5de680c4cfcb 156 // Handle a request
Bobty 15:0865fa4b046a 157 bool RdWebServer::handleReceivedHttp(TCPSocketConnection &clientSocketConn)
Bobty 6:46285c519af2 158 {
Bobty 17:080f2bed8b36 159 bool handledOk = false;
Bobty 19:f67ac231b570 160 RD_DBG("Received Data: %d\n\r\n\r%.*s",strlen(_buffer),strlen(_buffer),_buffer);
Bobty 7:fe7c33f7fbb8 161 int method = METHOD_OTHER;
Bobty 6:46285c519af2 162 if (strncmp(_buffer, "GET ", 4) == 0)
Bobty 7:fe7c33f7fbb8 163 method = METHOD_GET;
Bobty 23:0fc3d7b5e596 164 else if (strncmp(_buffer, "POST", 4) == 0)
Bobty 7:fe7c33f7fbb8 165 method = METHOD_POST;
Bobty 23:0fc3d7b5e596 166 else if (strncmp(_buffer, "OPTIONS", 7) == 0)
Bobty 23:0fc3d7b5e596 167 method = METHOD_OPTIONS;
Bobty 7:fe7c33f7fbb8 168
Bobty 7:fe7c33f7fbb8 169 char cmdStr[MAX_CMDSTR_LEN];
Bobty 7:fe7c33f7fbb8 170 char argStr[MAX_ARGSTR_LEN];
Bobty 7:fe7c33f7fbb8 171 if (extractCmdArgs(_buffer+3, cmdStr, MAX_CMDSTR_LEN, argStr, MAX_ARGSTR_LEN))
Bobty 6:46285c519af2 172 {
Bobty 19:f67ac231b570 173 RD_DBG("CmdStr %s", cmdStr);
Bobty 19:f67ac231b570 174 RD_DBG("ArgStr %s", argStr);
Bobty 7:fe7c33f7fbb8 175 bool cmdFound = false;
Bobty 7:fe7c33f7fbb8 176 for (std::vector<RdWebServerCmdDef*>::iterator it = _commands.begin() ; it != _commands.end(); ++it)
Bobty 6:46285c519af2 177 {
Bobty 19:f67ac231b570 178 RD_DBG("Testing <<%s>> with <<%s>>", (*it)->_pCmdStr, cmdStr);
Bobty 7:fe7c33f7fbb8 179 if (strcasecmp((*it)->_pCmdStr, cmdStr) == 0)
Bobty 7:fe7c33f7fbb8 180 {
Bobty 19:f67ac231b570 181 RD_DBG("FoundCmd <%s> Type %d", cmdStr, (*it)->_cmdType);
Bobty 7:fe7c33f7fbb8 182 cmdFound = true;
Bobty 7:fe7c33f7fbb8 183 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
Bobty 7:fe7c33f7fbb8 184 {
Bobty 19:f67ac231b570 185 char* respStr = ((*it)->_callback)(method, cmdStr, argStr, _buffer);
Bobty 23:0fc3d7b5e596 186 clientSocketConn.send_all(respStr, strlen(respStr));
Bobty 6:46285c519af2 187 }
Bobty 17:080f2bed8b36 188 else if ( ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE) ||
Bobty 17:080f2bed8b36 189 ((*it)->_cmdType == RdWebServerCmdDef::CMD_SDORUSBFILE) )
Bobty 7:fe7c33f7fbb8 190 {
Bobty 17:080f2bed8b36 191 char combinedFileName[MAX_FILENAME_LEN];
Bobty 17:080f2bed8b36 192 strcpy(combinedFileName, (*it)->_substFileName);
Bobty 17:080f2bed8b36 193 if (strlen(combinedFileName) == 0)
Bobty 17:080f2bed8b36 194 strcpy(combinedFileName, cmdStr);
Bobty 17:080f2bed8b36 195 else if (combinedFileName[strlen(combinedFileName)-1] == '*')
Bobty 17:080f2bed8b36 196 strcpy(combinedFileName+strlen(combinedFileName)-1, argStr);
Bobty 17:080f2bed8b36 197 if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
Bobty 17:080f2bed8b36 198 handledOk = handleLocalFileRequest(combinedFileName, argStr, clientSocketConn, (*it)->_bCacheIfPossible);
Bobty 7:fe7c33f7fbb8 199 else
Bobty 17:080f2bed8b36 200 handledOk = handleSDFileRequest(combinedFileName, argStr, clientSocketConn);
Bobty 17:080f2bed8b36 201
Bobty 7:fe7c33f7fbb8 202 }
Bobty 7:fe7c33f7fbb8 203 break;
Bobty 6:46285c519af2 204 }
Bobty 6:46285c519af2 205 }
Bobty 7:fe7c33f7fbb8 206 // If command not found see if it is a local file
Bobty 7:fe7c33f7fbb8 207 if (!cmdFound)
Bobty 17:080f2bed8b36 208 {
Bobty 17:080f2bed8b36 209 char combinedFileName[MAX_FILENAME_LEN];
Bobty 17:080f2bed8b36 210 strcpy(combinedFileName, cmdStr);
Bobty 17:080f2bed8b36 211 strcat(combinedFileName, "/");
Bobty 17:080f2bed8b36 212 strcat(combinedFileName, argStr);
Bobty 17:080f2bed8b36 213 handledOk = handleSDFileRequest(cmdStr, argStr, clientSocketConn);
Bobty 17:080f2bed8b36 214 }
Bobty 6:46285c519af2 215 }
Bobty 17:080f2bed8b36 216 return handledOk;
Bobty 6:46285c519af2 217 }
Bobty 6:46285c519af2 218
Bobty 18:5de680c4cfcb 219 // Add a command to the server
Bobty 6:46285c519af2 220 void RdWebServer::addCommand(char* pCmdStr, int cmdType, CmdCallbackType callback, char* substFileName, bool cacheIfPossible)
Bobty 6:46285c519af2 221 {
Bobty 6:46285c519af2 222 _commands.push_back(new RdWebServerCmdDef(pCmdStr, cmdType, callback, substFileName, cacheIfPossible));
Bobty 6:46285c519af2 223 }
Bobty 6:46285c519af2 224
Bobty 18:5de680c4cfcb 225 // Form a header to respond
Bobty 17:080f2bed8b36 226 void RdWebServer::formHTTPHeader(const char* rsltCode, const char* contentType, int contentLen)
Bobty 17:080f2bed8b36 227 {
Bobty 23:0fc3d7b5e596 228 const char* closeConnStr = "\r\nConnection: keep-alive";
Bobty 17:080f2bed8b36 229 if(contentLen != -1)
Bobty 22:598a21373539 230 sprintf(_httpHeader, "HTTP/1.1 %s\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: %s\r\nContent-Length: %d%s\r\n\r\n", rsltCode, contentType, contentLen, _closeConnAfterSend ? closeConnStr : "");
Bobty 17:080f2bed8b36 231 else
Bobty 22:598a21373539 232 sprintf(_httpHeader, "HTTP/1.1 %s\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: %s%s\r\n\r\n", rsltCode, contentType, _closeConnAfterSend ? closeConnStr : "");
Bobty 17:080f2bed8b36 233 }
Bobty 18:5de680c4cfcb 234
Bobty 18:5de680c4cfcb 235 // Use file extension to determine MIME type
Bobty 17:080f2bed8b36 236 char* getMimeTypeStr(char* filename)
Bobty 6:46285c519af2 237 {
Bobty 17:080f2bed8b36 238 char* mimeType = "text/plain";
Bobty 17:080f2bed8b36 239 char *pDot = strrchr(filename, '.');
Bobty 17:080f2bed8b36 240 if (pDot && (!strcmp(pDot, ".html") || !strcmp(pDot, ".htm")))
Bobty 17:080f2bed8b36 241 mimeType = "text/html";
Bobty 17:080f2bed8b36 242 else if (pDot && !strcmp(pDot, ".css"))
Bobty 17:080f2bed8b36 243 mimeType = "text/css";
Bobty 17:080f2bed8b36 244 else if (pDot && !strcmp(pDot, ".js"))
Bobty 17:080f2bed8b36 245 mimeType = "application/javascript";
Bobty 17:080f2bed8b36 246 else if (pDot && !strcmp(pDot, ".png"))
Bobty 17:080f2bed8b36 247 mimeType = "image/png";
Bobty 17:080f2bed8b36 248 else if (pDot && (!strcmp(pDot, ".jpeg") || !strcmp(pDot, ".jpeg")))
Bobty 17:080f2bed8b36 249 mimeType = "image/jpeg";
Bobty 17:080f2bed8b36 250 return mimeType;
Bobty 17:080f2bed8b36 251 }
Bobty 18:5de680c4cfcb 252
Bobty 18:5de680c4cfcb 253 // Handle request for files/listing from SDCard
Bobty 17:080f2bed8b36 254 bool RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn)
Bobty 17:080f2bed8b36 255 {
Bobty 17:080f2bed8b36 256 bool handledOk = false;
Bobty 6:46285c519af2 257 const int HTTPD_MAX_FNAME_LENGTH = 127;
Bobty 6:46285c519af2 258 char filename[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 259
Bobty 19:f67ac231b570 260 RD_INFO("Requesting file %s", inFileName);
Bobty 6:46285c519af2 261
Bobty 12:c14ffd4ec125 262 #ifdef SUPPORT_FOLDER_VIEW
Bobty 10:b4b9d4d5e5be 263 if ((strlen(inFileName) > 0) && (inFileName[strlen(inFileName)-1] == '/'))
Bobty 6:46285c519af2 264 {
Bobty 19:f67ac231b570 265 RD_INFO("Request directory %s%s", _pBaseWebFolder, inFileName);
Bobty 6:46285c519af2 266 sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
Bobty 6:46285c519af2 267 DIR *d = opendir(filename);
Bobty 11:cec51b430b20 268 if (d != NULL)
Bobty 11:cec51b430b20 269 {
Bobty 17:080f2bed8b36 270 formHTTPHeader("200 OK", "text/html", -1);
Bobty 23:0fc3d7b5e596 271 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 272 sprintf(_httpHeader,"<html><head><title>Directory Listing</title></head><body><h1>%s</h1><ul>", inFileName);
Bobty 23:0fc3d7b5e596 273 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 274 struct dirent *p;
Bobty 11:cec51b430b20 275 while((p = readdir(d)) != NULL)
Bobty 11:cec51b430b20 276 {
Bobty 19:f67ac231b570 277 RD_INFO("%s", p->d_name);
Bobty 17:080f2bed8b36 278 sprintf(_httpHeader,"<li>%s</li>", p->d_name);
Bobty 23:0fc3d7b5e596 279 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 280 }
Bobty 6:46285c519af2 281 }
Bobty 6:46285c519af2 282 closedir(d);
Bobty 11:cec51b430b20 283 RD_DBG("Directory closed\n");
Bobty 17:080f2bed8b36 284 sprintf(_httpHeader,"</ul></body></html>");
Bobty 23:0fc3d7b5e596 285 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 286 handledOk = true;
Bobty 6:46285c519af2 287 }
Bobty 6:46285c519af2 288 else
Bobty 12:c14ffd4ec125 289 #endif
Bobty 6:46285c519af2 290 {
Bobty 6:46285c519af2 291 sprintf(filename, "%s%s", _pBaseWebFolder, inFileName);
Bobty 19:f67ac231b570 292 RD_INFO("Filename %s", filename);
Bobty 6:46285c519af2 293
Bobty 6:46285c519af2 294 FILE* fp = fopen(filename, "r");
Bobty 6:46285c519af2 295 if (fp == NULL)
Bobty 6:46285c519af2 296 {
Bobty 19:f67ac231b570 297 RD_WARN("Filename %s not found", filename);
Bobty 17:080f2bed8b36 298 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 23:0fc3d7b5e596 299 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 300 handledOk = true;
Bobty 6:46285c519af2 301 }
Bobty 6:46285c519af2 302 else
Bobty 6:46285c519af2 303 {
Bobty 19:f67ac231b570 304 RD_INFO("Sending file %s", filename);
Bobty 17:080f2bed8b36 305 // Find file length
Bobty 14:4b83670854f0 306 fseek(fp, 0L, SEEK_END);
Bobty 14:4b83670854f0 307 int sz = ftell(fp);
Bobty 14:4b83670854f0 308 fseek(fp, 0L, SEEK_SET);
Bobty 17:080f2bed8b36 309 // Get mime type
Bobty 17:080f2bed8b36 310 char* mimeType = getMimeTypeStr(filename);
Bobty 18:5de680c4cfcb 311 RD_DBG("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 312 // Form header
Bobty 17:080f2bed8b36 313 formHTTPHeader("200 OK", mimeType, sz);
Bobty 23:0fc3d7b5e596 314 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 14:4b83670854f0 315 // Read file in blocks and send
Bobty 6:46285c519af2 316 int rdCnt = 0;
Bobty 6:46285c519af2 317 char fileBuf[1024];
Bobty 6:46285c519af2 318 while ((rdCnt = fread(fileBuf, sizeof( char ), 1024, fp)) == 1024)
Bobty 6:46285c519af2 319 {
Bobty 23:0fc3d7b5e596 320 clientSocketConn.send_all(fileBuf, rdCnt);
Bobty 6:46285c519af2 321 }
Bobty 23:0fc3d7b5e596 322 clientSocketConn.send_all(fileBuf, rdCnt);
Bobty 6:46285c519af2 323 fclose(fp);
Bobty 17:080f2bed8b36 324 handledOk = true;
Bobty 6:46285c519af2 325 }
Bobty 6:46285c519af2 326 }
Bobty 17:080f2bed8b36 327 return handledOk;
Bobty 6:46285c519af2 328 }
Bobty 6:46285c519af2 329
Bobty 18:5de680c4cfcb 330 // Send a file from cache - used for local files only
Bobty 15:0865fa4b046a 331 #ifdef SUPPORT_LOCAL_FILE_CACHE
Bobty 17:080f2bed8b36 332 void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &clientSocketConn)
Bobty 6:46285c519af2 333 {
Bobty 19:f67ac231b570 334 RD_INFO("Sending file %s from cache %d bytes", pCacheEntry->_fileName, pCacheEntry->_nFileLen);
Bobty 17:080f2bed8b36 335 // Get mime type
Bobty 17:080f2bed8b36 336 char* mimeType = getMimeTypeStr(pCacheEntry->_fileName);
Bobty 17:080f2bed8b36 337 RD_INFO("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 338 // Form header
Bobty 17:080f2bed8b36 339 formHTTPHeader("200 OK", mimeType, pCacheEntry->_nFileLen);
Bobty 23:0fc3d7b5e596 340 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 341 char* pMem = pCacheEntry->_pFileContent;
Bobty 6:46285c519af2 342 int nLenLeft = pCacheEntry->_nFileLen;
Bobty 6:46285c519af2 343 int blkSize = 2048;
Bobty 6:46285c519af2 344 while(nLenLeft > 0)
Bobty 6:46285c519af2 345 {
Bobty 6:46285c519af2 346 if (blkSize > nLenLeft)
Bobty 6:46285c519af2 347 blkSize = nLenLeft;
Bobty 23:0fc3d7b5e596 348 clientSocketConn.send_all(pMem, blkSize);
Bobty 6:46285c519af2 349 pMem += blkSize;
Bobty 6:46285c519af2 350 nLenLeft -= blkSize;
Bobty 6:46285c519af2 351 }
Bobty 6:46285c519af2 352 }
Bobty 15:0865fa4b046a 353 #endif
Bobty 6:46285c519af2 354
Bobty 18:5de680c4cfcb 355 // Handle a request for a local file
Bobty 17:080f2bed8b36 356 bool RdWebServer::handleLocalFileRequest(char* inFileName, char* argStr, TCPSocketConnection &clientSocketConn, bool bCacheIfPossible)
Bobty 6:46285c519af2 357 {
Bobty 12:c14ffd4ec125 358
Bobty 12:c14ffd4ec125 359 #ifdef SUPPORT_LOCAL_FILESYSTEM
Bobty 12:c14ffd4ec125 360
Bobty 6:46285c519af2 361 const int HTTPD_MAX_FNAME_LENGTH = 127;
Bobty 6:46285c519af2 362 char localFilename[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 363 char reqFileNameStr[HTTPD_MAX_FNAME_LENGTH+1];
Bobty 6:46285c519af2 364
Bobty 19:f67ac231b570 365 RD_INFO("Requesting local file %s", inFileName);
Bobty 6:46285c519af2 366 sprintf(reqFileNameStr, "/%s", inFileName);
Bobty 6:46285c519af2 367 sprintf(localFilename, "/local/%s", inFileName);
Bobty 6:46285c519af2 368
Bobty 6:46285c519af2 369 if (bCacheIfPossible)
Bobty 6:46285c519af2 370 {
Bobty 6:46285c519af2 371 // Check if file already cached
Bobty 6:46285c519af2 372 bool bTryToCache = true;
Bobty 6:46285c519af2 373 for (std::vector<RdFileCacheEntry*>::iterator it = _cachedFiles.begin() ; it != _cachedFiles.end(); ++it)
Bobty 6:46285c519af2 374 {
Bobty 6:46285c519af2 375 if (strcmp(inFileName, (*it)->_fileName) == 0)
Bobty 6:46285c519af2 376 {
Bobty 6:46285c519af2 377 if ((*it)->_bCacheValid)
Bobty 6:46285c519af2 378 {
Bobty 21:2dfb56648b93 379 sendFromCache(*it, clientSocketConn);
Bobty 17:080f2bed8b36 380 return true;
Bobty 6:46285c519af2 381 }
Bobty 6:46285c519af2 382 bTryToCache = false; // don't try to cache as cacheing must have already failed
Bobty 6:46285c519af2 383 }
Bobty 6:46285c519af2 384 }
Bobty 6:46285c519af2 385
Bobty 6:46285c519af2 386 // See if we can cache the file
Bobty 6:46285c519af2 387 if (bTryToCache)
Bobty 6:46285c519af2 388 {
Bobty 6:46285c519af2 389 RdFileCacheEntry* pCacheEntry = new RdFileCacheEntry(inFileName);
Bobty 6:46285c519af2 390 if (pCacheEntry)
Bobty 6:46285c519af2 391 {
Bobty 6:46285c519af2 392 bool bCacheSuccess = pCacheEntry->readLocalFileIntoCache(localFilename);
Bobty 6:46285c519af2 393 // 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 394 _cachedFiles.push_back(pCacheEntry);
Bobty 6:46285c519af2 395 if (bCacheSuccess)
Bobty 6:46285c519af2 396 {
Bobty 21:2dfb56648b93 397 sendFromCache(pCacheEntry, clientSocketConn);
Bobty 17:080f2bed8b36 398 return true;
Bobty 6:46285c519af2 399 }
Bobty 6:46285c519af2 400 }
Bobty 6:46285c519af2 401 }
Bobty 6:46285c519af2 402 }
Bobty 6:46285c519af2 403
Bobty 6:46285c519af2 404 LocalFileSystem local("local");
Bobty 6:46285c519af2 405
Bobty 6:46285c519af2 406 FILE* fp = fopen(localFilename, "r");
Bobty 6:46285c519af2 407 if (fp == NULL)
Bobty 6:46285c519af2 408 {
Bobty 19:f67ac231b570 409 RD_WARN("Local file %s not found", localFilename);
Bobty 17:080f2bed8b36 410 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 23:0fc3d7b5e596 411 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 412 return true;
Bobty 6:46285c519af2 413 }
Bobty 6:46285c519af2 414 else
Bobty 6:46285c519af2 415 {
Bobty 19:f67ac231b570 416 RD_INFO("Sending file %s from disk", localFilename);
Bobty 17:080f2bed8b36 417 // Find file length
Bobty 17:080f2bed8b36 418 fseek(fp, 0L, SEEK_END);
Bobty 17:080f2bed8b36 419 int sz = ftell(fp);
Bobty 17:080f2bed8b36 420 fseek(fp, 0L, SEEK_SET);
Bobty 17:080f2bed8b36 421 // Get mime type
Bobty 17:080f2bed8b36 422 char* mimeType = getMimeTypeStr(localFilename);
Bobty 18:5de680c4cfcb 423 RD_DBG("MIME TYPE %s", mimeType);
Bobty 17:080f2bed8b36 424 // Form header
Bobty 17:080f2bed8b36 425 formHTTPHeader("200 OK", mimeType, sz);
Bobty 23:0fc3d7b5e596 426 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 6:46285c519af2 427 int rdCnt = 0;
Bobty 17:080f2bed8b36 428 while ((rdCnt = fread(_httpHeader, sizeof( char ), sizeof(_httpHeader), fp)) == sizeof(_httpHeader))
Bobty 6:46285c519af2 429 {
Bobty 23:0fc3d7b5e596 430 clientSocketConn.send_all(_httpHeader, rdCnt);
Bobty 6:46285c519af2 431 }
Bobty 17:080f2bed8b36 432 if (rdCnt != 0)
Bobty 23:0fc3d7b5e596 433 clientSocketConn.send_all(_httpHeader, rdCnt);
Bobty 6:46285c519af2 434 fclose(fp);
Bobty 17:080f2bed8b36 435 return true;
Bobty 6:46285c519af2 436 }
Bobty 12:c14ffd4ec125 437
Bobty 12:c14ffd4ec125 438 #else
Bobty 12:c14ffd4ec125 439
Bobty 19:f67ac231b570 440 RD_WARN("Local file system not supported");
Bobty 17:080f2bed8b36 441 formHTTPHeader("404 Not Found", "text/plain", 0);
Bobty 23:0fc3d7b5e596 442 clientSocketConn.send_all(_httpHeader,strlen(_httpHeader));
Bobty 17:080f2bed8b36 443 return true;
Bobty 12:c14ffd4ec125 444
Bobty 12:c14ffd4ec125 445 #endif
Bobty 6:46285c519af2 446 }
Bobty 6:46285c519af2 447
Bobty 18:5de680c4cfcb 448 // Read file into cache
Bobty 15:0865fa4b046a 449 #ifdef SUPPORT_LOCAL_FILE_CACHE
Bobty 6:46285c519af2 450 bool RdFileCacheEntry::readLocalFileIntoCache(char* fileName)
Bobty 6:46285c519af2 451 {
Bobty 12:c14ffd4ec125 452 #ifdef SUPPORT_LOCAL_FILESYSTEM
Bobty 12:c14ffd4ec125 453
Bobty 19:f67ac231b570 454 RD_INFO("Reading into cache %s", fileName);
Bobty 6:46285c519af2 455 LocalFileSystem local("local");
Bobty 6:46285c519af2 456 FILE* fp = fopen(fileName, "r");
Bobty 6:46285c519af2 457 if (fp == NULL)
Bobty 6:46285c519af2 458 {
Bobty 19:f67ac231b570 459 RD_WARN("Failed to open file");
Bobty 6:46285c519af2 460 return false;
Bobty 6:46285c519af2 461 }
Bobty 21:2dfb56648b93 462 RD_DBG("Seeking ");
Bobty 6:46285c519af2 463 fseek(fp, 0, SEEK_END);
Bobty 6:46285c519af2 464 _nFileLen = (int)ftell(fp);
Bobty 21:2dfb56648b93 465 RD_DBG("Len %d ", _nFileLen);
Bobty 6:46285c519af2 466 _pFileContent = new char[_nFileLen];
Bobty 21:2dfb56648b93 467 RD_DBG("Buf %08x ", _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 21:2dfb56648b93 551 unsigned 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 21:2dfb56648b93 555 return (unsigned char*) (ptr+4);
Bobty 21:2dfb56648b93 556 return (unsigned char*) "\0";
Bobty 19:f67ac231b570 557 }
Bobty 21:2dfb56648b93 558
Bobty 21:2dfb56648b93 559 int RdWebServer::getPayloadLengthFromMsg(char* msgBuf)
Bobty 21:2dfb56648b93 560 {
Bobty 21:2dfb56648b93 561 char* ptr = strstr(msgBuf, "Content-Length:");
Bobty 21:2dfb56648b93 562 if (ptr)
Bobty 21:2dfb56648b93 563 {
Bobty 21:2dfb56648b93 564 ptr += 15;
Bobty 21:2dfb56648b93 565 int contentLen = atoi(ptr);
Bobty 21:2dfb56648b93 566 if (contentLen >= 0)
Bobty 21:2dfb56648b93 567 return contentLen;
Bobty 21:2dfb56648b93 568 }
Bobty 21:2dfb56648b93 569 return 0;
Bobty 21:2dfb56648b93 570 }