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:
Fri Jan 03 19:57:31 2014 +0000
Revision:
33:41ac99847df8
Parent:
30:4717cc1f970e
Child:
34:77d0f63f612a
Ongoing changes are working on supporting file upload.

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