A sample program demonstrating a small but powerful web server using the Wifly module. This uses several libraries from others, but has a custom version of the WiflyInterface library, with numerous improvement to the mbed standard library.

Dependencies:   SW_HTTPServer WiflyInterface mbed C12832 IniManager

Here's the code

But you also might want to check out the SmartBoard-WiFly project page.

Basic Web Server

  • Serves static files from the selected file system. This is a compile-time setting, and a typical configuration supports gif, jpg, jpeg, ico, png, zip, gz, tar, txt, pdf, htm, and html.
  • It is designed to be small, thereby better supporting the limited resources of an embedded environment.

Advanced Web Services

  • Serves dynamically generated pages, where your software registers for a path, and then everything to that path activates your handler. Your handler then defines the header and body response.
  • Dynamic handlers can process GET query parameters (e.g. /dyn1?sky=blue&grass=green).
  • Dynamic handlers can process POST query parameters, as delivered from submission of a form.
  • Dynamic handlers can protect a resource with user:password access.

Run-Time Configurations

  • File System Support - using either the "local" file system supported by the magic chip, or from either an SD-Card or a USB flash drive.
  • Configurable to the maximum number of dynamic handlers (minimize memory requirements).
  • Configurable to the maximum number of name=value pairs for dynamic handlers (minimize memory requirements).

Compile-Time Configurations

  • Default filename for URL ending in '/' - default is 'index.htm'.
  • Configurable buffer sizes permit minimizing RAM requirements.
  • Configurable header response information.
  • Configurable for which serial port is used to communicate to the WiFly module.
  • Improved security option - to disable telnet access.

Diagnostics

  • API to determine the largest header (to more efficiently size the buffers).
  • API to gather the run-time characteristics - header processing time and content delivery time.

Limitations / Constraints

Known Issues

These are known issues, not yet resolved.

  1. Occasionally fails to serve a page - one test will constantly reload a web page every 30 seconds. It may run for hours, or minutes, then fail to load. Behaviors then are:
    • Hit the reload button in the browser and away it goes.
    • Hit the reload and you'll see the Wifly LEDs energize from the request, but no response by the web server. It appears that the embedded code does not "accept()" the connection in the TCP Socket Server.
      • In this case, the Wifly module has gone through an internal watchdog reset and the configuration parameters are such that it does not gracefully recover. Microchip is aware of this issue, but has not solved it.

Wifly Limitations

  • Single thread - it doesn't respond to overlapping requests (e.g. an embedded image may be requested before the main page completes transfer - the request is lost and the image not shown).
  • Single client - goes along with the single thread, but it doesn't support more than one client at a time.

Smart-Wifly-WebServer

  • Dynamic memory allocation - it does use dynamic memory allocation, which would be discouraged/avoided in many embedded systems. Here it uses it in parsing a request and it releases those resources upon completion of that request. If there is no other dynamic allocation that persists beyond a transaction, it should not cause memory fragmentation. Note that with multi-threading (if this is implemented with an OS), you then have race conditions that could cause fragmentation.

Web Server

Here's the web server in action. A combination of static pages served from the file system and dynamically generated pages.

/media/uploads/WiredHome/swsvr_1.pngPart of the main demo page,
which basically has all the
specifications, configurations, and limitations.
/media/uploads/WiredHome/swsvr_2.pngA zoomed out view of the same page.
/media/uploads/WiredHome/swsvr_3.pngIt would be possible to configure
the server via the web.
/media/uploads/WiredHome/swsvr_4.pngOne of the dynamically generated pages.
This one has parsed the query parameters.
/media/uploads/WiredHome/swsvr_5.pngA simple form which has a dynamic handler on the back end.
Here it takes the value associated with "leds"
and uses that to set the 4 LEDs on the mbed module.
/media/uploads/WiredHome/swsvr_6.pngA dynamic handler can require authentication.
/media/uploads/WiredHome/swsvr_7.pngSuccess!

But I've now gone so far beyond that in the current version. Here's what this one can do:

  1. It serves static web pages from a file system. I've only tested with the local file system and an SD card, but should work for any, so long as you remember that the local file system can't read subdirectories.
  2. It can serve dynamically generated web pages. This lets you accept name=value pairs using the URL (using either a GET or POST method). It can also accept them from forms. The demo lets you control the 4 LEDs from a form.
  3. As safely as possible it retrieves your credentials to the Wi-Fi Access Point. After using them, it overwrites that ram so they can't be as easily extracted.
  4. I made a large number of changes to the Wifly driver. It had too short of a timeout and I found quite a number of optimizations for performance and robustness.
  5. I have the start on a security feature - you can configure a resource to require user credentials to access it. The browser typically provides a username and password dialog. Take care however, as it does not support a secure (https) connection, so the credentials are not as securely transferred as I would like.

Optimizations I'd like to do:

  1. speed it up - I'm running the mbed to wifly module interface at 230K, which is about the top speed w/o flow control. There are other places where some time delays remain - I have eliminated a number of them.
  2. make it non-blocking, so other work can happen.
  3. integrate it with the rtos
  4. When a web page has referenced resources (e.g. an image tag), it rarely loads the image on the first try. I think the request for the resource comes in while it is still in the WiflyInterface cleaning up from the last connection. The Wifly module supports only a single connection at a time. I worked around this with a small bit of javascript to load the images after the web page.

But all in all I think it is a good start.

Program prerequisite

Here's the link to the program, but when you open it up, note a few very important items.

  1. Port Numbers listed in the constructor match the SmartBoard Baseboard.
  2. I sped up the communication baud rate to the mbed from the default 9600. Match your terminal program accordingly.
  3. Download this zip. Place it and an unzipped copy into the mbed local file system. These are the demo files.
  4. The typical ssid and password are not shown. See below to set yours.

ssid and password

You need to create a simple text file on your mbed root folder named "config.ini". The easiest way perhaps is to create "config.txt", add the information shown below and then rename it. This will be read at startup to connect you to the network. Something quite simple, like this:

[Wifi]
ssid=your_ssid
pass=your_pass_code

The program

And the program.

Import programSmart-WiFly-WebServer

A sample program demonstrating a small but powerful web server using the Wifly module. This uses several libraries from others, but has a custom version of the WiflyInterface library, with numerous improvement to the mbed standard library.

Committer:
WiredHome
Date:
Sun May 11 21:20:00 2014 +0000
Revision:
34:77d0f63f612a
Parent:
33:41ac99847df8
Updated examples, particular focus on POST method.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 26:eea4db568404 1 /// Demonstration of dynamic page creation using the Smartware Web Server
WiredHome 26:eea4db568404 2 ///
WiredHome 26:eea4db568404 3 /// Here is a sample for file upload.
WiredHome 26:eea4db568404 4 #include "SW_HTTPServer.h"
WiredHome 26:eea4db568404 5 #include "DynamicFileIn.h"
WiredHome 33:41ac99847df8 6
WiredHome 33:41ac99847df8 7 #define DEBUG "File"
WiredHome 26:eea4db568404 8 #include "Utility.h"
WiredHome 26:eea4db568404 9
WiredHome 34:77d0f63f612a 10 // This defines the size of the largest file you are willing to accept.
WiredHome 34:77d0f63f612a 11 // Hopefully, space is also available on the file system for it.
WiredHome 33:41ac99847df8 12 #define ACCEPT_MAX_BYTES (25000)
WiredHome 33:41ac99847df8 13
WiredHome 34:77d0f63f612a 14 // This defines the local cache, which should be at least 2 x the size of ETHER_CHUNK.
WiredHome 34:77d0f63f612a 15 // It can be any size above that, so long as RAM is available.
WiredHome 34:77d0f63f612a 16 #define PCACHE_SIZE (3000)
WiredHome 34:77d0f63f612a 17
WiredHome 34:77d0f63f612a 18 // ETHER_CHUNK is the maximum size of a single packet handoff.
WiredHome 33:41ac99847df8 19 #define ETHER_CHUNK (1500)
WiredHome 34:77d0f63f612a 20 #if ((2 * ETHER_CHUNK) > PCACHE_SIZE)
WiredHome 34:77d0f63f612a 21 #error "Configuration error, PCACHE_SIZE must be at least 2 * ETHER_CHUNK size.
WiredHome 34:77d0f63f612a 22 #endif
WiredHome 34:77d0f63f612a 23
WiredHome 33:41ac99847df8 24 static int bytesInCache;
WiredHome 33:41ac99847df8 25 static char * pCache;
WiredHome 33:41ac99847df8 26 static char * boundary = ""; // for pointing at the boundary marker part of multipart/form-data in pCache
WiredHome 34:77d0f63f612a 27 static char * contenttype = NULL; // for "text/plain", or "image/bmp"
WiredHome 33:41ac99847df8 28 static char * fqfn = NULL; // for the filepath/filename
WiredHome 34:77d0f63f612a 29 static FILE * fp = NULL;
WiredHome 33:41ac99847df8 30 static int bytesWritten;
WiredHome 33:41ac99847df8 31
WiredHome 33:41ac99847df8 32 static int FileSize(const char * dir, const char * filename);
WiredHome 33:41ac99847df8 33 static char _tolower(char c);
WiredHome 33:41ac99847df8 34 static int _strnicmp(const char * left, const char * right, int len);
WiredHome 33:41ac99847df8 35 static char * _stristr(char * pHaystack, const char * pNeedle);
WiredHome 33:41ac99847df8 36 static char * _strnbin(char * pBinstack, const char *pNeedle, int len);
WiredHome 34:77d0f63f612a 37 static void FreeResources(void);
WiredHome 33:41ac99847df8 38
WiredHome 26:eea4db568404 39 typedef enum {
WiredHome 26:eea4db568404 40 IDLE,
WiredHome 26:eea4db568404 41 ACCEPTED,
WiredHome 26:eea4db568404 42 REJECTED_TOO_LARGE
WiredHome 26:eea4db568404 43 } AcceptStatus;
WiredHome 26:eea4db568404 44
WiredHome 33:41ac99847df8 45 //
WiredHome 33:41ac99847df8 46 //-----------------------------7de3ab1652086c
WiredHome 33:41ac99847df8 47 //Content-Disposition: form-data; name="InFile"; filename="Len0700.txt"
WiredHome 33:41ac99847df8 48 //Content-Type: text/plain
WiredHome 33:41ac99847df8 49 //
WiredHome 33:41ac99847df8 50 //__1 - _50 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 51 //_51 - 100 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 52 //101 - 150 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 53 //151 - 200 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 54 //201 - 250 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 55 //251 - 300 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 56 //301 - 350 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 57 //351 - 400 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 58 //401 - 450 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 59 //451 - 500 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 60 //501 - 550 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 61 //551 - 600 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 62 //601 - 650 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 63 //651 - 700 12345678901234567890123456789012345678
WiredHome 33:41ac99847df8 64 //
WiredHome 33:41ac99847df8 65 //-----------------------------7de3ab1652086c--
WiredHome 33:41ac99847df8 66 //^^ Boundary Definition ^^ Boundary Definition
WiredHome 33:41ac99847df8 67 // is appended to "--" is suffixed "--" indicating the end.
WiredHome 33:41ac99847df8 68 //---
WiredHome 33:41ac99847df8 69 //Content-Disposition: form-data; name="InFile"; filename="somefile.txt"\r\n
WiredHome 33:41ac99847df8 70 //Content-Type: image/bmp\r\n
WiredHome 33:41ac99847df8 71 //\r\n
WiredHome 33:41ac99847df8 72 //BM6.... (where the ... is purely binary data data)
WiredHome 33:41ac99847df8 73 //\r\n------webKitFormBoundary9A3C872FAC9BE23--\r\n
WiredHome 33:41ac99847df8 74 //---
WiredHome 33:41ac99847df8 75 //
WiredHome 33:41ac99847df8 76 HTTPServer::CallBackResults ProcessCache(HTTPServer *svr, HTTPServer::CallBackType type)
WiredHome 33:41ac99847df8 77 {
WiredHome 33:41ac99847df8 78 typedef enum {
WiredHome 33:41ac99847df8 79 Seeking_Boundary_Start,
WiredHome 33:41ac99847df8 80 Seeking_Filename,
WiredHome 33:41ac99847df8 81 Seeking_ContentType,
WiredHome 33:41ac99847df8 82 Seeking_Data
WiredHome 33:41ac99847df8 83 } EF_State_E;
WiredHome 34:77d0f63f612a 84 //const char * statelist[] = {
WiredHome 34:77d0f63f612a 85 // "Start",
WiredHome 34:77d0f63f612a 86 // "Filename",
WiredHome 34:77d0f63f612a 87 // "ContentType",
WiredHome 34:77d0f63f612a 88 // "Data"
WiredHome 34:77d0f63f612a 89 //};
WiredHome 33:41ac99847df8 90 static EF_State_E EF_State = Seeking_Boundary_Start;
WiredHome 33:41ac99847df8 91
WiredHome 33:41ac99847df8 92 char * pDataStart = pCache; // start of data in this transaction
WiredHome 33:41ac99847df8 93 char * pDataEnd = pCache + bytesInCache; // end of data in this transaction
WiredHome 33:41ac99847df8 94 char * pDBLcrlf;
WiredHome 33:41ac99847df8 95 char *p1, *p2, *p3; // general purpose pointer for processing the results
WiredHome 33:41ac99847df8 96 HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_CONTINUE;
WiredHome 33:41ac99847df8 97
WiredHome 34:77d0f63f612a 98 //INFO("ProcessCache %s", statelist[EF_State]);
WiredHome 33:41ac99847df8 99 INFO("### Cache Begin\r\n%s\r\n### Cache End", pCache);
WiredHome 33:41ac99847df8 100 if (EF_State == Seeking_Boundary_Start) {
WiredHome 34:77d0f63f612a 101 INFO("Start");
WiredHome 33:41ac99847df8 102 pDBLcrlf = _strnbin((char *)pDataStart, "\r\n\r\n", pDataEnd - pDataStart); // find end of preamble
WiredHome 33:41ac99847df8 103 if (pDBLcrlf) {
WiredHome 33:41ac99847df8 104 p1 = _strnbin((char *)pDataStart, boundary, pDBLcrlf - pDataStart); // is there a boundary in the cache?
WiredHome 33:41ac99847df8 105 if (p1) {
WiredHome 33:41ac99847df8 106 EF_State = Seeking_Filename;
WiredHome 33:41ac99847df8 107 }
WiredHome 33:41ac99847df8 108 }
WiredHome 33:41ac99847df8 109 }
WiredHome 33:41ac99847df8 110
WiredHome 33:41ac99847df8 111 if (EF_State == Seeking_Filename) {
WiredHome 34:77d0f63f612a 112 INFO("File");
WiredHome 33:41ac99847df8 113 pDBLcrlf = _strnbin((char *)pDataStart, "\r\n\r\n", pDataEnd - pDataStart); // find end of preamble
WiredHome 33:41ac99847df8 114 if (pDBLcrlf) {
WiredHome 33:41ac99847df8 115 p2 = _strnbin((char *)pDataStart, "Content-Disposition: ", pDBLcrlf - pDataStart); // and the filename container?
WiredHome 33:41ac99847df8 116 if (p2) {
WiredHome 33:41ac99847df8 117 p2 = _strnbin((char *)p2, "filename=\"", pDBLcrlf - pDataStart);
WiredHome 33:41ac99847df8 118 if (p2) { // found filename
WiredHome 33:41ac99847df8 119 p2 += strlen("filename=\""); // start of filename
WiredHome 33:41ac99847df8 120 p3 = strchr(p2, '"'); // end of filename
WiredHome 33:41ac99847df8 121 if (p3) {
WiredHome 33:41ac99847df8 122 *p3 = '\0';
WiredHome 33:41ac99847df8 123 fqfn = (char *)malloc(p3-p2 + strlen(svr->GetWebRoot())+2);
WiredHome 33:41ac99847df8 124 if (fqfn) {
WiredHome 33:41ac99847df8 125 strcpy(fqfn, svr->GetWebRoot()); // We could put it elsewhere
WiredHome 33:41ac99847df8 126 strcat(fqfn, "/");
WiredHome 33:41ac99847df8 127 strcat(fqfn, p2);
WiredHome 33:41ac99847df8 128 *p3 = '"'; // Put this back so we can print the cache
WiredHome 33:41ac99847df8 129 EF_State = Seeking_ContentType;
WiredHome 33:41ac99847df8 130 pDataStart = p3 + 1;
WiredHome 33:41ac99847df8 131 } else {
WiredHome 33:41ac99847df8 132 ERR("Failed to allocate space for filename");
WiredHome 33:41ac99847df8 133 ret = HTTPServer::ACCEPT_ERROR;
WiredHome 33:41ac99847df8 134 EF_State = Seeking_Boundary_Start;
WiredHome 33:41ac99847df8 135 bytesInCache = 0;
WiredHome 33:41ac99847df8 136 return ret;
WiredHome 33:41ac99847df8 137 }
WiredHome 33:41ac99847df8 138 }
WiredHome 33:41ac99847df8 139 }
WiredHome 33:41ac99847df8 140 }
WiredHome 33:41ac99847df8 141 }
WiredHome 33:41ac99847df8 142 }
WiredHome 33:41ac99847df8 143
WiredHome 33:41ac99847df8 144 if (EF_State == Seeking_ContentType) {
WiredHome 34:77d0f63f612a 145 INFO("ContentType");
WiredHome 33:41ac99847df8 146 pDBLcrlf = _strnbin((char *)pDataStart, "\r\n\r\n", pDataEnd - pDataStart); // find end of preamble
WiredHome 33:41ac99847df8 147 if (pDBLcrlf) {
WiredHome 33:41ac99847df8 148 p2 = _strnbin((char *)pDataStart, "Content-Type: ", pDBLcrlf - pDataStart);
WiredHome 33:41ac99847df8 149 if (p2) {
WiredHome 33:41ac99847df8 150 p2 += strlen("Content-Type: ");
WiredHome 33:41ac99847df8 151 p3 = strchr(p2, '\r');
WiredHome 33:41ac99847df8 152 if (p3) {
WiredHome 33:41ac99847df8 153 *p3 = '\0';
WiredHome 33:41ac99847df8 154 contenttype = (char *)malloc(p3-p2 + 2);
WiredHome 33:41ac99847df8 155 if (contenttype) {
WiredHome 33:41ac99847df8 156 strcpy(contenttype, p2);
WiredHome 33:41ac99847df8 157 *p3 = '\r'; // put it back so we can print the cache
WiredHome 33:41ac99847df8 158 } else {
WiredHome 33:41ac99847df8 159 ERR("Failed to allocate space for content type");
WiredHome 33:41ac99847df8 160 ret = HTTPServer::ACCEPT_ERROR;
WiredHome 33:41ac99847df8 161 EF_State = Seeking_Boundary_Start;
WiredHome 33:41ac99847df8 162 bytesInCache = 0;
WiredHome 33:41ac99847df8 163 return ret;
WiredHome 33:41ac99847df8 164 }
WiredHome 33:41ac99847df8 165 pDataStart = pDBLcrlf + 4; // Advance to the actual data
WiredHome 33:41ac99847df8 166 EF_State = Seeking_Data;
WiredHome 33:41ac99847df8 167 }
WiredHome 33:41ac99847df8 168 }
WiredHome 33:41ac99847df8 169 }
WiredHome 33:41ac99847df8 170 }
WiredHome 33:41ac99847df8 171
WiredHome 33:41ac99847df8 172 if (EF_State == Seeking_Data) {
WiredHome 34:77d0f63f612a 173 INFO("Data");
WiredHome 33:41ac99847df8 174 if (!fp && fqfn) {
WiredHome 34:77d0f63f612a 175 INFO("Open file [%s]", fqfn);
WiredHome 33:41ac99847df8 176 fp = fopen(fqfn, "w");
WiredHome 33:41ac99847df8 177 if (!fp) {
WiredHome 33:41ac99847df8 178 ERR("failed to open [%s]", fqfn);
WiredHome 34:77d0f63f612a 179 FreeResources();
WiredHome 33:41ac99847df8 180 ret = HTTPServer::ACCEPT_ERROR;
WiredHome 33:41ac99847df8 181 return ret;
WiredHome 33:41ac99847df8 182 }
WiredHome 33:41ac99847df8 183 }
WiredHome 33:41ac99847df8 184 if (fp) {
WiredHome 33:41ac99847df8 185 Timer t;
WiredHome 33:41ac99847df8 186 // The file is open, so we're writing data to it.
WiredHome 33:41ac99847df8 187 // We do this until we find the ending boundary, but we may not have the [whole]
WiredHome 33:41ac99847df8 188 // ending boundary in the cache yet.
WiredHome 33:41ac99847df8 189 p1 = _strnbin((char *)pDataStart, boundary, pDataEnd - pDataStart); // is there a boundary in the cache?
WiredHome 33:41ac99847df8 190 if (p1) { // if we find the ending boundary, we can truncate and write the data ahead of the boundary
WiredHome 33:41ac99847df8 191 p1 -= 4; // Actual ending boundary is "\r\n--" + "boundary" + "--", so back up 4.
WiredHome 33:41ac99847df8 192 *p1 = '\0';
WiredHome 33:41ac99847df8 193 pDataEnd = p1;
WiredHome 33:41ac99847df8 194 if (pDataEnd > pDataStart) {
WiredHome 33:41ac99847df8 195 bytesWritten += pDataEnd - pDataStart;
WiredHome 33:41ac99847df8 196 t.start();
WiredHome 33:41ac99847df8 197 fwrite(pDataStart, 1, pDataEnd - pDataStart, fp);
WiredHome 33:41ac99847df8 198 INFO("Writing %5d bytes, %5d total written, (reserve %d) in %d us",
WiredHome 33:41ac99847df8 199 pDataEnd - pDataStart, bytesWritten, 0, t.read_us());
WiredHome 33:41ac99847df8 200 //INFO("Write: %s***", pDataStart);
WiredHome 33:41ac99847df8 201 bytesInCache = 0;
WiredHome 33:41ac99847df8 202 *pCache = '\0'; // this for debugging in case it is text and you want to print it.
WiredHome 33:41ac99847df8 203 } else {
WiredHome 33:41ac99847df8 204 INFO("Not enough data to write (%d <= %d)", pDataEnd, pDataStart);
WiredHome 33:41ac99847df8 205 }
WiredHome 33:41ac99847df8 206 } else { // didn't find the boundary, but it might be partially there...
WiredHome 33:41ac99847df8 207 // write the data, but not the last (strlen(boundary) + 4)
WiredHome 33:41ac99847df8 208 // in case this transaction has some, but not all of the boundary
WiredHome 33:41ac99847df8 209 int reserve = strlen(boundary) + 4;
WiredHome 33:41ac99847df8 210 pDataEnd -= reserve;
WiredHome 33:41ac99847df8 211 if (pDataEnd > pDataStart) {
WiredHome 33:41ac99847df8 212 bytesWritten += pDataEnd - pDataStart;
WiredHome 33:41ac99847df8 213 t.start();
WiredHome 33:41ac99847df8 214 fwrite(pDataStart, 1, pDataEnd - pDataStart, fp);
WiredHome 33:41ac99847df8 215 INFO("Writing %5d bytes, %5d total written, (reserve %d) in %d us",
WiredHome 33:41ac99847df8 216 pDataEnd - pDataStart, bytesWritten, reserve, t.read_us());
WiredHome 33:41ac99847df8 217 //INFO("Write: %s###", pDataStart);
WiredHome 33:41ac99847df8 218 memcpy(pCache, pDataEnd, reserve+1); // copies the trailing '\0'
WiredHome 33:41ac99847df8 219 bytesInCache = reserve;
WiredHome 33:41ac99847df8 220 //*(pCache + bytesInCache) = '\0';
WiredHome 33:41ac99847df8 221 INFO("remaining: %s", pCache);
WiredHome 33:41ac99847df8 222 } else {
WiredHome 33:41ac99847df8 223 INFO("Not enough data to write (%d <= %d)", pDataEnd, pDataStart);
WiredHome 33:41ac99847df8 224 }
WiredHome 33:41ac99847df8 225 }
WiredHome 33:41ac99847df8 226 ret = HTTPServer::ACCEPT_CONTINUE;
WiredHome 33:41ac99847df8 227 }
WiredHome 33:41ac99847df8 228 }
WiredHome 33:41ac99847df8 229
WiredHome 33:41ac99847df8 230 if (type == HTTPServer::DATA_TRANSFER_END) {
WiredHome 34:77d0f63f612a 231 FreeResources();
WiredHome 33:41ac99847df8 232 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 33:41ac99847df8 233 }
WiredHome 33:41ac99847df8 234 return ret;
WiredHome 33:41ac99847df8 235 }
WiredHome 33:41ac99847df8 236
WiredHome 33:41ac99847df8 237
WiredHome 33:41ac99847df8 238
WiredHome 26:eea4db568404 239 /// DynamicFileIn
WiredHome 26:eea4db568404 240 ///
WiredHome 26:eea4db568404 241 /// This page lets you submit a file.
WiredHome 26:eea4db568404 242 ///
WiredHome 26:eea4db568404 243 /// You can see in main how this page was registered.
WiredHome 26:eea4db568404 244 ///
WiredHome 33:41ac99847df8 245 HTTPServer::CallBackResults DynamicFileIn(
WiredHome 33:41ac99847df8 246 HTTPServer *svr,
WiredHome 33:41ac99847df8 247 HTTPServer::CallBackType type,
WiredHome 33:41ac99847df8 248 const char * path,
WiredHome 33:41ac99847df8 249 const HTTPServer::namevalue *params,
WiredHome 33:41ac99847df8 250 int count)
WiredHome 26:eea4db568404 251 {
WiredHome 26:eea4db568404 252 char buf[100];
WiredHome 26:eea4db568404 253 int clSize; // content-length size to be transferred
WiredHome 26:eea4db568404 254 static AcceptStatus accept = IDLE;
WiredHome 29:14c47d31a9dc 255 HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
WiredHome 26:eea4db568404 256 DIR *d;
WiredHome 26:eea4db568404 257 struct dirent *p;
WiredHome 26:eea4db568404 258
WiredHome 26:eea4db568404 259 switch (type) {
WiredHome 26:eea4db568404 260 case HTTPServer::CONTENT_LENGTH_REQUEST:
WiredHome 26:eea4db568404 261 // Here we can find out how much data is headed our way,
WiredHome 26:eea4db568404 262 // and choose to accept or reject it.
WiredHome 26:eea4db568404 263 clSize = atoi(svr->GetHeaderValue("Content-Length"));
WiredHome 26:eea4db568404 264 //printf("Content Len RQST(%d)\r\n", clSize);
WiredHome 26:eea4db568404 265 //printf("Content-Type: [%s]\r\n", svr->GetHeaderValue("Content-Type"));
WiredHome 26:eea4db568404 266 // also, for the multipart data, we have to learn and track
WiredHome 26:eea4db568404 267 // the boundary sequence, so we can parse the big block
WiredHome 26:eea4db568404 268 // successfully.
WiredHome 33:41ac99847df8 269 boundary = _stristr((char *)svr->GetHeaderValue("Content-Type"), "boundary=");
WiredHome 26:eea4db568404 270 if (boundary)
WiredHome 26:eea4db568404 271 boundary += strlen("boundary=");
WiredHome 33:41ac99847df8 272 //INFO("C-Len: %d, boundary: [%s]", clSize, boundary);
WiredHome 26:eea4db568404 273 //printf("InFile = [%s]\r\n", svr->GetHeaderValue("InFile"));
WiredHome 26:eea4db568404 274 // If we're happy with the size, and we have the boundary marker we need
WiredHome 33:41ac99847df8 275 if (clSize < ACCEPT_MAX_BYTES && boundary) {
WiredHome 33:41ac99847df8 276 pCache = (char *)malloc(PCACHE_SIZE);
WiredHome 33:41ac99847df8 277 if (pCache) {
WiredHome 33:41ac99847df8 278 //INFO("pCache - allocated");
WiredHome 33:41ac99847df8 279 bytesInCache = 0;
WiredHome 33:41ac99847df8 280 accept = ACCEPTED;
WiredHome 33:41ac99847df8 281 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 33:41ac99847df8 282 } else {
WiredHome 33:41ac99847df8 283 accept = REJECTED_TOO_LARGE; // can't get the RAM to host the stream
WiredHome 33:41ac99847df8 284 ret = HTTPServer::ACCEPT_ERROR;
WiredHome 33:41ac99847df8 285 }
WiredHome 26:eea4db568404 286 } else {
WiredHome 26:eea4db568404 287 accept = REJECTED_TOO_LARGE;
WiredHome 26:eea4db568404 288 }
WiredHome 26:eea4db568404 289 break;
WiredHome 30:4717cc1f970e 290
WiredHome 33:41ac99847df8 291 case HTTPServer::DATA_TRANSFER_END:
WiredHome 33:41ac99847df8 292 //INFO("\r\n\\\\\\\\\\\\ Transfer Begin [%d, %d]\r\n%s\r\n////// Transfer End", count, bytesInCache, path);
WiredHome 34:77d0f63f612a 293 INFO("TransferEnd [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
WiredHome 33:41ac99847df8 294 // if there is anything there, copy it in.
WiredHome 33:41ac99847df8 295 if (count) {
WiredHome 33:41ac99847df8 296 memcpy(pCache + bytesInCache, path, count);
WiredHome 33:41ac99847df8 297 bytesInCache += count;
WiredHome 33:41ac99847df8 298 pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
WiredHome 33:41ac99847df8 299 }
WiredHome 33:41ac99847df8 300 ret = ProcessCache(svr, type);
WiredHome 33:41ac99847df8 301 break;
WiredHome 33:41ac99847df8 302
WiredHome 26:eea4db568404 303 case HTTPServer::DATA_TRANSFER:
WiredHome 33:41ac99847df8 304 //INFO("\r\n\\\\\\\\\\\\ Transfer Begin [%d, %d]\r\n%s\r\n////// Transfer End", count, bytesInCache, path);
WiredHome 33:41ac99847df8 305 // We chose to accept the transfer, and it may come in chunks.
WiredHome 34:77d0f63f612a 306 if ((bytesInCache + count) < (PCACHE_SIZE - ETHER_CHUNK)) { // room in the cache for more, juat accept it locally
WiredHome 34:77d0f63f612a 307 INFO("Transfer A [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
WiredHome 33:41ac99847df8 308 memcpy(pCache + bytesInCache, path, count);
WiredHome 33:41ac99847df8 309 bytesInCache += count;
WiredHome 33:41ac99847df8 310 pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
WiredHome 33:41ac99847df8 311 ret = HTTPServer::ACCEPT_CONTINUE;
WiredHome 34:77d0f63f612a 312 } else if ((bytesInCache + count) < PCACHE_SIZE) { // room in the cache, now process it.
WiredHome 34:77d0f63f612a 313 INFO("Transfer B [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
WiredHome 33:41ac99847df8 314 memcpy(pCache + bytesInCache, path, count);
WiredHome 33:41ac99847df8 315 bytesInCache += count;
WiredHome 33:41ac99847df8 316 pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
WiredHome 33:41ac99847df8 317 ret = ProcessCache(svr, type); // now hand it off
WiredHome 33:41ac99847df8 318 } else {
WiredHome 34:77d0f63f612a 319 INFO("Transfer C [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
WiredHome 33:41ac99847df8 320 ret = ProcessCache(svr, type); // panic, ah!, ok, first hand it off then cache it
WiredHome 33:41ac99847df8 321 memcpy(pCache + bytesInCache, path, count);
WiredHome 33:41ac99847df8 322 bytesInCache += count;
WiredHome 33:41ac99847df8 323 pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
WiredHome 34:77d0f63f612a 324 ERR("FATAL - didn't flush pCache, %d in cache.", bytesInCache);
WiredHome 33:41ac99847df8 325 break;
WiredHome 26:eea4db568404 326 }
WiredHome 26:eea4db568404 327 break;
WiredHome 33:41ac99847df8 328
WiredHome 30:4717cc1f970e 329 case HTTPServer::SEND_PAGE:
WiredHome 30:4717cc1f970e 330 svr->header(200, "OK", svr->GetSupportedType(".htm"));
WiredHome 30:4717cc1f970e 331 svr->send("<html><head><title>Dynamic File Submit</title></head>\r\n");
WiredHome 30:4717cc1f970e 332 svr->send("<body>\r\n");
WiredHome 30:4717cc1f970e 333 svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
WiredHome 33:41ac99847df8 334 sprintf(buf, "This example permits you to upload a file of not more than %d bytes. ", ACCEPT_MAX_BYTES);
WiredHome 33:41ac99847df8 335 svr->send(buf);
WiredHome 33:41ac99847df8 336 svr->send("If the file is one of the supported types, you can then download it too. ");
WiredHome 33:41ac99847df8 337 svr->send("Select the file using the form gadget and then submit it.");
WiredHome 30:4717cc1f970e 338 switch(accept) {
WiredHome 30:4717cc1f970e 339 case IDLE:
WiredHome 30:4717cc1f970e 340 default:
WiredHome 30:4717cc1f970e 341 break;
WiredHome 30:4717cc1f970e 342 case ACCEPTED:
WiredHome 30:4717cc1f970e 343 svr->send("<br/><b>Previous submission accepted.</b><br/>\r\n");
WiredHome 30:4717cc1f970e 344 break;
WiredHome 30:4717cc1f970e 345 case REJECTED_TOO_LARGE:
WiredHome 30:4717cc1f970e 346 svr->send("<br/><b>Previous submission rejected.</b><br/>\r\n");
WiredHome 30:4717cc1f970e 347 break;
WiredHome 30:4717cc1f970e 348 }
WiredHome 30:4717cc1f970e 349 // note that to accept a file, the form enctype is multipart/form-data.
WiredHome 33:41ac99847df8 350 svr->send("<blockquote>\r\n");
WiredHome 30:4717cc1f970e 351 sprintf(buf, "<form method='post' enctype='multipart/form-data' action='%s'>\r\n", path);
WiredHome 30:4717cc1f970e 352 svr->send(buf);
WiredHome 33:41ac99847df8 353 svr->send("<table border='0'>\r\n");
WiredHome 33:41ac99847df8 354 svr->send("<tr><td>File</td><td><input type='file' name='InFile' size='60'></td></tr>\r\n");
WiredHome 33:41ac99847df8 355 svr->send("<tr><td>&nbsp;</td><td><input type='submit' value='Submit'><input type='reset' value='Clear'></td></tr>\r\n");
WiredHome 30:4717cc1f970e 356 svr->send("</table>\r\n");
WiredHome 30:4717cc1f970e 357 svr->send("</form>\r\n");
WiredHome 33:41ac99847df8 358 svr->send("</blockquote>\r\n");
WiredHome 30:4717cc1f970e 359 #if 1
WiredHome 33:41ac99847df8 360 sprintf(buf, "<h2>Directory of [%s]:</h2>\r\n", svr->GetWebRoot());
WiredHome 30:4717cc1f970e 361 svr->send(buf);
WiredHome 30:4717cc1f970e 362 d = opendir(svr->GetWebRoot());
WiredHome 30:4717cc1f970e 363 if ( d != NULL ) {
WiredHome 33:41ac99847df8 364 svr->send("<ul>\r\n");
WiredHome 30:4717cc1f970e 365 while ( (p = readdir(d)) != NULL ) {
WiredHome 33:41ac99847df8 366 if (svr->GetSupportedType(p->d_name) != NULL)
WiredHome 33:41ac99847df8 367 sprintf(buf, "<li><a href='%s'>%s</a> (%d bytes)</li>\r\n", p->d_name, p->d_name, FileSize(svr->GetWebRoot(), p->d_name)); // only supported types linked
WiredHome 33:41ac99847df8 368 else
WiredHome 33:41ac99847df8 369 sprintf(buf, "<li>%s (%d bytes)</li>\r\n", p->d_name, FileSize(svr->GetWebRoot(), p->d_name)); // We could skip/hide these if that makes more sense
WiredHome 30:4717cc1f970e 370 svr->send(buf);
WiredHome 30:4717cc1f970e 371 }
WiredHome 33:41ac99847df8 372 svr->send("</ul>\r\n");
WiredHome 30:4717cc1f970e 373 closedir(d);
WiredHome 30:4717cc1f970e 374 } else {
WiredHome 30:4717cc1f970e 375 svr->send("Unable to open directory!");
WiredHome 30:4717cc1f970e 376 }
WiredHome 30:4717cc1f970e 377 #endif
WiredHome 33:41ac99847df8 378 svr->send("<a href='/'>Back to main</a></body></html>\r\n");
WiredHome 30:4717cc1f970e 379 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 30:4717cc1f970e 380 break;
WiredHome 33:41ac99847df8 381
WiredHome 26:eea4db568404 382 default:
WiredHome 26:eea4db568404 383 printf("unknown command %d\r\n", type);
WiredHome 29:14c47d31a9dc 384 ret = HTTPServer::ACCEPT_ERROR;
WiredHome 26:eea4db568404 385 break;
WiredHome 26:eea4db568404 386 }
WiredHome 26:eea4db568404 387 return ret;
WiredHome 26:eea4db568404 388 }
WiredHome 26:eea4db568404 389
WiredHome 34:77d0f63f612a 390 static void FreeResources(void)
WiredHome 34:77d0f63f612a 391 {
WiredHome 34:77d0f63f612a 392 if (fp) {
WiredHome 34:77d0f63f612a 393 fclose(fp);
WiredHome 34:77d0f63f612a 394 fp = NULL;
WiredHome 34:77d0f63f612a 395 }
WiredHome 34:77d0f63f612a 396 if (fqfn) {
WiredHome 34:77d0f63f612a 397 free(fqfn);
WiredHome 34:77d0f63f612a 398 fqfn = NULL;
WiredHome 34:77d0f63f612a 399 }
WiredHome 34:77d0f63f612a 400 if (contenttype) {
WiredHome 34:77d0f63f612a 401 free(contenttype);
WiredHome 34:77d0f63f612a 402 contenttype = NULL;
WiredHome 34:77d0f63f612a 403 }
WiredHome 34:77d0f63f612a 404 if (pCache) {
WiredHome 34:77d0f63f612a 405 free(pCache);
WiredHome 34:77d0f63f612a 406 pCache = NULL;
WiredHome 34:77d0f63f612a 407 bytesInCache = 0;
WiredHome 34:77d0f63f612a 408 }
WiredHome 34:77d0f63f612a 409 }
WiredHome 34:77d0f63f612a 410
WiredHome 33:41ac99847df8 411 static int FileSize(const char * dir, const char * filename)
WiredHome 33:41ac99847df8 412 {
WiredHome 33:41ac99847df8 413 int size = 0;
WiredHome 33:41ac99847df8 414 char * fqfn = (char *)malloc(strlen(dir) + strlen(filename) + 2);
WiredHome 33:41ac99847df8 415 if (fqfn) {
WiredHome 33:41ac99847df8 416 strcpy(fqfn, dir);
WiredHome 33:41ac99847df8 417 strcat(fqfn, "\\");
WiredHome 33:41ac99847df8 418 strcat(fqfn, filename);
WiredHome 33:41ac99847df8 419 FILE * f = fopen(fqfn,"r");
WiredHome 33:41ac99847df8 420 if (f) {
WiredHome 33:41ac99847df8 421 fseek(f, 0, SEEK_END); // seek to end of file
WiredHome 33:41ac99847df8 422 size = ftell(f);
WiredHome 33:41ac99847df8 423 fclose(f);
WiredHome 33:41ac99847df8 424 }
WiredHome 34:77d0f63f612a 425 free(fqfn);
WiredHome 33:41ac99847df8 426 }
WiredHome 33:41ac99847df8 427 return size;
WiredHome 33:41ac99847df8 428 }
WiredHome 29:14c47d31a9dc 429
WiredHome 33:41ac99847df8 430 // very simple ASCII only function to convert
WiredHome 33:41ac99847df8 431 // UPPERCASE to lowercase.
WiredHome 33:41ac99847df8 432 //
WiredHome 33:41ac99847df8 433 static char _tolower(char c)
WiredHome 33:41ac99847df8 434 {
WiredHome 33:41ac99847df8 435 if (c >= 'A' && c <= 'Z')
WiredHome 33:41ac99847df8 436 c = c - 'A' + 'a';
WiredHome 33:41ac99847df8 437 return c;
WiredHome 33:41ac99847df8 438 }
WiredHome 33:41ac99847df8 439
WiredHome 33:41ac99847df8 440 // case insensitive compare
WiredHome 33:41ac99847df8 441 static int _strnicmp(const char * left, const char * right, int len)
WiredHome 33:41ac99847df8 442 {
WiredHome 33:41ac99847df8 443 if (!left || !right)
WiredHome 33:41ac99847df8 444 return 0; // actually, can't compare so this is a small lie
WiredHome 33:41ac99847df8 445 while (len) {
WiredHome 33:41ac99847df8 446 if (_tolower(*left) < _tolower(*right))
WiredHome 33:41ac99847df8 447 return -1;
WiredHome 33:41ac99847df8 448 else if (_tolower(*left) > _tolower(*right))
WiredHome 33:41ac99847df8 449 return 1;
WiredHome 33:41ac99847df8 450 left++;
WiredHome 33:41ac99847df8 451 right++;
WiredHome 33:41ac99847df8 452 len--;
WiredHome 33:41ac99847df8 453 }
WiredHome 33:41ac99847df8 454 return 0; // match
WiredHome 33:41ac99847df8 455 }
WiredHome 33:41ac99847df8 456
WiredHome 33:41ac99847df8 457 // Search a haystack string for a needle string - case insensitive.
WiredHome 33:41ac99847df8 458 //
WiredHome 33:41ac99847df8 459 static char * _stristr(char * pHaystack, const char * pNeedle)
WiredHome 33:41ac99847df8 460 {
WiredHome 33:41ac99847df8 461 if (!pHaystack || !*pHaystack || !pNeedle || !*pNeedle)
WiredHome 33:41ac99847df8 462 return NULL; // bad params
WiredHome 33:41ac99847df8 463 while (*pHaystack) {
WiredHome 33:41ac99847df8 464 if (_strnicmp(pHaystack, pNeedle, strlen(pNeedle)) == 0)
WiredHome 33:41ac99847df8 465 return pHaystack;
WiredHome 33:41ac99847df8 466 pHaystack++;
WiredHome 33:41ac99847df8 467 }
WiredHome 33:41ac99847df8 468 return NULL;
WiredHome 33:41ac99847df8 469 }
WiredHome 33:41ac99847df8 470
WiredHome 33:41ac99847df8 471 // Search the [possibly] binary haystack for the Needle, which is a string.
WiredHome 33:41ac99847df8 472 // This is used to find the 'boundary' marker in a stream from the network
WiredHome 33:41ac99847df8 473 // interface. The contents between the boundary markers could be text, or
WiredHome 33:41ac99847df8 474 // it could be a binary stream. Since the haystack could be binary, we need
WiredHome 33:41ac99847df8 475 // to control the search by a length parameter.
WiredHome 33:41ac99847df8 476 //
WiredHome 33:41ac99847df8 477 static char * _strnbin(char * pBinstack, const char *pNeedle, int len)
WiredHome 33:41ac99847df8 478 {
WiredHome 33:41ac99847df8 479 char * pLast;
WiredHome 33:41ac99847df8 480 if (!pBinstack || !pNeedle || !*pNeedle || len == 0)
WiredHome 33:41ac99847df8 481 return NULL; // bad params
WiredHome 33:41ac99847df8 482 len -= strlen(pNeedle); // only search [most] of the haystack
WiredHome 33:41ac99847df8 483 pLast = pBinstack + len;
WiredHome 33:41ac99847df8 484 while (pBinstack <= pLast) {
WiredHome 33:41ac99847df8 485 if (_strnicmp(pBinstack, pNeedle, strlen(pNeedle)) == 0)
WiredHome 33:41ac99847df8 486 return pBinstack;
WiredHome 33:41ac99847df8 487 pBinstack++;
WiredHome 33:41ac99847df8 488 }
WiredHome 33:41ac99847df8 489 return NULL;
WiredHome 33:41ac99847df8 490 }