A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.
Dependents: Smart-WiFly-WebServer WattEye X10Svr SSDP_Server
SW_HTTPServer.cpp@23:6963f45e950a, 2013-10-10 (annotated)
- Committer:
- WiredHome
- Date:
- Thu Oct 10 16:00:23 2013 +0000
- Revision:
- 23:6963f45e950a
- Parent:
- 22:55974de690c1
Enhanced debugging, but this version does not work. It hangs in the SendResponse, for reasons unknown.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
WiredHome | 0:729320f63c5c | 1 | // |
WiredHome | 2:a29c32190037 | 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 | 23:6963f45e950a | 17 | #define DEBUG "HTTPd" // Debug aids report this |
WiredHome | 23:6963f45e950a | 18 | #include "DebugAid.h" |
WiredHome | 0:729320f63c5c | 19 | |
WiredHome | 0:729320f63c5c | 20 | const char * DEFAULT_FILENAME = "index.htm"; |
WiredHome | 0:729320f63c5c | 21 | |
WiredHome | 8:262583f054f6 | 22 | // Header information to always send (must be \r\n terminated) |
WiredHome | 17:69ff00ce39f4 | 23 | const char hdr_httpver[] = "HTTP/1.0"; // Wifly may not be able to support HTTP/1.1 protocol |
WiredHome | 8:262583f054f6 | 24 | const char hdr_age[] = "Max-age: 0\r\n"; // expires right away |
WiredHome | 8:262583f054f6 | 25 | const char hdr_server[] = "Server: Smart_Server v0.1\r\n"; // Server |
WiredHome | 17:69ff00ce39f4 | 26 | const char hdr_close[] = "Connection: close\r\n"; // tell the client the server closes the connection immediately |
WiredHome | 8:262583f054f6 | 27 | const char nl[] = "\r\n"; // final \r\n for the termination of the header |
WiredHome | 0:729320f63c5c | 28 | |
WiredHome | 14:19c5f6151319 | 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 | 3:17928786bdb5 | 34 | {".gif", "Content-Type: image/gif\r\n" }, |
WiredHome | 3:17928786bdb5 | 35 | {".jpg", "Content-Type: image/jpeg\r\n" }, |
WiredHome | 3:17928786bdb5 | 36 | {".jpeg","Content-Type: image/jpeg\r\n" }, |
WiredHome | 3:17928786bdb5 | 37 | {".ico", "Content-Type: image/x-icon\r\n" }, |
WiredHome | 3:17928786bdb5 | 38 | {".png", "Content-Type: image/png\r\n" }, |
WiredHome | 3:17928786bdb5 | 39 | {".zip", "Content-Type: image/zip\r\n" }, |
WiredHome | 3:17928786bdb5 | 40 | {".gz", "Content-Type: image/gz\r\n" }, |
WiredHome | 3:17928786bdb5 | 41 | {".tar", "Content-Type: image/tar\r\n" }, |
WiredHome | 3:17928786bdb5 | 42 | {".txt", "Content-Type: plain/text\r\n" }, |
WiredHome | 3:17928786bdb5 | 43 | {".pdf", "Content-Type: application/pdf\r\n" }, |
WiredHome | 3:17928786bdb5 | 44 | {".htm", "Content-Type: text/html\r\n" }, |
WiredHome | 3:17928786bdb5 | 45 | {".html","Content-Type: text/html\r\n" }, |
WiredHome | 0:729320f63c5c | 46 | {0,0} |
WiredHome | 0:729320f63c5c | 47 | }; |
WiredHome | 0:729320f63c5c | 48 | |
WiredHome | 8:262583f054f6 | 49 | #ifdef DEBUG |
WiredHome | 12:109bf1558300 | 50 | // This uses standard library dynamic memory management, but for an |
WiredHome | 9:2ea342765c9d | 51 | // embedded system there are alternates that may make better sense - |
WiredHome | 9:2ea342765c9d | 52 | // search the web for embedded system malloc alternates. |
WiredHome | 8:262583f054f6 | 53 | static void * MyMalloc(int x, int y) |
WiredHome | 8:262583f054f6 | 54 | { |
WiredHome | 23:6963f45e950a | 55 | std::printf("[DBG %s%4d] malloc(%d)\r\n", DEBUG, y, x); |
WiredHome | 8:262583f054f6 | 56 | return malloc(x); |
WiredHome | 8:262583f054f6 | 57 | } |
WiredHome | 11:17d84c41a7b3 | 58 | static char toP(void * x) |
WiredHome | 11:17d84c41a7b3 | 59 | { |
WiredHome | 11:17d84c41a7b3 | 60 | char * c = (char *) x; |
WiredHome | 11:17d84c41a7b3 | 61 | if (*c >= ' ' && *c < 0x7F) |
WiredHome | 11:17d84c41a7b3 | 62 | return *c; |
WiredHome | 11:17d84c41a7b3 | 63 | else |
WiredHome | 11:17d84c41a7b3 | 64 | return '.'; |
WiredHome | 11:17d84c41a7b3 | 65 | } |
WiredHome | 8:262583f054f6 | 66 | #define mymalloc(x) MyMalloc(x, __LINE__) |
WiredHome | 8:262583f054f6 | 67 | #define myfree(x) \ |
WiredHome | 23:6963f45e950a | 68 | pc->printf("[DBG %s%4d] free(%02x %02x %02x %02x %02x ... %c%c%c%c%c)\r\n", DEBUG, __LINE__, \ |
WiredHome | 11:17d84c41a7b3 | 69 | *x, *(x+1), *(x+2), *(x+3), *(x+4), \ |
WiredHome | 11:17d84c41a7b3 | 70 | toP(x), toP(x+1), toP(x+2), toP(x+3), toP(x+4) ); \ |
WiredHome | 8:262583f054f6 | 71 | free(x); |
WiredHome | 8:262583f054f6 | 72 | #else |
WiredHome | 8:262583f054f6 | 73 | #define mymalloc(x) malloc(x) |
WiredHome | 8:262583f054f6 | 74 | #define myfree(x) free(x) |
WiredHome | 8:262583f054f6 | 75 | #endif |
WiredHome | 8:262583f054f6 | 76 | |
WiredHome | 3:17928786bdb5 | 77 | HTTPServer::HTTPServer( |
WiredHome | 7:99ad7a67f05e | 78 | Wifly * _wf, |
WiredHome | 7:99ad7a67f05e | 79 | int port, |
WiredHome | 7:99ad7a67f05e | 80 | const char * _webroot, |
WiredHome | 13:8975d7928678 | 81 | int maxheaderParams, |
WiredHome | 13:8975d7928678 | 82 | int _maxqueryParams, |
WiredHome | 7:99ad7a67f05e | 83 | int _maxdynamicpages, |
WiredHome | 7:99ad7a67f05e | 84 | PC * _pc, |
WiredHome | 7:99ad7a67f05e | 85 | int _allocforheader, |
WiredHome | 3:17928786bdb5 | 86 | int _allocforfile) |
WiredHome | 0:729320f63c5c | 87 | { |
WiredHome | 0:729320f63c5c | 88 | wifly = _wf; |
WiredHome | 0:729320f63c5c | 89 | webroot = (char *)malloc(strlen(_webroot)+1); |
WiredHome | 0:729320f63c5c | 90 | strcpy(webroot, _webroot); |
WiredHome | 22:55974de690c1 | 91 | if (strlen(webroot)>1 && webroot[strlen(webroot)-1] == '/') // remove trailing '/' |
WiredHome | 22:55974de690c1 | 92 | webroot[strlen(webroot)-1] = '\0'; |
WiredHome | 13:8975d7928678 | 93 | maxqueryParams = _maxqueryParams; |
WiredHome | 0:729320f63c5c | 94 | maxdynamicpages = _maxdynamicpages; |
WiredHome | 13:8975d7928678 | 95 | headerParams = (namevalue *)malloc(maxheaderParams * sizeof(namevalue)); |
WiredHome | 13:8975d7928678 | 96 | queryParams = (namevalue *)malloc(maxqueryParams * sizeof(namevalue)); |
WiredHome | 0:729320f63c5c | 97 | handlers = (handler *)malloc(maxdynamicpages * sizeof(handler)); |
WiredHome | 3:17928786bdb5 | 98 | headerbuffersize = _allocforheader; |
WiredHome | 3:17928786bdb5 | 99 | headerbuffer = (char *)malloc(headerbuffersize); |
WiredHome | 0:729320f63c5c | 100 | pc = _pc; |
WiredHome | 3:17928786bdb5 | 101 | queryType = NULL; |
WiredHome | 3:17928786bdb5 | 102 | queryString = NULL; |
WiredHome | 3:17928786bdb5 | 103 | postQueryString = NULL; |
WiredHome | 13:8975d7928678 | 104 | queryParamCount = 0; |
WiredHome | 0:729320f63c5c | 105 | handlercount = 0; |
WiredHome | 3:17928786bdb5 | 106 | maxheaderbytes = 0; |
WiredHome | 0:729320f63c5c | 107 | server = new TCPSocketServer(); |
WiredHome | 0:729320f63c5c | 108 | server->bind(port); |
WiredHome | 0:729320f63c5c | 109 | server->listen(); |
WiredHome | 16:6ebacf2946d8 | 110 | server->set_blocking(false, 10); |
WiredHome | 3:17928786bdb5 | 111 | ResetPerformanceData(); |
WiredHome | 10:9c8d2c6a3469 | 112 | PerformanceTimer.start(); |
WiredHome | 0:729320f63c5c | 113 | } |
WiredHome | 0:729320f63c5c | 114 | |
WiredHome | 0:729320f63c5c | 115 | HTTPServer::~HTTPServer() |
WiredHome | 0:729320f63c5c | 116 | { |
WiredHome | 8:262583f054f6 | 117 | int i; |
WiredHome | 8:262583f054f6 | 118 | |
WiredHome | 8:262583f054f6 | 119 | for (i=0; i<handlercount; i++) |
WiredHome | 8:262583f054f6 | 120 | myfree(handlers[i].path); |
WiredHome | 8:262583f054f6 | 121 | myfree(headerbuffer); |
WiredHome | 8:262583f054f6 | 122 | myfree(handlers); |
WiredHome | 13:8975d7928678 | 123 | myfree(queryParams); |
WiredHome | 8:262583f054f6 | 124 | myfree(webroot); |
WiredHome | 0:729320f63c5c | 125 | webroot = NULL; |
WiredHome | 0:729320f63c5c | 126 | } |
WiredHome | 0:729320f63c5c | 127 | |
WiredHome | 3:17928786bdb5 | 128 | int HTTPServer::GetMaxHeaderSize() |
WiredHome | 3:17928786bdb5 | 129 | { |
WiredHome | 3:17928786bdb5 | 130 | return maxheaderbytes; |
WiredHome | 3:17928786bdb5 | 131 | } |
WiredHome | 3:17928786bdb5 | 132 | |
WiredHome | 0:729320f63c5c | 133 | bool HTTPServer::RegisterHandler(const char * path, Handler callback) |
WiredHome | 0:729320f63c5c | 134 | { |
WiredHome | 0:729320f63c5c | 135 | if (handlercount < maxdynamicpages && path && callback) { |
WiredHome | 8:262583f054f6 | 136 | handlers[handlercount].path = (char *)mymalloc(strlen(path)+1); |
WiredHome | 0:729320f63c5c | 137 | memcpy(handlers[handlercount].path, path, strlen(path)+1); |
WiredHome | 0:729320f63c5c | 138 | handlers[handlercount].callback = callback; |
WiredHome | 0:729320f63c5c | 139 | handlercount++; |
WiredHome | 0:729320f63c5c | 140 | return true; |
WiredHome | 0:729320f63c5c | 141 | } else { |
WiredHome | 0:729320f63c5c | 142 | return false; |
WiredHome | 0:729320f63c5c | 143 | } |
WiredHome | 0:729320f63c5c | 144 | } |
WiredHome | 0:729320f63c5c | 145 | |
WiredHome | 2:a29c32190037 | 146 | // Poll() |
WiredHome | 0:729320f63c5c | 147 | // |
WiredHome | 0:729320f63c5c | 148 | // *OPEN*GET /x=1 HTTP/1.1 |
WiredHome | 0:729320f63c5c | 149 | // Host: 192.168.1.140 |
WiredHome | 0:729320f63c5c | 150 | // Connection: keep-alive |
WiredHome | 0:729320f63c5c | 151 | // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
WiredHome | 0:729320f63c5c | 152 | // 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 | 153 | // Accept-Encoding: gzip,deflate,sdch |
WiredHome | 0:729320f63c5c | 154 | // Accept-Language: en-US,en;q=0.8 |
WiredHome | 0:729320f63c5c | 155 | // |
WiredHome | 2:a29c32190037 | 156 | void HTTPServer::Poll() |
WiredHome | 0:729320f63c5c | 157 | { |
WiredHome | 3:17928786bdb5 | 158 | typedef enum { |
WiredHome | 3:17928786bdb5 | 159 | Idle, // waiting for a connection |
WiredHome | 3:17928786bdb5 | 160 | Receiving, // receiving data |
WiredHome | 3:17928786bdb5 | 161 | Sending, // send the response |
WiredHome | 7:99ad7a67f05e | 162 | WaitingToClose, // small timeout to close |
WiredHome | 7:99ad7a67f05e | 163 | Reset |
WiredHome | 3:17928786bdb5 | 164 | } state; |
WiredHome | 0:729320f63c5c | 165 | static state op = Idle; |
WiredHome | 3:17928786bdb5 | 166 | static char * bPtr = headerbuffer; |
WiredHome | 0:729320f63c5c | 167 | int n; |
WiredHome | 16:6ebacf2946d8 | 168 | static unsigned int t_ref; // reference point for the PerformanceTimer |
WiredHome | 0:729320f63c5c | 169 | |
WiredHome | 20:786aa5749007 | 170 | #if defined(DEBUG) |
WiredHome | 8:262583f054f6 | 171 | static state lastOp = Reset; |
WiredHome | 7:99ad7a67f05e | 172 | if (lastOp != op) { |
WiredHome | 9:2ea342765c9d | 173 | const char *states[] = {"Idle", "Receiving", "Sending", "WaitingToClose", "Reset"}; |
WiredHome | 8:262583f054f6 | 174 | pc->printf("Poll: %s\r\n", states[op]); |
WiredHome | 7:99ad7a67f05e | 175 | lastOp = op; |
WiredHome | 7:99ad7a67f05e | 176 | } |
WiredHome | 8:262583f054f6 | 177 | #endif |
WiredHome | 0:729320f63c5c | 178 | switch(op) { |
WiredHome | 3:17928786bdb5 | 179 | default: // not expected to arrive here |
WiredHome | 3:17928786bdb5 | 180 | op = Idle; |
WiredHome | 3:17928786bdb5 | 181 | break; |
WiredHome | 8:262583f054f6 | 182 | |
WiredHome | 3:17928786bdb5 | 183 | case Idle: |
WiredHome | 10:9c8d2c6a3469 | 184 | PerformanceTimer.reset(); |
WiredHome | 17:69ff00ce39f4 | 185 | t_ref = (unsigned int)PerformanceTimer.read_us(); |
WiredHome | 3:17928786bdb5 | 186 | bPtr = headerbuffer; |
WiredHome | 11:17d84c41a7b3 | 187 | if (0 == server->accept(client)) { |
WiredHome | 3:17928786bdb5 | 188 | op = Receiving; |
WiredHome | 17:69ff00ce39f4 | 189 | t_ref = RecordPerformanceData(&perfData.ConnectionAccepted, t_ref); |
WiredHome | 8:262583f054f6 | 190 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 191 | pc->printf("Accepted at %u\r\n", (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 8:262583f054f6 | 192 | #endif |
WiredHome | 3:17928786bdb5 | 193 | } |
WiredHome | 0:729320f63c5c | 194 | break; |
WiredHome | 8:262583f054f6 | 195 | |
WiredHome | 3:17928786bdb5 | 196 | case Receiving: |
WiredHome | 3:17928786bdb5 | 197 | n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer)); |
WiredHome | 3:17928786bdb5 | 198 | if (n < 0) { |
WiredHome | 9:2ea342765c9d | 199 | #ifdef DEBUG |
WiredHome | 8:262583f054f6 | 200 | pc->printf("*** client.receive() => %d\r\n", n); |
WiredHome | 9:2ea342765c9d | 201 | #endif |
WiredHome | 3:17928786bdb5 | 202 | } else if (n) { |
WiredHome | 3:17928786bdb5 | 203 | bPtr[n] = '\0'; |
WiredHome | 3:17928786bdb5 | 204 | if (ParseHeader(headerbuffer)) { |
WiredHome | 3:17928786bdb5 | 205 | op = Sending; |
WiredHome | 17:69ff00ce39f4 | 206 | t_ref = RecordPerformanceData(&perfData.HeaderParsed, t_ref); |
WiredHome | 17:69ff00ce39f4 | 207 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 208 | pc->printf("Header Parsed at %u\r\n", (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 17:69ff00ce39f4 | 209 | #endif |
WiredHome | 3:17928786bdb5 | 210 | } |
WiredHome | 3:17928786bdb5 | 211 | bPtr += n; |
WiredHome | 0:729320f63c5c | 212 | } |
WiredHome | 0:729320f63c5c | 213 | break; |
WiredHome | 8:262583f054f6 | 214 | |
WiredHome | 0:729320f63c5c | 215 | case Sending: |
WiredHome | 3:17928786bdb5 | 216 | SendResponse(); |
WiredHome | 0:729320f63c5c | 217 | op = WaitingToClose; |
WiredHome | 17:69ff00ce39f4 | 218 | t_ref = RecordPerformanceData(&perfData.ResponseSent, t_ref); |
WiredHome | 17:69ff00ce39f4 | 219 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 220 | pc->printf("Response Sent at %u\r\n", (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 17:69ff00ce39f4 | 221 | #endif |
WiredHome | 0:729320f63c5c | 222 | break; |
WiredHome | 9:2ea342765c9d | 223 | |
WiredHome | 0:729320f63c5c | 224 | case WaitingToClose: |
WiredHome | 17:69ff00ce39f4 | 225 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 226 | pc->printf("Connection closed entry %u\r\n", (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 17:69ff00ce39f4 | 227 | #endif |
WiredHome | 3:17928786bdb5 | 228 | close_connection(); |
WiredHome | 0:729320f63c5c | 229 | op = Idle; |
WiredHome | 17:69ff00ce39f4 | 230 | RecordPerformanceData(&perfData.ConnectionClosed, t_ref); |
WiredHome | 17:69ff00ce39f4 | 231 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 232 | pc->printf("Connection closed exit %u\r\n", (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 17:69ff00ce39f4 | 233 | #endif |
WiredHome | 0:729320f63c5c | 234 | break; |
WiredHome | 0:729320f63c5c | 235 | } |
WiredHome | 0:729320f63c5c | 236 | } |
WiredHome | 0:729320f63c5c | 237 | |
WiredHome | 0:729320f63c5c | 238 | |
WiredHome | 0:729320f63c5c | 239 | const char * HTTPServer::GetSupportedType(const char * filename) |
WiredHome | 0:729320f63c5c | 240 | { |
WiredHome | 0:729320f63c5c | 241 | int i; |
WiredHome | 0:729320f63c5c | 242 | int buflen = strlen(filename); |
WiredHome | 0:729320f63c5c | 243 | int extlen; |
WiredHome | 0:729320f63c5c | 244 | |
WiredHome | 0:729320f63c5c | 245 | for (i=0; extensions[i].ext != 0; i++) { |
WiredHome | 0:729320f63c5c | 246 | extlen = strlen(extensions[i].ext); |
WiredHome | 0:729320f63c5c | 247 | if ( !strncmp(&filename[buflen-extlen], extensions[i].ext, extlen)) { |
WiredHome | 0:729320f63c5c | 248 | return extensions[i].filetype; |
WiredHome | 0:729320f63c5c | 249 | } |
WiredHome | 0:729320f63c5c | 250 | } |
WiredHome | 0:729320f63c5c | 251 | return NULL; |
WiredHome | 0:729320f63c5c | 252 | } |
WiredHome | 0:729320f63c5c | 253 | |
WiredHome | 3:17928786bdb5 | 254 | |
WiredHome | 0:729320f63c5c | 255 | void HTTPServer::send(const char * msg, int bytes) |
WiredHome | 0:729320f63c5c | 256 | { |
WiredHome | 0:729320f63c5c | 257 | if (bytes == -1) |
WiredHome | 0:729320f63c5c | 258 | bytes = strlen(msg); |
WiredHome | 20:786aa5749007 | 259 | client.send((char *)msg, bytes); |
WiredHome | 0:729320f63c5c | 260 | } |
WiredHome | 0:729320f63c5c | 261 | |
WiredHome | 3:17928786bdb5 | 262 | |
WiredHome | 0:729320f63c5c | 263 | bool HTTPServer::SendFile(const char * filename, const char * filetype) |
WiredHome | 0:729320f63c5c | 264 | { |
WiredHome | 0:729320f63c5c | 265 | FILE * fp; |
WiredHome | 3:17928786bdb5 | 266 | |
WiredHome | 0:729320f63c5c | 267 | fp = fopen(filename,"rb"); |
WiredHome | 0:729320f63c5c | 268 | if (fp) { // can open it |
WiredHome | 8:262583f054f6 | 269 | char *fbuffer = (char *)mymalloc(FILESEND_BUF_SIZE); |
WiredHome | 0:729320f63c5c | 270 | int bytes; |
WiredHome | 0:729320f63c5c | 271 | |
WiredHome | 23:6963f45e950a | 272 | INFO("SendFile"); |
WiredHome | 3:17928786bdb5 | 273 | if (fbuffer) { |
WiredHome | 3:17928786bdb5 | 274 | header(200, "OK", filetype); |
WiredHome | 0:729320f63c5c | 275 | bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp); |
WiredHome | 3:17928786bdb5 | 276 | while (bytes > 0) { |
WiredHome | 3:17928786bdb5 | 277 | send(fbuffer, bytes); |
WiredHome | 3:17928786bdb5 | 278 | bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp); |
WiredHome | 3:17928786bdb5 | 279 | } |
WiredHome | 8:262583f054f6 | 280 | myfree(fbuffer); |
WiredHome | 3:17928786bdb5 | 281 | } else { |
WiredHome | 3:17928786bdb5 | 282 | header(500, "Server Error", "Pragma: err - insufficient memory\r\n"); |
WiredHome | 0:729320f63c5c | 283 | } |
WiredHome | 0:729320f63c5c | 284 | fclose(fp); |
WiredHome | 0:729320f63c5c | 285 | return true; |
WiredHome | 0:729320f63c5c | 286 | } else { |
WiredHome | 3:17928786bdb5 | 287 | header(404, "Not Found", "Pragma: err - Can't open file\r\n"); |
WiredHome | 0:729320f63c5c | 288 | return false; |
WiredHome | 0:729320f63c5c | 289 | } |
WiredHome | 0:729320f63c5c | 290 | } |
WiredHome | 0:729320f63c5c | 291 | |
WiredHome | 0:729320f63c5c | 292 | int HTTPServer::HexCharToInt(char c) |
WiredHome | 0:729320f63c5c | 293 | { |
WiredHome | 0:729320f63c5c | 294 | if (c >= 'a' && c <= 'f') |
WiredHome | 0:729320f63c5c | 295 | return (c - 'a' + 10); |
WiredHome | 0:729320f63c5c | 296 | else if (c >= 'A' && c <= 'F') |
WiredHome | 0:729320f63c5c | 297 | return (c - 'A' + 10); |
WiredHome | 0:729320f63c5c | 298 | else if (c >= '0' && c <= '9') |
WiredHome | 0:729320f63c5c | 299 | return c - '0'; |
WiredHome | 0:729320f63c5c | 300 | else |
WiredHome | 0:729320f63c5c | 301 | return 0; |
WiredHome | 0:729320f63c5c | 302 | } |
WiredHome | 0:729320f63c5c | 303 | |
WiredHome | 0:729320f63c5c | 304 | char HTTPServer::HexPairToChar(char * p) |
WiredHome | 0:729320f63c5c | 305 | { |
WiredHome | 0:729320f63c5c | 306 | return 16 * HexCharToInt(*p) + HexCharToInt(*(p+1)); |
WiredHome | 0:729320f63c5c | 307 | } |
WiredHome | 0:729320f63c5c | 308 | |
WiredHome | 0:729320f63c5c | 309 | void HTTPServer::UnescapeString(char * encoded) |
WiredHome | 0:729320f63c5c | 310 | { |
WiredHome | 0:729320f63c5c | 311 | char *p; |
WiredHome | 0:729320f63c5c | 312 | |
WiredHome | 0:729320f63c5c | 313 | // first convert '+' to ' ' |
WiredHome | 0:729320f63c5c | 314 | p = strchr(encoded, '+'); |
WiredHome | 0:729320f63c5c | 315 | while (p) { |
WiredHome | 0:729320f63c5c | 316 | *p = ' '; |
WiredHome | 0:729320f63c5c | 317 | p = strchr(encoded, '+'); |
WiredHome | 0:729320f63c5c | 318 | } |
WiredHome | 0:729320f63c5c | 319 | // then convert hex '%xx' to char 'x' |
WiredHome | 0:729320f63c5c | 320 | p = strchr(encoded, '%'); |
WiredHome | 0:729320f63c5c | 321 | while (p) { |
WiredHome | 0:729320f63c5c | 322 | if (strchr("0123456789ABCDEFabcdef", *(p+1)) |
WiredHome | 0:729320f63c5c | 323 | && strchr("0123456789ABCDEFabcdef", *(p+2)) ) { |
WiredHome | 0:729320f63c5c | 324 | *p = HexPairToChar(p+1); |
WiredHome | 0:729320f63c5c | 325 | p++; // advance past the % |
WiredHome | 0:729320f63c5c | 326 | char * a = p; |
WiredHome | 0:729320f63c5c | 327 | char * b = p + 2; |
WiredHome | 0:729320f63c5c | 328 | do { |
WiredHome | 0:729320f63c5c | 329 | *a++ = *b++; |
WiredHome | 0:729320f63c5c | 330 | } while (*b); |
WiredHome | 0:729320f63c5c | 331 | *a = '\0'; |
WiredHome | 0:729320f63c5c | 332 | } |
WiredHome | 0:729320f63c5c | 333 | p = strchr(p, '%'); |
WiredHome | 0:729320f63c5c | 334 | } |
WiredHome | 0:729320f63c5c | 335 | } |
WiredHome | 0:729320f63c5c | 336 | |
WiredHome | 0:729320f63c5c | 337 | const char * HTTPServer::GetParameter(const char * name) |
WiredHome | 0:729320f63c5c | 338 | { |
WiredHome | 13:8975d7928678 | 339 | for (int i=0; i<queryParamCount; i++) { |
WiredHome | 13:8975d7928678 | 340 | if (strcmp(queryParams[i].name, name) == 0) { |
WiredHome | 13:8975d7928678 | 341 | return queryParams[i].value; |
WiredHome | 0:729320f63c5c | 342 | } |
WiredHome | 0:729320f63c5c | 343 | } |
WiredHome | 0:729320f63c5c | 344 | return NULL; |
WiredHome | 0:729320f63c5c | 345 | } |
WiredHome | 0:729320f63c5c | 346 | |
WiredHome | 0:729320f63c5c | 347 | // this=that&who=what&more=stuff... |
WiredHome | 0:729320f63c5c | 348 | // ^ ^ ^ |
WiredHome | 3:17928786bdb5 | 349 | void HTTPServer::ParseParameters(char * pName) |
WiredHome | 0:729320f63c5c | 350 | { |
WiredHome | 0:729320f63c5c | 351 | char * pVal; |
WiredHome | 0:729320f63c5c | 352 | char * pNextName; |
WiredHome | 0:729320f63c5c | 353 | |
WiredHome | 13:8975d7928678 | 354 | // Parse queryParams |
WiredHome | 0:729320f63c5c | 355 | pVal = strchr(pName, '#'); // If there is a '#fragment_id', we can ignore it |
WiredHome | 0:729320f63c5c | 356 | if (pVal) |
WiredHome | 3:17928786bdb5 | 357 | *pVal = '\0'; |
WiredHome | 0:729320f63c5c | 358 | do { |
WiredHome | 13:8975d7928678 | 359 | queryParams[queryParamCount].name = pName; |
WiredHome | 0:729320f63c5c | 360 | pVal = strchr(pName, '='); |
WiredHome | 0:729320f63c5c | 361 | pNextName = strchr(pName,'&'); |
WiredHome | 0:729320f63c5c | 362 | if (pVal) { |
WiredHome | 0:729320f63c5c | 363 | if (pNextName == NULL || (pNextName && pNextName > pVal)) { |
WiredHome | 0:729320f63c5c | 364 | *pVal++ = '\0'; |
WiredHome | 13:8975d7928678 | 365 | queryParams[queryParamCount].value = pVal; |
WiredHome | 0:729320f63c5c | 366 | pName = pVal; |
WiredHome | 0:729320f63c5c | 367 | } |
WiredHome | 0:729320f63c5c | 368 | } |
WiredHome | 13:8975d7928678 | 369 | queryParamCount++; |
WiredHome | 0:729320f63c5c | 370 | if (pNextName) { |
WiredHome | 0:729320f63c5c | 371 | pName = pNextName; |
WiredHome | 0:729320f63c5c | 372 | *pName++ = '\0'; |
WiredHome | 0:729320f63c5c | 373 | } else { |
WiredHome | 0:729320f63c5c | 374 | pName = NULL; |
WiredHome | 0:729320f63c5c | 375 | } |
WiredHome | 13:8975d7928678 | 376 | } while (pName && queryParamCount < maxqueryParams); |
WiredHome | 0:729320f63c5c | 377 | } |
WiredHome | 0:729320f63c5c | 378 | |
WiredHome | 0:729320f63c5c | 379 | |
WiredHome | 0:729320f63c5c | 380 | void HTTPServer::header(int code, const char * code_text, const char * content_type, const char * optional_text) |
WiredHome | 0:729320f63c5c | 381 | { |
WiredHome | 0:729320f63c5c | 382 | char http[100]; |
WiredHome | 0:729320f63c5c | 383 | |
WiredHome | 0:729320f63c5c | 384 | sprintf(http, "%s %i %s\r\n", hdr_httpver, code, code_text); |
WiredHome | 23:6963f45e950a | 385 | INFO("header(%s)", http); |
WiredHome | 0:729320f63c5c | 386 | send(http); |
WiredHome | 0:729320f63c5c | 387 | send(hdr_age); |
WiredHome | 0:729320f63c5c | 388 | send(hdr_server); |
WiredHome | 0:729320f63c5c | 389 | if (content_type) { |
WiredHome | 0:729320f63c5c | 390 | send(content_type); |
WiredHome | 0:729320f63c5c | 391 | } |
WiredHome | 0:729320f63c5c | 392 | if (optional_text) { |
WiredHome | 0:729320f63c5c | 393 | send(optional_text); |
WiredHome | 0:729320f63c5c | 394 | } |
WiredHome | 0:729320f63c5c | 395 | send(hdr_close); |
WiredHome | 0:729320f63c5c | 396 | send(nl); |
WiredHome | 0:729320f63c5c | 397 | } |
WiredHome | 0:729320f63c5c | 398 | |
WiredHome | 14:19c5f6151319 | 399 | |
WiredHome | 7:99ad7a67f05e | 400 | bool HTTPServer::close_connection() |
WiredHome | 0:729320f63c5c | 401 | { |
WiredHome | 7:99ad7a67f05e | 402 | bool res; |
WiredHome | 7:99ad7a67f05e | 403 | |
WiredHome | 14:19c5f6151319 | 404 | res = server->close(); |
WiredHome | 7:99ad7a67f05e | 405 | #ifdef DEBUG |
WiredHome | 7:99ad7a67f05e | 406 | pc->printf("close connection returned %d\r\n", res); |
WiredHome | 0:729320f63c5c | 407 | #endif |
WiredHome | 7:99ad7a67f05e | 408 | return res; |
WiredHome | 0:729320f63c5c | 409 | } |
WiredHome | 0:729320f63c5c | 410 | |
WiredHome | 14:19c5f6151319 | 411 | |
WiredHome | 0:729320f63c5c | 412 | bool HTTPServer::Extract(char * haystack, char * needle, char ** string) |
WiredHome | 0:729320f63c5c | 413 | { |
WiredHome | 0:729320f63c5c | 414 | bool ret = false; // assume failure until proven otherwise |
WiredHome | 0:729320f63c5c | 415 | char * qs = NULL; |
WiredHome | 0:729320f63c5c | 416 | char * eqs = NULL; |
WiredHome | 0:729320f63c5c | 417 | char * container = NULL; |
WiredHome | 0:729320f63c5c | 418 | char * get = strstr(haystack, needle); // what if not at the front? |
WiredHome | 0:729320f63c5c | 419 | if (get) { |
WiredHome | 0:729320f63c5c | 420 | // Seems to be a valid "...GET /QueryString HTTP/1.1" |
WiredHome | 8:262583f054f6 | 421 | // or "...<needle>param..." |
WiredHome | 0:729320f63c5c | 422 | qs = get + strlen(needle); // in case the needle didn't have space delimiters |
WiredHome | 0:729320f63c5c | 423 | while (*qs == ' ') |
WiredHome | 0:729320f63c5c | 424 | qs++; |
WiredHome | 0:729320f63c5c | 425 | // /QueryString\0HTTP/1.1\0\0 |
WiredHome | 0:729320f63c5c | 426 | if (*string) // recycle old string when working a new one |
WiredHome | 8:262583f054f6 | 427 | myfree(*string); |
WiredHome | 8:262583f054f6 | 428 | container = (char *)mymalloc(strlen(qs)); |
WiredHome | 0:729320f63c5c | 429 | if (container) { |
WiredHome | 0:729320f63c5c | 430 | strcpy(container, qs); |
WiredHome | 0:729320f63c5c | 431 | eqs = strchr(container, ' '); |
WiredHome | 0:729320f63c5c | 432 | if (eqs) |
WiredHome | 0:729320f63c5c | 433 | *eqs = '\0'; |
WiredHome | 0:729320f63c5c | 434 | *string = container; |
WiredHome | 8:262583f054f6 | 435 | #ifdef DEBUG |
WiredHome | 8:262583f054f6 | 436 | pc->printf("Extract(%s) = %s\r\n", needle, container); |
WiredHome | 8:262583f054f6 | 437 | #endif |
WiredHome | 0:729320f63c5c | 438 | ret = true; |
WiredHome | 0:729320f63c5c | 439 | } else { |
WiredHome | 0:729320f63c5c | 440 | *string = NULL; // something bad happened... no memory |
WiredHome | 0:729320f63c5c | 441 | } |
WiredHome | 0:729320f63c5c | 442 | } |
WiredHome | 0:729320f63c5c | 443 | return ret; |
WiredHome | 0:729320f63c5c | 444 | } |
WiredHome | 0:729320f63c5c | 445 | |
WiredHome | 14:19c5f6151319 | 446 | |
WiredHome | 0:729320f63c5c | 447 | char * HTTPServer::rewriteWithDefaultFile(char * queryString) |
WiredHome | 0:729320f63c5c | 448 | { |
WiredHome | 8:262583f054f6 | 449 | char * temp = (char *)mymalloc(strlen(queryString) + strlen(DEFAULT_FILENAME) + 1); |
WiredHome | 0:729320f63c5c | 450 | |
WiredHome | 0:729320f63c5c | 451 | if (temp) { |
WiredHome | 0:729320f63c5c | 452 | *temp = '\0'; |
WiredHome | 0:729320f63c5c | 453 | strcpy(temp, queryString); |
WiredHome | 0:729320f63c5c | 454 | strcat(temp, DEFAULT_FILENAME); |
WiredHome | 8:262583f054f6 | 455 | myfree(queryString); |
WiredHome | 0:729320f63c5c | 456 | return temp; |
WiredHome | 0:729320f63c5c | 457 | } else { |
WiredHome | 0:729320f63c5c | 458 | return queryString; |
WiredHome | 0:729320f63c5c | 459 | } |
WiredHome | 0:729320f63c5c | 460 | } |
WiredHome | 0:729320f63c5c | 461 | |
WiredHome | 14:19c5f6151319 | 462 | |
WiredHome | 0:729320f63c5c | 463 | char * HTTPServer::rewritePrependWebroot(char * queryString) |
WiredHome | 0:729320f63c5c | 464 | { |
WiredHome | 22:55974de690c1 | 465 | char * temp = (char *)mymalloc(strlen(webroot) + strlen(queryString) + 2); // room for / |
WiredHome | 0:729320f63c5c | 466 | |
WiredHome | 0:729320f63c5c | 467 | if (temp) { |
WiredHome | 0:729320f63c5c | 468 | *temp = '\0'; |
WiredHome | 22:55974de690c1 | 469 | char lastchar; |
WiredHome | 0:729320f63c5c | 470 | strcpy(temp, webroot); |
WiredHome | 22:55974de690c1 | 471 | lastchar = temp[strlen(temp)-1]; |
WiredHome | 22:55974de690c1 | 472 | if (lastchar != '/' && *queryString != '/') |
WiredHome | 22:55974de690c1 | 473 | strcat(temp, "/"); |
WiredHome | 22:55974de690c1 | 474 | else if (lastchar == '/' && *queryString == '/') |
WiredHome | 22:55974de690c1 | 475 | queryString++; |
WiredHome | 0:729320f63c5c | 476 | strcat(temp, queryString); |
WiredHome | 8:262583f054f6 | 477 | myfree(queryString); |
WiredHome | 0:729320f63c5c | 478 | return temp; |
WiredHome | 0:729320f63c5c | 479 | } else { |
WiredHome | 0:729320f63c5c | 480 | return queryString; |
WiredHome | 0:729320f63c5c | 481 | } |
WiredHome | 0:729320f63c5c | 482 | } |
WiredHome | 0:729320f63c5c | 483 | |
WiredHome | 14:19c5f6151319 | 484 | |
WiredHome | 3:17928786bdb5 | 485 | bool HTTPServer::CheckDynamicHandlers() |
WiredHome | 3:17928786bdb5 | 486 | { |
WiredHome | 3:17928786bdb5 | 487 | bool regHandled = false; |
WiredHome | 0:729320f63c5c | 488 | |
WiredHome | 3:17928786bdb5 | 489 | // If this queryString is in the list of registered handlers, call that |
WiredHome | 3:17928786bdb5 | 490 | for (int i=0; i<handlercount; i++) { |
WiredHome | 3:17928786bdb5 | 491 | if (strcmp(handlers[i].path, queryString) == 0) { |
WiredHome | 13:8975d7928678 | 492 | (*handlers[i].callback)(this, SEND_PAGE, queryString, queryParams, queryParamCount); |
WiredHome | 3:17928786bdb5 | 493 | regHandled = true; |
WiredHome | 3:17928786bdb5 | 494 | break; // we only execute the first one |
WiredHome | 3:17928786bdb5 | 495 | } |
WiredHome | 3:17928786bdb5 | 496 | } |
WiredHome | 3:17928786bdb5 | 497 | return regHandled; |
WiredHome | 3:17928786bdb5 | 498 | } |
WiredHome | 3:17928786bdb5 | 499 | |
WiredHome | 14:19c5f6151319 | 500 | |
WiredHome | 3:17928786bdb5 | 501 | void HTTPServer::SendResponse() |
WiredHome | 3:17928786bdb5 | 502 | { |
WiredHome | 8:262583f054f6 | 503 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 504 | pc->printf("SendResponse(%s) at %u\r\n", queryType, (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 8:262583f054f6 | 505 | #endif |
WiredHome | 3:17928786bdb5 | 506 | if (strcmp(queryType, "GET") == 0 || strcmp(queryType, "POST") == 0) { |
WiredHome | 3:17928786bdb5 | 507 | if (!(queryString[0] == '.' && queryString[1] == '.')) { |
WiredHome | 3:17928786bdb5 | 508 | const char * fType; |
WiredHome | 3:17928786bdb5 | 509 | |
WiredHome | 3:17928786bdb5 | 510 | if (!CheckDynamicHandlers()) { |
WiredHome | 3:17928786bdb5 | 511 | // Otherwise, this queryString must be trying to reference a static file |
WiredHome | 3:17928786bdb5 | 512 | if (queryString[strlen(queryString)-1] == '/') { |
WiredHome | 3:17928786bdb5 | 513 | queryString = rewriteWithDefaultFile(queryString); |
WiredHome | 3:17928786bdb5 | 514 | } |
WiredHome | 3:17928786bdb5 | 515 | // see if we support this file type |
WiredHome | 3:17928786bdb5 | 516 | fType = GetSupportedType(queryString); |
WiredHome | 3:17928786bdb5 | 517 | if (fType) { |
WiredHome | 3:17928786bdb5 | 518 | queryString = rewritePrependWebroot(queryString); |
WiredHome | 3:17928786bdb5 | 519 | SendFile(queryString, fType); |
WiredHome | 3:17928786bdb5 | 520 | } else { |
WiredHome | 3:17928786bdb5 | 521 | //pc->printf("Unsupported file type %s\r\n", queryString); |
WiredHome | 3:17928786bdb5 | 522 | header(404, "Not Found", "Pragma: err - Unsupported type\r\n"); |
WiredHome | 3:17928786bdb5 | 523 | } |
WiredHome | 3:17928786bdb5 | 524 | } |
WiredHome | 3:17928786bdb5 | 525 | } else { |
WiredHome | 3:17928786bdb5 | 526 | //pc->printf("Unsupported path %s\r\n", queryString); |
WiredHome | 3:17928786bdb5 | 527 | header(400, "Bad Request", "Pragma: err - Unsupported path\r\n"); |
WiredHome | 3:17928786bdb5 | 528 | } |
WiredHome | 3:17928786bdb5 | 529 | } else { |
WiredHome | 3:17928786bdb5 | 530 | //pc->printf("Unsupported query type %s\r\n", queryType); |
WiredHome | 3:17928786bdb5 | 531 | header(400, "Bad Request", "Pragma: err - Unsupported query type\r\n"); |
WiredHome | 3:17928786bdb5 | 532 | } |
WiredHome | 17:69ff00ce39f4 | 533 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 534 | pc->printf(" SendResponse complete at %u\r\n", (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 17:69ff00ce39f4 | 535 | #endif |
WiredHome | 17:69ff00ce39f4 | 536 | |
WiredHome | 3:17928786bdb5 | 537 | if (queryType) { |
WiredHome | 8:262583f054f6 | 538 | myfree(queryType); |
WiredHome | 3:17928786bdb5 | 539 | queryType = NULL; |
WiredHome | 3:17928786bdb5 | 540 | } |
WiredHome | 3:17928786bdb5 | 541 | if (queryString) { |
WiredHome | 8:262583f054f6 | 542 | myfree(queryString); |
WiredHome | 3:17928786bdb5 | 543 | queryString = NULL; |
WiredHome | 3:17928786bdb5 | 544 | } |
WiredHome | 3:17928786bdb5 | 545 | if (postQueryString) { |
WiredHome | 8:262583f054f6 | 546 | myfree(postQueryString); |
WiredHome | 3:17928786bdb5 | 547 | postQueryString = NULL; |
WiredHome | 3:17928786bdb5 | 548 | } |
WiredHome | 17:69ff00ce39f4 | 549 | #ifdef DEBUG |
WiredHome | 17:69ff00ce39f4 | 550 | pc->printf(" SendResponse free at %u\r\n", (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 17:69ff00ce39f4 | 551 | #endif |
WiredHome | 3:17928786bdb5 | 552 | } |
WiredHome | 3:17928786bdb5 | 553 | |
WiredHome | 14:19c5f6151319 | 554 | |
WiredHome | 3:17928786bdb5 | 555 | bool HTTPServer::ParseHeader(char * buffer) |
WiredHome | 3:17928786bdb5 | 556 | { |
WiredHome | 3:17928786bdb5 | 557 | char * dblCR; |
WiredHome | 3:17928786bdb5 | 558 | bool advanceState = false; |
WiredHome | 3:17928786bdb5 | 559 | int bytecount; |
WiredHome | 7:99ad7a67f05e | 560 | |
WiredHome | 3:17928786bdb5 | 561 | // Buffer could have partial, but the double \r\n is the key |
WiredHome | 13:8975d7928678 | 562 | // GET /QueryString?this=that&sky=blue HTTP/1.1\r\n |
WiredHome | 8:262583f054f6 | 563 | // GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Con |
WiredHome | 8:262583f054f6 | 564 | // GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Control: max-age=0\r\n\r\n |
WiredHome | 3:17928786bdb5 | 565 | dblCR = strstr(buffer,"\r\n\r\n"); |
WiredHome | 3:17928786bdb5 | 566 | if (dblCR) { // Have to scan from the beginning in case split on \r |
WiredHome | 8:262583f054f6 | 567 | #ifdef DEBUG |
WiredHome | 8:262583f054f6 | 568 | pc->printf("==\r\n%s==\r\n", buffer); |
WiredHome | 3:17928786bdb5 | 569 | #endif |
WiredHome | 3:17928786bdb5 | 570 | char * soRec = buffer; // start of the next record of text |
WiredHome | 13:8975d7928678 | 571 | char * eoRec = strchr(soRec, '\n'); // search for end of the current record |
WiredHome | 7:99ad7a67f05e | 572 | |
WiredHome | 13:8975d7928678 | 573 | headerParamCount = 0; |
WiredHome | 3:17928786bdb5 | 574 | bytecount = strlen(buffer); |
WiredHome | 3:17928786bdb5 | 575 | if (bytecount > maxheaderbytes) |
WiredHome | 3:17928786bdb5 | 576 | maxheaderbytes = bytecount; |
WiredHome | 3:17928786bdb5 | 577 | while (eoRec) { |
WiredHome | 3:17928786bdb5 | 578 | *eoRec = '\0'; |
WiredHome | 3:17928786bdb5 | 579 | if (*(eoRec-1) == '\r') |
WiredHome | 3:17928786bdb5 | 580 | *(eoRec-1) = '\0'; |
WiredHome | 8:262583f054f6 | 581 | // Inspect the supported query types (GET, POST) and ignore (HEAD, PUT, OPTION, DELETE, TRACE, CONNECT] |
WiredHome | 8:262583f054f6 | 582 | // This is very clumsy |
WiredHome | 8:262583f054f6 | 583 | if (strstr(soRec, "GET ") == soRec) { |
WiredHome | 8:262583f054f6 | 584 | Extract(soRec, "GET", &queryString); |
WiredHome | 8:262583f054f6 | 585 | if (queryString) { |
WiredHome | 8:262583f054f6 | 586 | queryType = (char *)mymalloc(strlen("GET")+1); |
WiredHome | 8:262583f054f6 | 587 | strcpy(queryType, "GET"); |
WiredHome | 8:262583f054f6 | 588 | } |
WiredHome | 8:262583f054f6 | 589 | } else if (strstr(soRec, "POST ") == soRec) { |
WiredHome | 8:262583f054f6 | 590 | Extract(soRec, "POST", &queryString); |
WiredHome | 8:262583f054f6 | 591 | if (queryString) { |
WiredHome | 8:262583f054f6 | 592 | queryType = (char *)mymalloc(strlen("POST")+1); |
WiredHome | 8:262583f054f6 | 593 | strcpy(queryType, "POST"); |
WiredHome | 8:262583f054f6 | 594 | } |
WiredHome | 10:9c8d2c6a3469 | 595 | } |
WiredHome | 13:8975d7928678 | 596 | |
WiredHome | 13:8975d7928678 | 597 | // if there is a ": " delimiter, we have a header item to parse into name,value pair |
WiredHome | 13:8975d7928678 | 598 | // "Connection: keep-alive" becomes "Connection" "keep-alive" |
WiredHome | 13:8975d7928678 | 599 | char *delim = strstr(soRec, ": "); |
WiredHome | 13:8975d7928678 | 600 | char *chkSpace = strchr(soRec, ' '); // a field-name has no space ahead of the ":" |
WiredHome | 13:8975d7928678 | 601 | if (delim |
WiredHome | 13:8975d7928678 | 602 | && (!chkSpace || (chkSpace && delim < chkSpace)) |
WiredHome | 13:8975d7928678 | 603 | && headerParamCount < maxheaderParams) { |
WiredHome | 14:19c5f6151319 | 604 | *delim++ = '\0'; // replace ": " with null |
WiredHome | 13:8975d7928678 | 605 | *delim++ = '\0'; |
WiredHome | 13:8975d7928678 | 606 | headerParams[headerParamCount].name = soRec; |
WiredHome | 13:8975d7928678 | 607 | headerParams[headerParamCount].value = delim; |
WiredHome | 13:8975d7928678 | 608 | #ifdef DEBUG |
WiredHome | 13:8975d7928678 | 609 | pc->printf("%d: headerParams[%s] = {%s}\r\n", headerParamCount, |
WiredHome | 13:8975d7928678 | 610 | headerParams[headerParamCount].name, headerParams[headerParamCount].value); |
WiredHome | 13:8975d7928678 | 611 | #endif |
WiredHome | 13:8975d7928678 | 612 | headerParamCount++; |
WiredHome | 13:8975d7928678 | 613 | } |
WiredHome | 3:17928786bdb5 | 614 | soRec = eoRec + 1; |
WiredHome | 3:17928786bdb5 | 615 | eoRec = strchr(soRec, '\n'); |
WiredHome | 3:17928786bdb5 | 616 | } |
WiredHome | 3:17928786bdb5 | 617 | if (queryString) { |
WiredHome | 3:17928786bdb5 | 618 | // We have enough to try to reply |
WiredHome | 12:109bf1558300 | 619 | #ifdef DEBUG |
WiredHome | 8:262583f054f6 | 620 | pc->printf("create reply queryType{%s}, queryString{%s}\r\n", "GET", queryString); |
WiredHome | 12:109bf1558300 | 621 | #endif |
WiredHome | 13:8975d7928678 | 622 | // parse queryParams - if any |
WiredHome | 3:17928786bdb5 | 623 | // /file.htm?name1=value1&name2=value2... |
WiredHome | 3:17928786bdb5 | 624 | // /file.htm?name1&name2=value2... |
WiredHome | 13:8975d7928678 | 625 | queryParamCount = 0; |
WiredHome | 3:17928786bdb5 | 626 | char * paramDelim = strchr(queryString, '?'); |
WiredHome | 3:17928786bdb5 | 627 | if (paramDelim) { |
WiredHome | 3:17928786bdb5 | 628 | *paramDelim++ = '\0'; |
WiredHome | 3:17928786bdb5 | 629 | UnescapeString(paramDelim); // everything after the '?' |
WiredHome | 13:8975d7928678 | 630 | ParseParameters(paramDelim); // pointing at the NULL, but there are queryParams beyond |
WiredHome | 3:17928786bdb5 | 631 | } |
WiredHome | 3:17928786bdb5 | 632 | } else { |
WiredHome | 10:9c8d2c6a3469 | 633 | pc->printf("ERROR: queryString not found in (%s)\r\n", soRec); |
WiredHome | 3:17928786bdb5 | 634 | } |
WiredHome | 3:17928786bdb5 | 635 | advanceState = true; |
WiredHome | 3:17928786bdb5 | 636 | buffer[0] = 0; |
WiredHome | 3:17928786bdb5 | 637 | |
WiredHome | 3:17928786bdb5 | 638 | // This part parses the extra data on a POST method. |
WiredHome | 3:17928786bdb5 | 639 | // Since there has to be a dynamic handler registered for this |
WiredHome | 3:17928786bdb5 | 640 | // it would make sense to move some of this responsibility to |
WiredHome | 3:17928786bdb5 | 641 | // that handler. It could then choose if it wanted to allocate |
WiredHome | 3:17928786bdb5 | 642 | // the requested 'Content-Length' amount of memory. |
WiredHome | 3:17928786bdb5 | 643 | // Should we check the 'Content-Type' to see if it is |
WiredHome | 3:17928786bdb5 | 644 | // 'application/x-www-form-urlencoded'? |
WiredHome | 13:8975d7928678 | 645 | int postBytes = atoi(GetHeaderValue("Content-Length")); |
WiredHome | 3:17928786bdb5 | 646 | bool acceptIt = false; |
WiredHome | 3:17928786bdb5 | 647 | if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) { |
WiredHome | 3:17928786bdb5 | 648 | if (postBytes) { |
WiredHome | 22:55974de690c1 | 649 | int ndxHandler = 0; |
WiredHome | 3:17928786bdb5 | 650 | bool regHandled = false; |
WiredHome | 3:17928786bdb5 | 651 | // Registered Dynamic Handler |
WiredHome | 3:17928786bdb5 | 652 | // Callback and ask if they want to accept this data |
WiredHome | 22:55974de690c1 | 653 | for (ndxHandler=0; ndxHandler<handlercount; ndxHandler++) { |
WiredHome | 22:55974de690c1 | 654 | if (strcmp(handlers[ndxHandler].path, queryString) == 0) { |
WiredHome | 22:55974de690c1 | 655 | acceptIt = (*handlers[ndxHandler].callback)(this, CONTENT_LENGTH_REQUEST, queryString, queryParams, queryParamCount); |
WiredHome | 3:17928786bdb5 | 656 | regHandled = true; |
WiredHome | 3:17928786bdb5 | 657 | break; // we only execute the first one |
WiredHome | 3:17928786bdb5 | 658 | } |
WiredHome | 3:17928786bdb5 | 659 | } |
WiredHome | 3:17928786bdb5 | 660 | |
WiredHome | 3:17928786bdb5 | 661 | if (regHandled && acceptIt) { |
WiredHome | 22:55974de690c1 | 662 | // @todo need to refactor - if the thing is bigger than the buffer, |
WiredHome | 22:55974de690c1 | 663 | // then we can receive it a chunk at a time, and hand off |
WiredHome | 22:55974de690c1 | 664 | // the chunks to the callback. May need callbacks that |
WiredHome | 22:55974de690c1 | 665 | // are: START: extract the filename/object name, |
WiredHome | 22:55974de690c1 | 666 | // NEXT: a chunk of data, |
WiredHome | 22:55974de690c1 | 667 | // END: signals that all chunks were delivered. |
WiredHome | 22:55974de690c1 | 668 | // |
WiredHome | 3:17928786bdb5 | 669 | // If so, we'll make space for it |
WiredHome | 8:262583f054f6 | 670 | postQueryString = (char *)mymalloc(postBytes + 1); |
WiredHome | 3:17928786bdb5 | 671 | if (postQueryString) { |
WiredHome | 3:17928786bdb5 | 672 | char * offset; |
WiredHome | 3:17928786bdb5 | 673 | int len; |
WiredHome | 3:17928786bdb5 | 674 | |
WiredHome | 3:17928786bdb5 | 675 | dblCR += 4; // If we slurped up any of the POST, |
WiredHome | 3:17928786bdb5 | 676 | while (*dblCR && *dblCR <= ' ') |
WiredHome | 3:17928786bdb5 | 677 | dblCR++; |
WiredHome | 3:17928786bdb5 | 678 | strcpy(postQueryString, dblCR); // copy that in and then get the rest |
WiredHome | 3:17928786bdb5 | 679 | while ((len = strlen(postQueryString)) < postBytes) { |
WiredHome | 3:17928786bdb5 | 680 | int n; |
WiredHome | 3:17928786bdb5 | 681 | offset = postQueryString + len; |
WiredHome | 3:17928786bdb5 | 682 | n = client.receive(offset, postBytes - len); |
WiredHome | 3:17928786bdb5 | 683 | if (n >=0) { |
WiredHome | 3:17928786bdb5 | 684 | offset[n] = '\0'; |
WiredHome | 22:55974de690c1 | 685 | //printf("HTTPd: %d of %d: [%s]\r\n", len, postBytes, offset); |
WiredHome | 22:55974de690c1 | 686 | } else if (n < 0) { |
WiredHome | 22:55974de690c1 | 687 | //printf("HTTPd: n=%d\r\n", n); |
WiredHome | 22:55974de690c1 | 688 | break; // no more data, before the plan |
WiredHome | 3:17928786bdb5 | 689 | } |
WiredHome | 3:17928786bdb5 | 690 | } |
WiredHome | 3:17928786bdb5 | 691 | if (len >= 0) { |
WiredHome | 22:55974de690c1 | 692 | // UnescapeString(postQueryString); |
WiredHome | 22:55974de690c1 | 693 | // ParseParameters(postQueryString); |
WiredHome | 22:55974de690c1 | 694 | // use the same handler as for the length check |
WiredHome | 22:55974de690c1 | 695 | acceptIt = (*handlers[ndxHandler].callback)(this, DATA_TRANSFER, postQueryString, NULL, 0); |
WiredHome | 22:55974de690c1 | 696 | } else { |
WiredHome | 22:55974de690c1 | 697 | pc->printf("HTTPd: len error.\r\n"); |
WiredHome | 3:17928786bdb5 | 698 | } |
WiredHome | 22:55974de690c1 | 699 | } else { |
WiredHome | 22:55974de690c1 | 700 | pc->printf("HTTPd: attempt to allocate %d failed.\r\n", postBytes+1); |
WiredHome | 3:17928786bdb5 | 701 | } |
WiredHome | 3:17928786bdb5 | 702 | } else { |
WiredHome | 3:17928786bdb5 | 703 | // Simply copy it to the bitbucket |
WiredHome | 3:17928786bdb5 | 704 | int bytesToDump = postBytes; |
WiredHome | 8:262583f054f6 | 705 | char * bitbucket = (char *)mymalloc(201); |
WiredHome | 3:17928786bdb5 | 706 | dblCR += 4; |
WiredHome | 3:17928786bdb5 | 707 | while (*dblCR && *dblCR <= ' ') |
WiredHome | 3:17928786bdb5 | 708 | dblCR++; |
WiredHome | 3:17928786bdb5 | 709 | bytesToDump -= strlen(dblCR); |
WiredHome | 3:17928786bdb5 | 710 | while (bytesToDump > 0) { |
WiredHome | 3:17928786bdb5 | 711 | int n = (bytesToDump > 200) ? 200 : bytesToDump; |
WiredHome | 3:17928786bdb5 | 712 | n = client.receive(bitbucket, n); |
WiredHome | 3:17928786bdb5 | 713 | bytesToDump -= n; |
WiredHome | 3:17928786bdb5 | 714 | } |
WiredHome | 8:262583f054f6 | 715 | myfree(bitbucket); |
WiredHome | 3:17928786bdb5 | 716 | } |
WiredHome | 3:17928786bdb5 | 717 | } |
WiredHome | 3:17928786bdb5 | 718 | } |
WiredHome | 3:17928786bdb5 | 719 | } |
WiredHome | 3:17928786bdb5 | 720 | return advanceState; |
WiredHome | 3:17928786bdb5 | 721 | } |
WiredHome | 3:17928786bdb5 | 722 | |
WiredHome | 14:19c5f6151319 | 723 | |
WiredHome | 13:8975d7928678 | 724 | const char * HTTPServer::GetHeaderValue(const char * hdr) |
WiredHome | 13:8975d7928678 | 725 | { |
WiredHome | 13:8975d7928678 | 726 | int i; |
WiredHome | 13:8975d7928678 | 727 | |
WiredHome | 13:8975d7928678 | 728 | for (i=0; i<headerParamCount; i++) |
WiredHome | 13:8975d7928678 | 729 | { |
WiredHome | 13:8975d7928678 | 730 | if (strcmp(hdr, headerParams[i].name) == 0) |
WiredHome | 13:8975d7928678 | 731 | return headerParams[i].value; |
WiredHome | 13:8975d7928678 | 732 | } |
WiredHome | 13:8975d7928678 | 733 | return NULL; |
WiredHome | 13:8975d7928678 | 734 | } |
WiredHome | 13:8975d7928678 | 735 | |
WiredHome | 12:109bf1558300 | 736 | |
WiredHome | 7:99ad7a67f05e | 737 | void HTTPServer::GetPerformanceData(SW_PerformanceData * p) |
WiredHome | 7:99ad7a67f05e | 738 | { |
WiredHome | 3:17928786bdb5 | 739 | memcpy(p, &perfData, sizeof(perfData)); |
WiredHome | 3:17928786bdb5 | 740 | } |
WiredHome | 3:17928786bdb5 | 741 | |
WiredHome | 17:69ff00ce39f4 | 742 | unsigned int HTTPServer::GetPerformanceClock() |
WiredHome | 17:69ff00ce39f4 | 743 | { |
WiredHome | 17:69ff00ce39f4 | 744 | return (unsigned int)PerformanceTimer.read_us(); |
WiredHome | 17:69ff00ce39f4 | 745 | } |
WiredHome | 14:19c5f6151319 | 746 | |
WiredHome | 16:6ebacf2946d8 | 747 | unsigned int HTTPServer::RecordPerformanceData(SW_PerformanceParam * param, unsigned int refTime) |
WiredHome | 7:99ad7a67f05e | 748 | { |
WiredHome | 16:6ebacf2946d8 | 749 | unsigned int t_now = (unsigned int)PerformanceTimer.read_us(); |
WiredHome | 3:17928786bdb5 | 750 | param->TotalTime_us += (t_now - refTime); |
WiredHome | 3:17928786bdb5 | 751 | param->Samples++; |
WiredHome | 3:17928786bdb5 | 752 | if ((t_now - refTime) > param->MaxTime_us) |
WiredHome | 3:17928786bdb5 | 753 | param->MaxTime_us = (t_now - refTime); |
WiredHome | 3:17928786bdb5 | 754 | return t_now; |
WiredHome | 3:17928786bdb5 | 755 | } |
WiredHome | 3:17928786bdb5 | 756 | |
WiredHome | 14:19c5f6151319 | 757 | |
WiredHome | 7:99ad7a67f05e | 758 | void HTTPServer::ResetPerformanceData() |
WiredHome | 7:99ad7a67f05e | 759 | { |
WiredHome | 3:17928786bdb5 | 760 | memset(&perfData, 0, sizeof(perfData)); |
WiredHome | 3:17928786bdb5 | 761 | } |
WiredHome | 3:17928786bdb5 | 762 | |
WiredHome | 4:f34642902056 | 763 | |
WiredHome | 21:660143f20b04 | 764 | #ifdef INCLUDE_GETREMOTE |
WiredHome | 21:660143f20b04 | 765 | bool HTTPServer::GetRemoteAddr(char * str, int strSize) |
WiredHome | 21:660143f20b04 | 766 | { |
WiredHome | 21:660143f20b04 | 767 | bool res = false; |
WiredHome | 21:660143f20b04 | 768 | char *p; |
WiredHome | 21:660143f20b04 | 769 | |
WiredHome | 21:660143f20b04 | 770 | if (strSize < 16) { // Can only guard it here w/o modifying Wifly class |
WiredHome | 21:660143f20b04 | 771 | *str = '\0'; |
WiredHome | 21:660143f20b04 | 772 | return res; |
WiredHome | 21:660143f20b04 | 773 | } |
WiredHome | 21:660143f20b04 | 774 | res = wifly->sendCommand("show z\r", NULL, str, strSize); |
WiredHome | 21:660143f20b04 | 775 | if (res) { |
WiredHome | 21:660143f20b04 | 776 | p = strchr(str, '\n'); // truncate after the octets. |
WiredHome | 21:660143f20b04 | 777 | if (p) *p = '\0'; |
WiredHome | 21:660143f20b04 | 778 | p = strchr(str, ' '); // or a space |
WiredHome | 21:660143f20b04 | 779 | if (p) *p = '\0'; |
WiredHome | 21:660143f20b04 | 780 | p = strchr(str, '<'); // or a < |
WiredHome | 21:660143f20b04 | 781 | if (p) *p = '\0'; |
WiredHome | 21:660143f20b04 | 782 | res = true; |
WiredHome | 21:660143f20b04 | 783 | } |
WiredHome | 21:660143f20b04 | 784 | wifly->exit(); |
WiredHome | 21:660143f20b04 | 785 | return res; |
WiredHome | 21:660143f20b04 | 786 | } |
WiredHome | 21:660143f20b04 | 787 | #endif |