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:
Mon Feb 02 03:03:08 2015 +0000
Revision:
38:962d71d6dd0e
Refresh update, bug fix, mbed application board compatibility.

Who changed what in which revision?

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