Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of SW_HTTPServer by
SW_HTTPServer.cpp@0:729320f63c5c, 2013-05-31 (annotated)
- Committer:
- WiredHome
- Date:
- Fri May 31 03:13:39 2013 +0000
- Revision:
- 0:729320f63c5c
- Child:
- 2:a29c32190037
First viable version of the Smartware HTTP Server. It handles static and dynamic pages, GET and POST methods.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
WiredHome | 0:729320f63c5c | 1 | // |
WiredHome | 0:729320f63c5c | 2 | // note Copyright © 2013 by Smartware Computing, all rights reserved. |
WiredHome | 0:729320f63c5c | 3 | // Individuals may use this application for evaluation or non-commercial |
WiredHome | 0:729320f63c5c | 4 | // purposes. Within this restriction, changes may be made to this application |
WiredHome | 0:729320f63c5c | 5 | // as long as this copyright notice is retained. The user shall make |
WiredHome | 0:729320f63c5c | 6 | // clear that their work is a derived work, and not the original. |
WiredHome | 0:729320f63c5c | 7 | // Users of this application and sources accept this application "as is" and |
WiredHome | 0:729320f63c5c | 8 | // shall hold harmless Smartware Computing, for any undesired results while |
WiredHome | 0:729320f63c5c | 9 | // using this application - whether real or imagined. |
WiredHome | 0:729320f63c5c | 10 | // |
WiredHome | 0:729320f63c5c | 11 | // author David Smart, Smartware Computing |
WiredHome | 0:729320f63c5c | 12 | // |
WiredHome | 0:729320f63c5c | 13 | #include "mbed.h" |
WiredHome | 0:729320f63c5c | 14 | #include "SW_HTTPServer.h" |
WiredHome | 0:729320f63c5c | 15 | #include "Utility.h" |
WiredHome | 0:729320f63c5c | 16 | |
WiredHome | 0:729320f63c5c | 17 | //#define DEBUG |
WiredHome | 0:729320f63c5c | 18 | |
WiredHome | 0:729320f63c5c | 19 | #define FILESEND_BUF_SIZE 1200 // should keep this <= the ethernet frame size |
WiredHome | 0:729320f63c5c | 20 | |
WiredHome | 0:729320f63c5c | 21 | const char * DEFAULT_FILENAME = "index.htm"; |
WiredHome | 0:729320f63c5c | 22 | |
WiredHome | 0:729320f63c5c | 23 | // Header information to always send |
WiredHome | 0:729320f63c5c | 24 | const char hdr_httpver[] = "HTTP/1.1"; // typically HTTP/1.0 or HTTP/1.1 |
WiredHome | 0:729320f63c5c | 25 | const char hdr_age[] = "Max-age: 0\r\n"; // expires right away (must be \r\n terminated) |
WiredHome | 0:729320f63c5c | 26 | const char hdr_server[] = "Server: Smart_Server v0.1\r\n"; // Server (must be \r\n terminated) |
WiredHome | 0:729320f63c5c | 27 | const char hdr_close[] = "Connection: close\r\n"; // tell the client to close the connection (must be \r\n terminated) |
WiredHome | 0:729320f63c5c | 28 | const char nl[] = "\r\n"; // final \r\n for the termination of the header |
WiredHome | 0:729320f63c5c | 29 | |
WiredHome | 0:729320f63c5c | 30 | static const struct { |
WiredHome | 0:729320f63c5c | 31 | char *ext; |
WiredHome | 0:729320f63c5c | 32 | char *filetype; |
WiredHome | 0:729320f63c5c | 33 | } extensions [] = { |
WiredHome | 0:729320f63c5c | 34 | {".gif", "Content-Type: image/gif\r\n" }, |
WiredHome | 0:729320f63c5c | 35 | {".jpg", "Content-Type: image/jpeg\r\n" }, |
WiredHome | 0:729320f63c5c | 36 | {".jpeg","Content-Type: image/jpeg\r\n" }, |
WiredHome | 0:729320f63c5c | 37 | {".ico", "Content-Type: image/x-icon\r\n"}, |
WiredHome | 0:729320f63c5c | 38 | {".png", "Content-Type: image/png\r\n" }, |
WiredHome | 0:729320f63c5c | 39 | {".zip", "Content-Type: image/zip\r\n" }, |
WiredHome | 0:729320f63c5c | 40 | {".gz", "Content-Type: image/gz\r\n" }, |
WiredHome | 0:729320f63c5c | 41 | {".tar", "Content-Type: image/tar\r\n" }, |
WiredHome | 0:729320f63c5c | 42 | {".htm", "Content-Type: text/html\r\n" }, |
WiredHome | 0:729320f63c5c | 43 | {".html","Content-Type: text/html\r\n" }, |
WiredHome | 0:729320f63c5c | 44 | {0,0} |
WiredHome | 0:729320f63c5c | 45 | }; |
WiredHome | 0:729320f63c5c | 46 | |
WiredHome | 0:729320f63c5c | 47 | HTTPServer::HTTPServer(Wifly * _wf, int port, const char * _webroot, int _maxparams, int _maxdynamicpages, PC * _pc) |
WiredHome | 0:729320f63c5c | 48 | { |
WiredHome | 0:729320f63c5c | 49 | wifly = _wf; |
WiredHome | 0:729320f63c5c | 50 | webroot = (char *)malloc(strlen(_webroot)+1); |
WiredHome | 0:729320f63c5c | 51 | strcpy(webroot, _webroot); |
WiredHome | 0:729320f63c5c | 52 | maxparams = _maxparams; |
WiredHome | 0:729320f63c5c | 53 | maxdynamicpages = _maxdynamicpages; |
WiredHome | 0:729320f63c5c | 54 | params = (namevalue *)malloc(maxparams * sizeof(namevalue)); |
WiredHome | 0:729320f63c5c | 55 | handlers = (handler *)malloc(maxdynamicpages * sizeof(handler)); |
WiredHome | 0:729320f63c5c | 56 | pc = _pc; |
WiredHome | 0:729320f63c5c | 57 | paramcount = 0; |
WiredHome | 0:729320f63c5c | 58 | handlercount = 0; |
WiredHome | 0:729320f63c5c | 59 | server = new TCPSocketServer(); |
WiredHome | 0:729320f63c5c | 60 | timer = new Timer(); |
WiredHome | 0:729320f63c5c | 61 | timer->start(); |
WiredHome | 0:729320f63c5c | 62 | server->bind(port); |
WiredHome | 0:729320f63c5c | 63 | server->listen(); |
WiredHome | 0:729320f63c5c | 64 | server->set_blocking(false); |
WiredHome | 0:729320f63c5c | 65 | server->accept(client); |
WiredHome | 0:729320f63c5c | 66 | } |
WiredHome | 0:729320f63c5c | 67 | |
WiredHome | 0:729320f63c5c | 68 | HTTPServer::~HTTPServer() |
WiredHome | 0:729320f63c5c | 69 | { |
WiredHome | 0:729320f63c5c | 70 | free(webroot); |
WiredHome | 0:729320f63c5c | 71 | webroot = NULL; |
WiredHome | 0:729320f63c5c | 72 | } |
WiredHome | 0:729320f63c5c | 73 | |
WiredHome | 0:729320f63c5c | 74 | bool HTTPServer::RegisterHandler(const char * path, Handler callback) |
WiredHome | 0:729320f63c5c | 75 | { |
WiredHome | 0:729320f63c5c | 76 | if (handlercount < maxdynamicpages && path && callback) { |
WiredHome | 0:729320f63c5c | 77 | handlers[handlercount].path = (char *)malloc(strlen(path)+1); |
WiredHome | 0:729320f63c5c | 78 | memcpy(handlers[handlercount].path, path, strlen(path)+1); |
WiredHome | 0:729320f63c5c | 79 | handlers[handlercount].callback = callback; |
WiredHome | 0:729320f63c5c | 80 | handlercount++; |
WiredHome | 0:729320f63c5c | 81 | return true; |
WiredHome | 0:729320f63c5c | 82 | } else { |
WiredHome | 0:729320f63c5c | 83 | return false; |
WiredHome | 0:729320f63c5c | 84 | } |
WiredHome | 0:729320f63c5c | 85 | } |
WiredHome | 0:729320f63c5c | 86 | |
WiredHome | 0:729320f63c5c | 87 | // ip_process() |
WiredHome | 0:729320f63c5c | 88 | // |
WiredHome | 0:729320f63c5c | 89 | // *OPEN*GET /x=1 HTTP/1.1 |
WiredHome | 0:729320f63c5c | 90 | // Host: 192.168.1.140 |
WiredHome | 0:729320f63c5c | 91 | // Connection: keep-alive |
WiredHome | 0:729320f63c5c | 92 | // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
WiredHome | 0:729320f63c5c | 93 | // User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36 |
WiredHome | 0:729320f63c5c | 94 | // Accept-Encoding: gzip,deflate,sdch |
WiredHome | 0:729320f63c5c | 95 | // Accept-Language: en-US,en;q=0.8 |
WiredHome | 0:729320f63c5c | 96 | // |
WiredHome | 0:729320f63c5c | 97 | void HTTPServer::ip_process() |
WiredHome | 0:729320f63c5c | 98 | { |
WiredHome | 0:729320f63c5c | 99 | #define MAXRECSIZE 2000 |
WiredHome | 0:729320f63c5c | 100 | static char buffer[MAXRECSIZE]; |
WiredHome | 0:729320f63c5c | 101 | static char * bPtr = buffer; |
WiredHome | 0:729320f63c5c | 102 | |
WiredHome | 0:729320f63c5c | 103 | static char * queryType = NULL; |
WiredHome | 0:729320f63c5c | 104 | static char * queryString = NULL; |
WiredHome | 0:729320f63c5c | 105 | static char * hostString = NULL; |
WiredHome | 0:729320f63c5c | 106 | static char * contentLength = NULL; |
WiredHome | 0:729320f63c5c | 107 | static char * contentType = NULL; |
WiredHome | 0:729320f63c5c | 108 | static char * postQueryString = NULL; |
WiredHome | 0:729320f63c5c | 109 | static Timer tmr; |
WiredHome | 0:729320f63c5c | 110 | typedef enum { Idle, Receiving, Reply , Waiting, Sending, WaitingToClose } state; |
WiredHome | 0:729320f63c5c | 111 | static state op = Idle; |
WiredHome | 0:729320f63c5c | 112 | int n; |
WiredHome | 0:729320f63c5c | 113 | |
WiredHome | 0:729320f63c5c | 114 | switch(op) { |
WiredHome | 0:729320f63c5c | 115 | case Reply: |
WiredHome | 0:729320f63c5c | 116 | tmr.reset(); |
WiredHome | 0:729320f63c5c | 117 | tmr.start(); |
WiredHome | 0:729320f63c5c | 118 | op = Waiting; |
WiredHome | 0:729320f63c5c | 119 | break; |
WiredHome | 0:729320f63c5c | 120 | case Waiting: |
WiredHome | 0:729320f63c5c | 121 | if (tmr.read_ms() > 10) { |
WiredHome | 0:729320f63c5c | 122 | //pc->printf("%06d delay expired, now send.\r\n", timer->read_ms()); |
WiredHome | 0:729320f63c5c | 123 | op = Sending; |
WiredHome | 0:729320f63c5c | 124 | } |
WiredHome | 0:729320f63c5c | 125 | break; |
WiredHome | 0:729320f63c5c | 126 | case Sending: |
WiredHome | 0:729320f63c5c | 127 | if (strcmp(queryType, "GET") == 0 |
WiredHome | 0:729320f63c5c | 128 | || strcmp(queryType, "POST") == 0) { |
WiredHome | 0:729320f63c5c | 129 | if (!(queryString[0] == '.' && queryString[1] == '.')) { |
WiredHome | 0:729320f63c5c | 130 | const char * fType; |
WiredHome | 0:729320f63c5c | 131 | bool regHandled = false; |
WiredHome | 0:729320f63c5c | 132 | |
WiredHome | 0:729320f63c5c | 133 | // Registered Dynamic Handler |
WiredHome | 0:729320f63c5c | 134 | // If this queryString is in the list of registered handlers, call that |
WiredHome | 0:729320f63c5c | 135 | for (int i=0; i<handlercount; i++) { |
WiredHome | 0:729320f63c5c | 136 | if (strcmp(handlers[i].path, queryString) == 0) { |
WiredHome | 0:729320f63c5c | 137 | (*handlers[i].callback)(this, queryString, params, paramcount); |
WiredHome | 0:729320f63c5c | 138 | regHandled = true; |
WiredHome | 0:729320f63c5c | 139 | break; // we only execute the first one |
WiredHome | 0:729320f63c5c | 140 | } |
WiredHome | 0:729320f63c5c | 141 | } |
WiredHome | 0:729320f63c5c | 142 | |
WiredHome | 0:729320f63c5c | 143 | if (!regHandled) { |
WiredHome | 0:729320f63c5c | 144 | // Otherwise, this queryString must be trying to reference a static file |
WiredHome | 0:729320f63c5c | 145 | if (queryString[strlen(queryString)-1] == '/') { |
WiredHome | 0:729320f63c5c | 146 | queryString = rewriteWithDefaultFile(queryString); |
WiredHome | 0:729320f63c5c | 147 | } |
WiredHome | 0:729320f63c5c | 148 | // see if we support this file type |
WiredHome | 0:729320f63c5c | 149 | fType = GetSupportedType(queryString); |
WiredHome | 0:729320f63c5c | 150 | if (fType) { |
WiredHome | 0:729320f63c5c | 151 | queryString = rewritePrependWebroot(queryString); |
WiredHome | 0:729320f63c5c | 152 | SendFile(queryString, fType); |
WiredHome | 0:729320f63c5c | 153 | } else { |
WiredHome | 0:729320f63c5c | 154 | //pc->printf("Unsupported file type %s\r\n", queryString); |
WiredHome | 0:729320f63c5c | 155 | header(404, "Not Found", "Pragma: err - Unsupported type\r\n"); |
WiredHome | 0:729320f63c5c | 156 | } |
WiredHome | 0:729320f63c5c | 157 | } |
WiredHome | 0:729320f63c5c | 158 | } else { |
WiredHome | 0:729320f63c5c | 159 | //pc->printf("Unsupported path %s\r\n", queryString); |
WiredHome | 0:729320f63c5c | 160 | header(400, "Bad Request", "Pragma: err - Unsupported path\r\n"); |
WiredHome | 0:729320f63c5c | 161 | } |
WiredHome | 0:729320f63c5c | 162 | } else { |
WiredHome | 0:729320f63c5c | 163 | //pc->printf("Unsupported query type %s\r\n", queryType); |
WiredHome | 0:729320f63c5c | 164 | header(400, "Bad Request", "Pragma: err - Unsupported query type\r\n"); |
WiredHome | 0:729320f63c5c | 165 | } |
WiredHome | 0:729320f63c5c | 166 | tmr.reset(); |
WiredHome | 0:729320f63c5c | 167 | if (queryType) { |
WiredHome | 0:729320f63c5c | 168 | free(queryType); |
WiredHome | 0:729320f63c5c | 169 | queryType = NULL; |
WiredHome | 0:729320f63c5c | 170 | } |
WiredHome | 0:729320f63c5c | 171 | if (queryString) { |
WiredHome | 0:729320f63c5c | 172 | free(queryString); |
WiredHome | 0:729320f63c5c | 173 | queryString = NULL; |
WiredHome | 0:729320f63c5c | 174 | } |
WiredHome | 0:729320f63c5c | 175 | if (hostString) { |
WiredHome | 0:729320f63c5c | 176 | free(hostString); |
WiredHome | 0:729320f63c5c | 177 | hostString = NULL; |
WiredHome | 0:729320f63c5c | 178 | } |
WiredHome | 0:729320f63c5c | 179 | if (contentLength) { |
WiredHome | 0:729320f63c5c | 180 | free(contentLength); |
WiredHome | 0:729320f63c5c | 181 | contentLength = NULL; |
WiredHome | 0:729320f63c5c | 182 | } |
WiredHome | 0:729320f63c5c | 183 | if (contentType) { |
WiredHome | 0:729320f63c5c | 184 | free(contentType); |
WiredHome | 0:729320f63c5c | 185 | contentType = NULL; |
WiredHome | 0:729320f63c5c | 186 | } |
WiredHome | 0:729320f63c5c | 187 | if (postQueryString) { |
WiredHome | 0:729320f63c5c | 188 | free(postQueryString); |
WiredHome | 0:729320f63c5c | 189 | postQueryString = NULL; |
WiredHome | 0:729320f63c5c | 190 | } |
WiredHome | 0:729320f63c5c | 191 | op = WaitingToClose; |
WiredHome | 0:729320f63c5c | 192 | break; |
WiredHome | 0:729320f63c5c | 193 | case WaitingToClose: |
WiredHome | 0:729320f63c5c | 194 | if (tmr.read_ms() > 10) { |
WiredHome | 0:729320f63c5c | 195 | //pc->printf("closing after %d\r\n", tmr.read_ms()); |
WiredHome | 0:729320f63c5c | 196 | close_connection(); |
WiredHome | 0:729320f63c5c | 197 | op = Idle; |
WiredHome | 0:729320f63c5c | 198 | } |
WiredHome | 0:729320f63c5c | 199 | break; |
WiredHome | 0:729320f63c5c | 200 | case Idle: |
WiredHome | 0:729320f63c5c | 201 | //if (server->accept(client) == 0) |
WiredHome | 0:729320f63c5c | 202 | // op = Receiving; |
WiredHome | 0:729320f63c5c | 203 | //break; |
WiredHome | 0:729320f63c5c | 204 | // Idle and Receiving are the same until I figure out the accept method |
WiredHome | 0:729320f63c5c | 205 | // so it doesn't eat a few chars after the *OPEN* |
WiredHome | 0:729320f63c5c | 206 | case Receiving: |
WiredHome | 0:729320f63c5c | 207 | n = client.receive(bPtr, sizeof(buffer) - (bPtr - buffer)); |
WiredHome | 0:729320f63c5c | 208 | if (n < 0) { |
WiredHome | 0:729320f63c5c | 209 | pc->printf("*** client.receive error: %d\r\n", n); |
WiredHome | 0:729320f63c5c | 210 | } else if (n) { |
WiredHome | 0:729320f63c5c | 211 | char * dblCR; |
WiredHome | 0:729320f63c5c | 212 | bPtr[n] = '\0'; |
WiredHome | 0:729320f63c5c | 213 | wifly->setConnectionState(true); // Bad hack to have to do this here.... |
WiredHome | 0:729320f63c5c | 214 | //pc->printf("%s", bPtr); |
WiredHome | 0:729320f63c5c | 215 | // Buffer could have partial, but the double \r\n is the key |
WiredHome | 0:729320f63c5c | 216 | // *OPEN*QueryType QueryString HTTP/1.1.... |
WiredHome | 0:729320f63c5c | 217 | // QueryType:= GET |
WiredHome | 0:729320f63c5c | 218 | // *OPEN*GET /QueryString HTTP/1.1\r\n |
WiredHome | 0:729320f63c5c | 219 | // *OPEN*GET /QueryString HTTP/1.1\r\nHeader stuf |
WiredHome | 0:729320f63c5c | 220 | // *OPEN*GET /QueryString HTTP/1.1\r\nHeader stuff...\r\n\r\n |
WiredHome | 0:729320f63c5c | 221 | dblCR = strstr(buffer,"\r\n\r\n"); |
WiredHome | 0:729320f63c5c | 222 | if (dblCR) { // Have to scan from the beginning in case split on \r |
WiredHome | 0:729320f63c5c | 223 | //pc->printf("\r\n\r\nThe Whole Header:\r\n%s\r\n\r\n", buffer); |
WiredHome | 0:729320f63c5c | 224 | char * soRec = buffer; // start of the next record of text |
WiredHome | 0:729320f63c5c | 225 | char * eoRec = strchr(soRec, '\n'); // search for end of a record |
WiredHome | 0:729320f63c5c | 226 | while (eoRec) { |
WiredHome | 0:729320f63c5c | 227 | *eoRec = '\0'; |
WiredHome | 0:729320f63c5c | 228 | if (*(eoRec-1) == '\r') |
WiredHome | 0:729320f63c5c | 229 | *(eoRec-1) = '\0'; |
WiredHome | 0:729320f63c5c | 230 | if (!Extract(soRec, "*OPEN*", &queryType)) |
WiredHome | 0:729320f63c5c | 231 | Extract(soRec, "*CLOS*", &queryType); |
WiredHome | 0:729320f63c5c | 232 | if (queryType) |
WiredHome | 0:729320f63c5c | 233 | Extract(soRec, queryType, &queryString); |
WiredHome | 0:729320f63c5c | 234 | Extract(soRec, "Host: ", &hostString); |
WiredHome | 0:729320f63c5c | 235 | Extract(soRec, "Content-Length: ", &contentLength); |
WiredHome | 0:729320f63c5c | 236 | Extract(soRec, "Content-Type: ", &contentType); |
WiredHome | 0:729320f63c5c | 237 | soRec = eoRec + 1; |
WiredHome | 0:729320f63c5c | 238 | eoRec = strchr(soRec, '\n'); |
WiredHome | 0:729320f63c5c | 239 | } |
WiredHome | 0:729320f63c5c | 240 | if (queryString) { |
WiredHome | 0:729320f63c5c | 241 | // We have enough to try to reply |
WiredHome | 0:729320f63c5c | 242 | //pc->printf("create reply queryType{%s}, queryString{%s}\r\n", queryType, queryString); |
WiredHome | 0:729320f63c5c | 243 | // parse params - if any |
WiredHome | 0:729320f63c5c | 244 | // /file.htm?name1=value1&name2=value2... |
WiredHome | 0:729320f63c5c | 245 | // /file.htm?name1&name2=value2... |
WiredHome | 0:729320f63c5c | 246 | paramcount = 0; |
WiredHome | 0:729320f63c5c | 247 | char * paramDelim = strchr(queryString, '?'); |
WiredHome | 0:729320f63c5c | 248 | if (paramDelim) { |
WiredHome | 0:729320f63c5c | 249 | *paramDelim++ = '\0'; |
WiredHome | 0:729320f63c5c | 250 | UnescapeString(paramDelim); // everything after the '?' |
WiredHome | 0:729320f63c5c | 251 | ParseParameters(paramDelim); // pointing at the NULL, but there are params beyond |
WiredHome | 0:729320f63c5c | 252 | } |
WiredHome | 0:729320f63c5c | 253 | //for (int i=0; i<paramcount; i++) |
WiredHome | 0:729320f63c5c | 254 | // pc->printf("param %d '%s'='%s'\r\n", i, params[i].name, params[i].value); |
WiredHome | 0:729320f63c5c | 255 | } else { |
WiredHome | 0:729320f63c5c | 256 | pc->printf("not found\r\n"); |
WiredHome | 0:729320f63c5c | 257 | } |
WiredHome | 0:729320f63c5c | 258 | op = Reply; |
WiredHome | 0:729320f63c5c | 259 | buffer[0] = 0; |
WiredHome | 0:729320f63c5c | 260 | bPtr = buffer; |
WiredHome | 0:729320f63c5c | 261 | |
WiredHome | 0:729320f63c5c | 262 | // This part parses the extra data on a POST method. |
WiredHome | 0:729320f63c5c | 263 | // Since there has to be a dynamic handler registered for this |
WiredHome | 0:729320f63c5c | 264 | // it would make sense to move some of this responsibility to |
WiredHome | 0:729320f63c5c | 265 | // that handler. It could then choose if it wanted to allocate |
WiredHome | 0:729320f63c5c | 266 | // the requested 'Content-Length' amount of memory. |
WiredHome | 0:729320f63c5c | 267 | // Should we check the 'Content-Type' to see if it is |
WiredHome | 0:729320f63c5c | 268 | // 'application/x-www-form-urlencoded'? |
WiredHome | 0:729320f63c5c | 269 | int postBytes = atoi(contentLength); |
WiredHome | 0:729320f63c5c | 270 | //pc->printf("Content-Length = %d\r\n", postBytes); |
WiredHome | 0:729320f63c5c | 271 | if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) { |
WiredHome | 0:729320f63c5c | 272 | if (postBytes) { |
WiredHome | 0:729320f63c5c | 273 | postQueryString = (char *)malloc(postBytes + 1); |
WiredHome | 0:729320f63c5c | 274 | if (postQueryString) { |
WiredHome | 0:729320f63c5c | 275 | char * offset; |
WiredHome | 0:729320f63c5c | 276 | int len; |
WiredHome | 0:729320f63c5c | 277 | |
WiredHome | 0:729320f63c5c | 278 | dblCR += 4; // If we slurped up any of the POST, |
WiredHome | 0:729320f63c5c | 279 | while (*dblCR && *dblCR <= ' ') |
WiredHome | 0:729320f63c5c | 280 | dblCR++; |
WiredHome | 0:729320f63c5c | 281 | strcpy(postQueryString, dblCR); // copy that in and then get the rest |
WiredHome | 0:729320f63c5c | 282 | while ((len = strlen(postQueryString)) < postBytes) { |
WiredHome | 0:729320f63c5c | 283 | int n; |
WiredHome | 0:729320f63c5c | 284 | offset = postQueryString + len; |
WiredHome | 0:729320f63c5c | 285 | n = client.receive(offset, postBytes - len); |
WiredHome | 0:729320f63c5c | 286 | if (n >=0) |
WiredHome | 0:729320f63c5c | 287 | offset[n] = '\0'; |
WiredHome | 0:729320f63c5c | 288 | } |
WiredHome | 0:729320f63c5c | 289 | if (len >= 0) { |
WiredHome | 0:729320f63c5c | 290 | UnescapeString(postQueryString); |
WiredHome | 0:729320f63c5c | 291 | ParseParameters(postQueryString); |
WiredHome | 0:729320f63c5c | 292 | } |
WiredHome | 0:729320f63c5c | 293 | } |
WiredHome | 0:729320f63c5c | 294 | } |
WiredHome | 0:729320f63c5c | 295 | } |
WiredHome | 0:729320f63c5c | 296 | } else { |
WiredHome | 0:729320f63c5c | 297 | // received partial, but not the double (\r\n\r\n) |
WiredHome | 0:729320f63c5c | 298 | bPtr += n; |
WiredHome | 0:729320f63c5c | 299 | } |
WiredHome | 0:729320f63c5c | 300 | } |
WiredHome | 0:729320f63c5c | 301 | break; |
WiredHome | 0:729320f63c5c | 302 | default: |
WiredHome | 0:729320f63c5c | 303 | // not expected to arrive here |
WiredHome | 0:729320f63c5c | 304 | op = Idle; |
WiredHome | 0:729320f63c5c | 305 | break; |
WiredHome | 0:729320f63c5c | 306 | } |
WiredHome | 0:729320f63c5c | 307 | } |
WiredHome | 0:729320f63c5c | 308 | |
WiredHome | 0:729320f63c5c | 309 | |
WiredHome | 0:729320f63c5c | 310 | const char * HTTPServer::GetSupportedType(const char * filename) |
WiredHome | 0:729320f63c5c | 311 | { |
WiredHome | 0:729320f63c5c | 312 | int i; |
WiredHome | 0:729320f63c5c | 313 | int buflen = strlen(filename); |
WiredHome | 0:729320f63c5c | 314 | int extlen; |
WiredHome | 0:729320f63c5c | 315 | |
WiredHome | 0:729320f63c5c | 316 | for (i=0; extensions[i].ext != 0; i++) { |
WiredHome | 0:729320f63c5c | 317 | extlen = strlen(extensions[i].ext); |
WiredHome | 0:729320f63c5c | 318 | if ( !strncmp(&filename[buflen-extlen], extensions[i].ext, extlen)) { |
WiredHome | 0:729320f63c5c | 319 | return extensions[i].filetype; |
WiredHome | 0:729320f63c5c | 320 | } |
WiredHome | 0:729320f63c5c | 321 | } |
WiredHome | 0:729320f63c5c | 322 | return NULL; |
WiredHome | 0:729320f63c5c | 323 | } |
WiredHome | 0:729320f63c5c | 324 | |
WiredHome | 0:729320f63c5c | 325 | void HTTPServer::send(const char * msg, int bytes) |
WiredHome | 0:729320f63c5c | 326 | { |
WiredHome | 0:729320f63c5c | 327 | if (bytes == -1) |
WiredHome | 0:729320f63c5c | 328 | bytes = strlen(msg); |
WiredHome | 0:729320f63c5c | 329 | wifly->send(msg, bytes); |
WiredHome | 0:729320f63c5c | 330 | #ifdef DEBUG |
WiredHome | 0:729320f63c5c | 331 | // pc->printf("%s", msg); |
WiredHome | 0:729320f63c5c | 332 | #endif |
WiredHome | 0:729320f63c5c | 333 | } |
WiredHome | 0:729320f63c5c | 334 | |
WiredHome | 0:729320f63c5c | 335 | bool HTTPServer::SendFile(const char * filename, const char * filetype) |
WiredHome | 0:729320f63c5c | 336 | { |
WiredHome | 0:729320f63c5c | 337 | FILE * fp; |
WiredHome | 0:729320f63c5c | 338 | |
WiredHome | 0:729320f63c5c | 339 | fp = fopen(filename,"rb"); |
WiredHome | 0:729320f63c5c | 340 | // is supported, now try to open it |
WiredHome | 0:729320f63c5c | 341 | if (fp) { // can open it |
WiredHome | 0:729320f63c5c | 342 | char *fbuffer = (char *)malloc(FILESEND_BUF_SIZE); |
WiredHome | 0:729320f63c5c | 343 | int bytes; |
WiredHome | 0:729320f63c5c | 344 | |
WiredHome | 0:729320f63c5c | 345 | //pc->printf("sending [%s]\r\n", filename); |
WiredHome | 0:729320f63c5c | 346 | header(200, "OK", filetype); |
WiredHome | 0:729320f63c5c | 347 | // now the file itself |
WiredHome | 0:729320f63c5c | 348 | bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp); |
WiredHome | 0:729320f63c5c | 349 | while (bytes > 0) { |
WiredHome | 0:729320f63c5c | 350 | send(fbuffer, bytes); |
WiredHome | 0:729320f63c5c | 351 | bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp); |
WiredHome | 0:729320f63c5c | 352 | } |
WiredHome | 0:729320f63c5c | 353 | free(fbuffer); |
WiredHome | 0:729320f63c5c | 354 | fclose(fp); |
WiredHome | 0:729320f63c5c | 355 | return true; |
WiredHome | 0:729320f63c5c | 356 | } else { |
WiredHome | 0:729320f63c5c | 357 | //pc->printf("Can't open %s\r\n", filename); |
WiredHome | 0:729320f63c5c | 358 | header(404, "Not Found", "Pragma: err - Can't open\r\n"); |
WiredHome | 0:729320f63c5c | 359 | return false; |
WiredHome | 0:729320f63c5c | 360 | } |
WiredHome | 0:729320f63c5c | 361 | } |
WiredHome | 0:729320f63c5c | 362 | |
WiredHome | 0:729320f63c5c | 363 | int HTTPServer::HexCharToInt(char c) |
WiredHome | 0:729320f63c5c | 364 | { |
WiredHome | 0:729320f63c5c | 365 | if (c >= 'a' && c <= 'f') |
WiredHome | 0:729320f63c5c | 366 | return (c - 'a' + 10); |
WiredHome | 0:729320f63c5c | 367 | else if (c >= 'A' && c <= 'F') |
WiredHome | 0:729320f63c5c | 368 | return (c - 'A' + 10); |
WiredHome | 0:729320f63c5c | 369 | else if (c >= '0' && c <= '9') |
WiredHome | 0:729320f63c5c | 370 | return c - '0'; |
WiredHome | 0:729320f63c5c | 371 | else |
WiredHome | 0:729320f63c5c | 372 | return 0; |
WiredHome | 0:729320f63c5c | 373 | } |
WiredHome | 0:729320f63c5c | 374 | |
WiredHome | 0:729320f63c5c | 375 | char HTTPServer::HexPairToChar(char * p) |
WiredHome | 0:729320f63c5c | 376 | { |
WiredHome | 0:729320f63c5c | 377 | return 16 * HexCharToInt(*p) + HexCharToInt(*(p+1)); |
WiredHome | 0:729320f63c5c | 378 | } |
WiredHome | 0:729320f63c5c | 379 | |
WiredHome | 0:729320f63c5c | 380 | void HTTPServer::UnescapeString(char * encoded) |
WiredHome | 0:729320f63c5c | 381 | { |
WiredHome | 0:729320f63c5c | 382 | char *p; |
WiredHome | 0:729320f63c5c | 383 | |
WiredHome | 0:729320f63c5c | 384 | // first convert '+' to ' ' |
WiredHome | 0:729320f63c5c | 385 | p = strchr(encoded, '+'); |
WiredHome | 0:729320f63c5c | 386 | while (p) { |
WiredHome | 0:729320f63c5c | 387 | *p = ' '; |
WiredHome | 0:729320f63c5c | 388 | p = strchr(encoded, '+'); |
WiredHome | 0:729320f63c5c | 389 | } |
WiredHome | 0:729320f63c5c | 390 | // then convert hex '%xx' to char 'x' |
WiredHome | 0:729320f63c5c | 391 | p = strchr(encoded, '%'); |
WiredHome | 0:729320f63c5c | 392 | while (p) { |
WiredHome | 0:729320f63c5c | 393 | if (strchr("0123456789ABCDEFabcdef", *(p+1)) |
WiredHome | 0:729320f63c5c | 394 | && strchr("0123456789ABCDEFabcdef", *(p+2)) ) { |
WiredHome | 0:729320f63c5c | 395 | *p = HexPairToChar(p+1); |
WiredHome | 0:729320f63c5c | 396 | p++; // advance past the % |
WiredHome | 0:729320f63c5c | 397 | char * a = p; |
WiredHome | 0:729320f63c5c | 398 | char * b = p + 2; |
WiredHome | 0:729320f63c5c | 399 | do { |
WiredHome | 0:729320f63c5c | 400 | *a++ = *b++; |
WiredHome | 0:729320f63c5c | 401 | } while (*b); |
WiredHome | 0:729320f63c5c | 402 | *a = '\0'; |
WiredHome | 0:729320f63c5c | 403 | } |
WiredHome | 0:729320f63c5c | 404 | p = strchr(p, '%'); |
WiredHome | 0:729320f63c5c | 405 | } |
WiredHome | 0:729320f63c5c | 406 | } |
WiredHome | 0:729320f63c5c | 407 | |
WiredHome | 0:729320f63c5c | 408 | const char * HTTPServer::GetParameter(const char * name) |
WiredHome | 0:729320f63c5c | 409 | { |
WiredHome | 0:729320f63c5c | 410 | for (int i=0; i<paramcount; i++) { |
WiredHome | 0:729320f63c5c | 411 | if (strcmp(params[i].name, name) == 0) { |
WiredHome | 0:729320f63c5c | 412 | return params[i].value; |
WiredHome | 0:729320f63c5c | 413 | } |
WiredHome | 0:729320f63c5c | 414 | } |
WiredHome | 0:729320f63c5c | 415 | return NULL; |
WiredHome | 0:729320f63c5c | 416 | } |
WiredHome | 0:729320f63c5c | 417 | |
WiredHome | 0:729320f63c5c | 418 | // this=that&who=what&more=stuff... |
WiredHome | 0:729320f63c5c | 419 | // ^ ^ ^ |
WiredHome | 0:729320f63c5c | 420 | void HTTPServer::ParseParameters(char * pName) |
WiredHome | 0:729320f63c5c | 421 | { |
WiredHome | 0:729320f63c5c | 422 | char * pVal; |
WiredHome | 0:729320f63c5c | 423 | char * pNextName; |
WiredHome | 0:729320f63c5c | 424 | |
WiredHome | 0:729320f63c5c | 425 | // Parse params |
WiredHome | 0:729320f63c5c | 426 | pVal = strchr(pName, '#'); // If there is a '#fragment_id', we can ignore it |
WiredHome | 0:729320f63c5c | 427 | if (pVal) |
WiredHome | 0:729320f63c5c | 428 | *pVal = '\0'; |
WiredHome | 0:729320f63c5c | 429 | do { |
WiredHome | 0:729320f63c5c | 430 | //pc->printf("Parse(%s)\r\n", pName); |
WiredHome | 0:729320f63c5c | 431 | //*pName++ = '\0'; // already '\0' on the first entry |
WiredHome | 0:729320f63c5c | 432 | params[paramcount].name = pName; |
WiredHome | 0:729320f63c5c | 433 | pVal = strchr(pName, '='); |
WiredHome | 0:729320f63c5c | 434 | pNextName = strchr(pName,'&'); |
WiredHome | 0:729320f63c5c | 435 | if (pVal) { |
WiredHome | 0:729320f63c5c | 436 | if (pNextName == NULL || (pNextName && pNextName > pVal)) { |
WiredHome | 0:729320f63c5c | 437 | *pVal++ = '\0'; |
WiredHome | 0:729320f63c5c | 438 | params[paramcount].value = pVal; |
WiredHome | 0:729320f63c5c | 439 | pName = pVal; |
WiredHome | 0:729320f63c5c | 440 | } |
WiredHome | 0:729320f63c5c | 441 | } |
WiredHome | 0:729320f63c5c | 442 | //pc->printf(" [%s]=[%s]\r\n", params[paramcount].name, params[paramcount].value); |
WiredHome | 0:729320f63c5c | 443 | paramcount++; |
WiredHome | 0:729320f63c5c | 444 | if (pNextName) { |
WiredHome | 0:729320f63c5c | 445 | pName = pNextName; |
WiredHome | 0:729320f63c5c | 446 | *pName++ = '\0'; |
WiredHome | 0:729320f63c5c | 447 | } else { |
WiredHome | 0:729320f63c5c | 448 | pName = NULL; |
WiredHome | 0:729320f63c5c | 449 | } |
WiredHome | 0:729320f63c5c | 450 | } while (pName && paramcount < maxparams); |
WiredHome | 0:729320f63c5c | 451 | } |
WiredHome | 0:729320f63c5c | 452 | |
WiredHome | 0:729320f63c5c | 453 | |
WiredHome | 0:729320f63c5c | 454 | void HTTPServer::GetRemoteAddr(char * str, int size) |
WiredHome | 0:729320f63c5c | 455 | { |
WiredHome | 0:729320f63c5c | 456 | bool res = false; |
WiredHome | 0:729320f63c5c | 457 | char *p; |
WiredHome | 0:729320f63c5c | 458 | |
WiredHome | 0:729320f63c5c | 459 | res = wifly->sendCommand("show z\r", "NO", str, 5000); |
WiredHome | 0:729320f63c5c | 460 | wait(0.2); // how long to wait is fragile, but need the rest of the chars to come in... |
WiredHome | 0:729320f63c5c | 461 | if (res) { |
WiredHome | 0:729320f63c5c | 462 | p = strchr(str, '\n'); // truncate after the octets. |
WiredHome | 0:729320f63c5c | 463 | if (p) *p = '\0'; |
WiredHome | 0:729320f63c5c | 464 | p = strchr(str, ' '); // or a space |
WiredHome | 0:729320f63c5c | 465 | if (p) *p = '\0'; |
WiredHome | 0:729320f63c5c | 466 | p = strchr(str, '<'); // or a < |
WiredHome | 0:729320f63c5c | 467 | if (p) *p = '\0'; |
WiredHome | 0:729320f63c5c | 468 | } |
WiredHome | 0:729320f63c5c | 469 | wifly->exit(); |
WiredHome | 0:729320f63c5c | 470 | } |
WiredHome | 0:729320f63c5c | 471 | |
WiredHome | 0:729320f63c5c | 472 | |
WiredHome | 0:729320f63c5c | 473 | void HTTPServer::header(int code, const char * code_text, const char * content_type, const char * optional_text) |
WiredHome | 0:729320f63c5c | 474 | { |
WiredHome | 0:729320f63c5c | 475 | char http[100]; |
WiredHome | 0:729320f63c5c | 476 | |
WiredHome | 0:729320f63c5c | 477 | sprintf(http, "%s %i %s\r\n", hdr_httpver, code, code_text); |
WiredHome | 0:729320f63c5c | 478 | send(http); |
WiredHome | 0:729320f63c5c | 479 | send(hdr_age); |
WiredHome | 0:729320f63c5c | 480 | send(hdr_server); |
WiredHome | 0:729320f63c5c | 481 | if (content_type) { |
WiredHome | 0:729320f63c5c | 482 | send(content_type); |
WiredHome | 0:729320f63c5c | 483 | } |
WiredHome | 0:729320f63c5c | 484 | if (optional_text) { |
WiredHome | 0:729320f63c5c | 485 | send(optional_text); |
WiredHome | 0:729320f63c5c | 486 | } |
WiredHome | 0:729320f63c5c | 487 | send(hdr_close); |
WiredHome | 0:729320f63c5c | 488 | send(nl); |
WiredHome | 0:729320f63c5c | 489 | } |
WiredHome | 0:729320f63c5c | 490 | |
WiredHome | 0:729320f63c5c | 491 | void HTTPServer::close_connection( ) |
WiredHome | 0:729320f63c5c | 492 | { |
WiredHome | 0:729320f63c5c | 493 | #ifndef DEBUG |
WiredHome | 0:729320f63c5c | 494 | wifly->close(); |
WiredHome | 0:729320f63c5c | 495 | #else // DEBUG |
WiredHome | 0:729320f63c5c | 496 | if (!wifly->close()) |
WiredHome | 0:729320f63c5c | 497 | pc->printf("Couldn't close connection\r\n"); |
WiredHome | 0:729320f63c5c | 498 | #endif |
WiredHome | 0:729320f63c5c | 499 | } |
WiredHome | 0:729320f63c5c | 500 | |
WiredHome | 0:729320f63c5c | 501 | bool HTTPServer::Extract(char * haystack, char * needle, char ** string) |
WiredHome | 0:729320f63c5c | 502 | { |
WiredHome | 0:729320f63c5c | 503 | bool ret = false; // assume failure until proven otherwise |
WiredHome | 0:729320f63c5c | 504 | char * qs = NULL; |
WiredHome | 0:729320f63c5c | 505 | char * eqs = NULL; |
WiredHome | 0:729320f63c5c | 506 | char * container = NULL; |
WiredHome | 0:729320f63c5c | 507 | char * get = strstr(haystack, needle); // what if not at the front? |
WiredHome | 0:729320f63c5c | 508 | if (get) { |
WiredHome | 0:729320f63c5c | 509 | // Seems to be a valid "...GET /QueryString HTTP/1.1" |
WiredHome | 0:729320f63c5c | 510 | qs = get + strlen(needle); // in case the needle didn't have space delimiters |
WiredHome | 0:729320f63c5c | 511 | while (*qs == ' ') |
WiredHome | 0:729320f63c5c | 512 | qs++; |
WiredHome | 0:729320f63c5c | 513 | // /QueryString\0HTTP/1.1\0\0 |
WiredHome | 0:729320f63c5c | 514 | if (*string) // recycle old string when working a new one |
WiredHome | 0:729320f63c5c | 515 | free(*string); |
WiredHome | 0:729320f63c5c | 516 | container = (char *)malloc(strlen(qs)); |
WiredHome | 0:729320f63c5c | 517 | if (container) { |
WiredHome | 0:729320f63c5c | 518 | strcpy(container, qs); |
WiredHome | 0:729320f63c5c | 519 | eqs = strchr(container, ' '); |
WiredHome | 0:729320f63c5c | 520 | if (eqs) |
WiredHome | 0:729320f63c5c | 521 | *eqs = '\0'; |
WiredHome | 0:729320f63c5c | 522 | *string = container; |
WiredHome | 0:729320f63c5c | 523 | ret = true; |
WiredHome | 0:729320f63c5c | 524 | } else { |
WiredHome | 0:729320f63c5c | 525 | *string = NULL; // something bad happened... no memory |
WiredHome | 0:729320f63c5c | 526 | } |
WiredHome | 0:729320f63c5c | 527 | } |
WiredHome | 0:729320f63c5c | 528 | return ret; |
WiredHome | 0:729320f63c5c | 529 | } |
WiredHome | 0:729320f63c5c | 530 | |
WiredHome | 0:729320f63c5c | 531 | char * HTTPServer::rewriteWithDefaultFile(char * queryString) |
WiredHome | 0:729320f63c5c | 532 | { |
WiredHome | 0:729320f63c5c | 533 | char * temp = (char *)malloc(strlen(queryString) + strlen(DEFAULT_FILENAME) + 1); |
WiredHome | 0:729320f63c5c | 534 | |
WiredHome | 0:729320f63c5c | 535 | if (temp) { |
WiredHome | 0:729320f63c5c | 536 | *temp = '\0'; |
WiredHome | 0:729320f63c5c | 537 | strcpy(temp, queryString); |
WiredHome | 0:729320f63c5c | 538 | strcat(temp, DEFAULT_FILENAME); |
WiredHome | 0:729320f63c5c | 539 | free(queryString); |
WiredHome | 0:729320f63c5c | 540 | return temp; |
WiredHome | 0:729320f63c5c | 541 | } else { |
WiredHome | 0:729320f63c5c | 542 | return queryString; |
WiredHome | 0:729320f63c5c | 543 | } |
WiredHome | 0:729320f63c5c | 544 | } |
WiredHome | 0:729320f63c5c | 545 | |
WiredHome | 0:729320f63c5c | 546 | char * HTTPServer::rewritePrependWebroot(char * queryString) |
WiredHome | 0:729320f63c5c | 547 | { |
WiredHome | 0:729320f63c5c | 548 | char * temp = (char *)malloc(strlen(webroot) + strlen(queryString) + 1); |
WiredHome | 0:729320f63c5c | 549 | |
WiredHome | 0:729320f63c5c | 550 | if (temp) { |
WiredHome | 0:729320f63c5c | 551 | *temp = '\0'; |
WiredHome | 0:729320f63c5c | 552 | strcpy(temp, webroot); |
WiredHome | 0:729320f63c5c | 553 | if (temp[strlen(temp)-1] == '/' && *queryString == '/') |
WiredHome | 0:729320f63c5c | 554 | temp[strlen(temp)-1] = '\0'; |
WiredHome | 0:729320f63c5c | 555 | strcat(temp, queryString); |
WiredHome | 0:729320f63c5c | 556 | free(queryString); |
WiredHome | 0:729320f63c5c | 557 | return temp; |
WiredHome | 0:729320f63c5c | 558 | } else { |
WiredHome | 0:729320f63c5c | 559 | return queryString; |
WiredHome | 0:729320f63c5c | 560 | } |
WiredHome | 0:729320f63c5c | 561 | } |
WiredHome | 0:729320f63c5c | 562 | |
WiredHome | 0:729320f63c5c | 563 |