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.
Revision 14:4b83670854f0, committed 2015-05-05
- Comitter:
- Bobty
- Date:
- Tue May 05 15:05:44 2015 +0000
- Parent:
- 13:4f9c09d3da13
- Child:
- 15:0865fa4b046a
- Commit message:
- Improving robustness
Changed in this revision
| RdWebServer.cpp | Show annotated file Show diff for this revision Revisions of this file |
| RdWebServer.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/RdWebServer.cpp Mon May 04 17:26:32 2015 +0000
+++ b/RdWebServer.cpp Tue May 05 15:05:44 2015 +0000
@@ -11,6 +11,7 @@
#define MAX_CMDSTR_LEN 100
#define MAX_ARGSTR_LEN 100
#define MAX_FILENAME_LEN (MAX_ARGSTR_LEN + 20)
+#define MAX_MIMETYPE_LEN 50
RdWebServer::RdWebServer()
{
@@ -58,8 +59,9 @@
return true;
}
-void RdWebServer::handleReceivedHttp(TCPSocketConnection &client)
+bool RdWebServer::handleReceivedHttp(TCPSocketConnection &client)
{
+ bool closeConn = true;
RD_DBG("Received Data: %d\n\r\n\r%.*s\n\r",strlen(_buffer),strlen(_buffer),_buffer);
int method = METHOD_OTHER;
if (strncmp(_buffer, "GET ", 4) == 0)
@@ -84,7 +86,7 @@
if ((*it)->_cmdType == RdWebServerCmdDef::CMD_CALLBACK)
{
char* respStr = ((*it)->_callback)(method, cmdStr, argStr);
- client.send(respStr, strlen(respStr));
+ client.send_all(respStr, strlen(respStr));
}
else if ((*it)->_cmdType == RdWebServerCmdDef::CMD_LOCALFILE)
{
@@ -109,11 +111,11 @@
strcpy(combinedFileName, (*it)->_substFileName);
if (combinedFileName[strlen(combinedFileName)-1] == '*')
strcpy(combinedFileName+strlen(combinedFileName)-1, argStr);
- handleSDFileRequest(combinedFileName, argStr, client, _httpHeader);
+ closeConn = handleSDFileRequest(combinedFileName, argStr, client, _httpHeader);
}
else
{
- handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
+ closeConn = handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
}
}
break;
@@ -121,8 +123,9 @@
}
// If command not found see if it is a local file
if (!cmdFound)
- handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
+ closeConn = handleSDFileRequest(cmdStr, argStr, client, _httpHeader);
}
+ return closeConn;
}
void RdWebServer::run()
@@ -164,22 +167,27 @@
else if (rxLen == 0)
{
RD_WARN("received buffer is empty.\n\r");
- continue;
+ break;
}
else if (rxLen > HTTPD_MAX_REQ_LENGTH)
{
sprintf(_httpHeader,"HTTP/1.1 413 Request Entity Too Large \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
- client.send(_httpHeader,strlen(_httpHeader));
- client.send(_buffer, rxLen);
- continue;
+ client.send_all(_httpHeader,strlen(_httpHeader));
+ client.send_all(_buffer, rxLen);
+ break;
}
_buffer[rxLen] = '\0';
// Handle buffer
- handleReceivedHttp(client);
-
- // Done
- break;
+ if (handleReceivedHttp(client))
+ {
+ break;
+ }
+ else
+ {
+ connectLimitTimer.reset();
+ connectLimitTimer.start();
+ }
}
// Connection now closed
@@ -197,8 +205,9 @@
_commands.push_back(new RdWebServerCmdDef(pCmdStr, cmdType, callback, substFileName, cacheIfPossible));
}
-void RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &client, char* httpHeader)
+bool RdWebServer::handleSDFileRequest(char* inFileName, char* argStr, TCPSocketConnection &client, char* httpHeader)
{
+ bool closeConn = true;
const int HTTPD_MAX_FNAME_LENGTH = 127;
char filename[HTTPD_MAX_FNAME_LENGTH+1];
@@ -213,21 +222,21 @@
if (d != NULL)
{
sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
- client.send(httpHeader,strlen(httpHeader));
+ client.send_all(httpHeader,strlen(httpHeader));
sprintf(httpHeader,"<html><head><title>Directory Listing</title></head><body><h1>%s</h1><ul>", inFileName);
- client.send(httpHeader,strlen(httpHeader));
+ client.send_all(httpHeader,strlen(httpHeader));
struct dirent *p;
while((p = readdir(d)) != NULL)
{
RD_INFO("%s\r\n", p->d_name);
sprintf(httpHeader,"<li>%s</li>", p->d_name);
- client.send(httpHeader,strlen(httpHeader));
+ client.send_all(httpHeader,strlen(httpHeader));
}
}
closedir(d);
RD_DBG("Directory closed\n");
sprintf(httpHeader,"</ul></body></html>");
- client.send(httpHeader,strlen(httpHeader));
+ client.send_all(httpHeader,strlen(httpHeader));
}
else
#endif
@@ -239,32 +248,49 @@
if (fp == NULL)
{
RD_WARN("Filename %s not found\r\n", filename);
- sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
- client.send(httpHeader,strlen(httpHeader));
- client.send(inFileName,strlen(inFileName));
+ sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nContent-Length: 0\r\n\r\n");
+ client.send_all(httpHeader,strlen(httpHeader));
+ closeConn = false;
}
else
{
RD_INFO("Sending file %s\r\n", filename);
- sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
- client.send(httpHeader,strlen(httpHeader));
+ fseek(fp, 0L, SEEK_END);
+ int sz = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+ sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", sz);
+ client.send_all(httpHeader,strlen(httpHeader));
+ // MIME type
+ char *pDot = strrchr(filename, '.');
+ char mimeType[MAX_MIMETYPE_LEN+1];
+ if (pDot && (!strcmp(pDot, ".html") || !strcmp(pDot, ".htm")))
+ strcpy(mimeType, "Content-Type: text/html\r\n");
+ else if (pDot && !strcmp(pDot, ".css"))
+ strcpy(mimeType, "Content-Type: text/css\r\n");
+ if (pDot && !strcmp(pDot, ".js"))
+ strcpy(mimeType, "Content-Type: application/javascript\r\n");
+// client.send_all(mimeType,strlen(mimeType));
+ RD_INFO("MIME TYPE %s", mimeType);
+ // Read file in blocks and send
int rdCnt = 0;
char fileBuf[1024];
while ((rdCnt = fread(fileBuf, sizeof( char ), 1024, fp)) == 1024)
{
client.send_all(fileBuf, rdCnt);
}
- client.send(fileBuf, rdCnt);
+ client.send_all(fileBuf, rdCnt);
fclose(fp);
+ closeConn = false;
}
}
+ return closeConn;
}
void RdWebServer::sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &client, char* httpHeader)
{
RD_INFO("Sending file %s from cache %d bytes\r\n", pCacheEntry->_fileName, pCacheEntry->_nFileLen);
sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
- client.send(httpHeader,strlen(httpHeader));
+ client.send_all(httpHeader,strlen(httpHeader));
char* pMem = pCacheEntry->_pFileContent;
int nLenLeft = pCacheEntry->_nFileLen;
int blkSize = 2048;
@@ -333,14 +359,14 @@
{
RD_WARN("Local file %s not found\r\n", localFilename);
sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
- client.send(httpHeader,strlen(httpHeader));
- client.send(reqFileNameStr,strlen(reqFileNameStr));
+ client.send_all(httpHeader,strlen(httpHeader));
+ client.send_all(reqFileNameStr,strlen(reqFileNameStr));
}
else
{
RD_INFO("Sending file %s from disk\r\n", localFilename);
sprintf(httpHeader,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n");
- client.send(httpHeader,strlen(httpHeader));
+ client.send_all(httpHeader,strlen(httpHeader));
int rdCnt = 0;
char fileBuf[2000];
while ((rdCnt = fread(fileBuf, sizeof( char ), 2000, fp)) == 2000)
@@ -355,7 +381,7 @@
RD_WARN("Local file system not supported\r\n");
sprintf(httpHeader,"HTTP/1.1 404 Not Found \r\nContent-Type: text\r\nConnection: Close\r\n\r\n");
- client.send(httpHeader,strlen(httpHeader));
+ client.send_all(httpHeader,strlen(httpHeader));
client.send_all(inFileName,strlen(inFileName));
#endif
--- a/RdWebServer.h Mon May 04 17:26:32 2015 +0000
+++ b/RdWebServer.h Tue May 05 15:05:44 2015 +0000
@@ -140,10 +140,10 @@
char _buffer[HTTPD_MAX_REQ_LENGTH+1];
void handleLocalFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client, char* httpHeader, bool bCacheIfPossible);
- void handleSDFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client, char* httpHeader);
+ bool handleSDFileRequest(char* cmdStr, char* argStr, TCPSocketConnection &client, char* httpHeader);
void handleCGIRequest(char* pUriStr, char* pQueryStr);
void sendFromCache(RdFileCacheEntry* pCacheEntry, TCPSocketConnection &client, char* httpHeader);
- void handleReceivedHttp(TCPSocketConnection &client);
+ bool handleReceivedHttp(TCPSocketConnection &client);
};