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@59:9a71ac02c782, 2019-02-26 (annotated)
- Committer:
- WiredHome
- Date:
- Tue Feb 26 22:45:55 2019 +0000
- Revision:
- 59:9a71ac02c782
- Parent:
- 58:5303e962f711
Update server handler and memory allocation
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
WiredHome | 0:729320f63c5c | 1 | // |
WiredHome | 44:71f09e4255f4 | 2 | // @note Copyright © 2014-2016 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 | 58:5303e962f711 | 14 | #include "SW_String.h" // Secure methods and others that were missing |
WiredHome | 29:00116fc9da74 | 15 | |
WiredHome | 59:9a71ac02c782 | 16 | //#define DEBUG "HTTP" |
WiredHome | 38:c8fa31e6fe02 | 17 | #include <cstdio> |
WiredHome | 38:c8fa31e6fe02 | 18 | #if (defined(DEBUG) && !defined(TARGET_LPC11U24)) |
WiredHome | 58:5303e962f711 | 19 | #define DBG(x, ...) std::printf("[DBG %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 58:5303e962f711 | 20 | #define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 58:5303e962f711 | 21 | #define ERR(x, ...) std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 58:5303e962f711 | 22 | #define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 38:c8fa31e6fe02 | 23 | #else |
WiredHome | 38:c8fa31e6fe02 | 24 | #define DBG(x, ...) |
WiredHome | 38:c8fa31e6fe02 | 25 | #define WARN(x, ...) |
WiredHome | 38:c8fa31e6fe02 | 26 | #define ERR(x, ...) |
WiredHome | 38:c8fa31e6fe02 | 27 | #define INFO(x, ...) |
WiredHome | 38:c8fa31e6fe02 | 28 | #endif |
WiredHome | 38:c8fa31e6fe02 | 29 | |
WiredHome | 31:8f72be717a3c | 30 | #include "SW_HTTPServer.h" // define DEBUG before this |
WiredHome | 27:90a1f5a5392f | 31 | |
WiredHome | 33:ef165a67ab22 | 32 | #define CHUNK_SIZE 1500 // max size of a single chunk (probably limited by Ethernet to 1500) |
WiredHome | 33:ef165a67ab22 | 33 | #define HANG_TIMEOUT_MS 250 // If we're waiting on the host, which may never respond, this is the timeout |
WiredHome | 0:729320f63c5c | 34 | |
WiredHome | 0:729320f63c5c | 35 | const char * DEFAULT_FILENAME = "index.htm"; |
WiredHome | 0:729320f63c5c | 36 | |
WiredHome | 8:262583f054f6 | 37 | // Header information to always send (must be \r\n terminated) |
WiredHome | 44:71f09e4255f4 | 38 | static const char hdr_httpver[] = "HTTP/1.1"; // supported HTTP/1.1 protocol (sort of) |
WiredHome | 44:71f09e4255f4 | 39 | static const char nl[] = "\r\n"; // final \r\n for the termination of the header |
WiredHome | 0:729320f63c5c | 40 | |
WiredHome | 44:71f09e4255f4 | 41 | // Header items that are sent if the user does not provide their own options. |
WiredHome | 44:71f09e4255f4 | 42 | static const char hdr_age[] = "Max-age: 0\r\n"; // expires right away |
WiredHome | 44:71f09e4255f4 | 43 | static const char hdr_server[] = "Server: Smart_Server v0.2\r\n"; // Server |
WiredHome | 44:71f09e4255f4 | 44 | static const char hdr_close[] = "Connection: close\r\n"; // tell the client the server closes the connection immediately |
WiredHome | 14:19c5f6151319 | 45 | |
WiredHome | 0:729320f63c5c | 46 | static const struct { |
WiredHome | 0:729320f63c5c | 47 | char *ext; |
WiredHome | 0:729320f63c5c | 48 | char *filetype; |
WiredHome | 0:729320f63c5c | 49 | } extensions [] = { |
WiredHome | 3:17928786bdb5 | 50 | {".gif", "Content-Type: image/gif\r\n" }, |
WiredHome | 3:17928786bdb5 | 51 | {".jpg", "Content-Type: image/jpeg\r\n" }, |
WiredHome | 3:17928786bdb5 | 52 | {".jpeg","Content-Type: image/jpeg\r\n" }, |
WiredHome | 3:17928786bdb5 | 53 | {".ico", "Content-Type: image/x-icon\r\n" }, |
WiredHome | 33:ef165a67ab22 | 54 | {".bmp", "Content-Type: image/bmp\r\n" }, |
WiredHome | 3:17928786bdb5 | 55 | {".png", "Content-Type: image/png\r\n" }, |
WiredHome | 3:17928786bdb5 | 56 | {".zip", "Content-Type: image/zip\r\n" }, |
WiredHome | 3:17928786bdb5 | 57 | {".gz", "Content-Type: image/gz\r\n" }, |
WiredHome | 3:17928786bdb5 | 58 | {".tar", "Content-Type: image/tar\r\n" }, |
WiredHome | 3:17928786bdb5 | 59 | {".txt", "Content-Type: plain/text\r\n" }, |
WiredHome | 3:17928786bdb5 | 60 | {".pdf", "Content-Type: application/pdf\r\n" }, |
WiredHome | 3:17928786bdb5 | 61 | {".htm", "Content-Type: text/html\r\n" }, |
WiredHome | 3:17928786bdb5 | 62 | {".html","Content-Type: text/html\r\n" }, |
WiredHome | 44:71f09e4255f4 | 63 | {".xml", "Content-Type: text/xml\r\n" }, |
WiredHome | 47:4c29c8f0cff2 | 64 | {".js", "Content-Type: text/javascript\r\n" }, |
WiredHome | 0:729320f63c5c | 65 | {0,0} |
WiredHome | 0:729320f63c5c | 66 | }; |
WiredHome | 0:729320f63c5c | 67 | |
WiredHome | 44:71f09e4255f4 | 68 | typedef struct { |
WiredHome | 44:71f09e4255f4 | 69 | char * queryType; |
WiredHome | 44:71f09e4255f4 | 70 | int notused; |
WiredHome | 44:71f09e4255f4 | 71 | } QueryMethod; |
WiredHome | 44:71f09e4255f4 | 72 | |
WiredHome | 44:71f09e4255f4 | 73 | // Be sure to include a trailing space. |
WiredHome | 44:71f09e4255f4 | 74 | static const QueryMethod QueryMethodList[] = { |
WiredHome | 44:71f09e4255f4 | 75 | {"GET ", 0}, |
WiredHome | 44:71f09e4255f4 | 76 | {"POST ", 0}, |
WiredHome | 44:71f09e4255f4 | 77 | {"HEAD ", 0}, |
WiredHome | 44:71f09e4255f4 | 78 | {"PUT ", 0}, |
WiredHome | 44:71f09e4255f4 | 79 | {"OPTION ", 0}, |
WiredHome | 44:71f09e4255f4 | 80 | {"DELETE ", 0}, |
WiredHome | 44:71f09e4255f4 | 81 | {"TRACE ", 0}, |
WiredHome | 44:71f09e4255f4 | 82 | {"CONNECT ", 0}, |
WiredHome | 44:71f09e4255f4 | 83 | {NULL, 0} |
WiredHome | 44:71f09e4255f4 | 84 | }; |
WiredHome | 44:71f09e4255f4 | 85 | |
WiredHome | 59:9a71ac02c782 | 86 | #if 1 && defined(DEBUG) |
WiredHome | 30:864843965b40 | 87 | // Haven't learned anything from this in a long time, so disabled. |
WiredHome | 12:109bf1558300 | 88 | // This uses standard library dynamic memory management, but for an |
WiredHome | 9:2ea342765c9d | 89 | // embedded system there are alternates that may make better sense - |
WiredHome | 9:2ea342765c9d | 90 | // search the web for embedded system malloc alternates. |
WiredHome | 59:9a71ac02c782 | 91 | void * HTTPServer::MyMalloc(int Bytes, int LINE) |
WiredHome | 8:262583f054f6 | 92 | { |
WiredHome | 59:9a71ac02c782 | 93 | void * pMem = malloc(Bytes); |
WiredHome | 59:9a71ac02c782 | 94 | pc->printf("[INF %s %4d] MyMalloc %p = malloc(%d)\r\n", DEBUG, LINE, pMem, Bytes); |
WiredHome | 59:9a71ac02c782 | 95 | return pMem; |
WiredHome | 8:262583f054f6 | 96 | } |
WiredHome | 29:00116fc9da74 | 97 | char HTTPServer::toP(void * x) |
WiredHome | 11:17d84c41a7b3 | 98 | { |
WiredHome | 11:17d84c41a7b3 | 99 | char * c = (char *) x; |
WiredHome | 30:864843965b40 | 100 | if (*c >= ' ' && *c < 0x7F) // isprint() |
WiredHome | 11:17d84c41a7b3 | 101 | return *c; |
WiredHome | 11:17d84c41a7b3 | 102 | else |
WiredHome | 11:17d84c41a7b3 | 103 | return '.'; |
WiredHome | 11:17d84c41a7b3 | 104 | } |
WiredHome | 59:9a71ac02c782 | 105 | #define mymalloc(Bytes) MyMalloc(Bytes, __LINE__) |
WiredHome | 8:262583f054f6 | 106 | #define myfree(x) \ |
WiredHome | 59:9a71ac02c782 | 107 | pc->printf("[INF %s %4d] MyMalloc %p = free(%02x %02x %02x %02x %02x ... %c%c%c%c%c)\r\n", \ |
WiredHome | 59:9a71ac02c782 | 108 | DEBUG, __LINE__, \ |
WiredHome | 59:9a71ac02c782 | 109 | x, \ |
WiredHome | 11:17d84c41a7b3 | 110 | *x, *(x+1), *(x+2), *(x+3), *(x+4), \ |
WiredHome | 11:17d84c41a7b3 | 111 | toP(x), toP(x+1), toP(x+2), toP(x+3), toP(x+4) ); \ |
WiredHome | 8:262583f054f6 | 112 | free(x); |
WiredHome | 8:262583f054f6 | 113 | #else |
WiredHome | 8:262583f054f6 | 114 | #define mymalloc(x) malloc(x) |
WiredHome | 8:262583f054f6 | 115 | #define myfree(x) free(x) |
WiredHome | 8:262583f054f6 | 116 | #endif |
WiredHome | 8:262583f054f6 | 117 | |
WiredHome | 58:5303e962f711 | 118 | |
WiredHome | 58:5303e962f711 | 119 | |
WiredHome | 3:17928786bdb5 | 120 | HTTPServer::HTTPServer( |
WiredHome | 7:99ad7a67f05e | 121 | int port, |
WiredHome | 7:99ad7a67f05e | 122 | const char * _webroot, |
WiredHome | 43:3fc773c2986e | 123 | int _maxheaderParams, |
WiredHome | 13:8975d7928678 | 124 | int _maxqueryParams, |
WiredHome | 7:99ad7a67f05e | 125 | int _maxdynamicpages, |
WiredHome | 49:cd391662f254 | 126 | int blockingtime, |
WiredHome | 7:99ad7a67f05e | 127 | PC * _pc, |
WiredHome | 7:99ad7a67f05e | 128 | int _allocforheader, |
WiredHome | 49:cd391662f254 | 129 | int _allocforfile |
WiredHome | 49:cd391662f254 | 130 | ) |
WiredHome | 0:729320f63c5c | 131 | { |
WiredHome | 58:5303e962f711 | 132 | int wrSize = strlen(_webroot)+1; |
WiredHome | 58:5303e962f711 | 133 | |
WiredHome | 58:5303e962f711 | 134 | webroot = (char *)malloc(wrSize); |
WiredHome | 58:5303e962f711 | 135 | strcpy_s(webroot, wrSize, _webroot); |
WiredHome | 27:90a1f5a5392f | 136 | if (strlen(webroot)>1 && webroot[strlen(webroot)-1] == '/') // remove trailing '/' |
WiredHome | 27:90a1f5a5392f | 137 | webroot[strlen(webroot)-1] = '\0'; |
WiredHome | 44:71f09e4255f4 | 138 | filenameAliasList = NULL; |
WiredHome | 43:3fc773c2986e | 139 | maxheaderParams = _maxheaderParams; |
WiredHome | 13:8975d7928678 | 140 | headerParams = (namevalue *)malloc(maxheaderParams * sizeof(namevalue)); |
WiredHome | 39:0427544a5c08 | 141 | |
WiredHome | 39:0427544a5c08 | 142 | maxqueryParams = _maxqueryParams; |
WiredHome | 13:8975d7928678 | 143 | queryParams = (namevalue *)malloc(maxqueryParams * sizeof(namevalue)); |
WiredHome | 39:0427544a5c08 | 144 | queryParamCount = 0; |
WiredHome | 39:0427544a5c08 | 145 | |
WiredHome | 39:0427544a5c08 | 146 | maxPostParams = _maxqueryParams; // Same as Query params, but for post method |
WiredHome | 39:0427544a5c08 | 147 | postParams = (namevalue *)malloc(maxPostParams * sizeof(namevalue)); |
WiredHome | 39:0427544a5c08 | 148 | postParamCount = 0; |
WiredHome | 43:3fc773c2986e | 149 | maxdynamicpages = _maxdynamicpages; |
WiredHome | 0:729320f63c5c | 150 | handlers = (handler *)malloc(maxdynamicpages * sizeof(handler)); |
WiredHome | 59:9a71ac02c782 | 151 | handlercount = 0; |
WiredHome | 59:9a71ac02c782 | 152 | ndxActiveHandler = -1; |
WiredHome | 3:17928786bdb5 | 153 | headerbuffersize = _allocforheader; |
WiredHome | 3:17928786bdb5 | 154 | headerbuffer = (char *)malloc(headerbuffersize); |
WiredHome | 0:729320f63c5c | 155 | pc = _pc; |
WiredHome | 3:17928786bdb5 | 156 | queryType = NULL; |
WiredHome | 3:17928786bdb5 | 157 | queryString = NULL; |
WiredHome | 3:17928786bdb5 | 158 | postQueryString = NULL; |
WiredHome | 3:17928786bdb5 | 159 | maxheaderbytes = 0; |
WiredHome | 0:729320f63c5c | 160 | server = new TCPSocketServer(); |
WiredHome | 0:729320f63c5c | 161 | server->bind(port); |
WiredHome | 51:758601b9bacd | 162 | server->listen(5); |
WiredHome | 57:fb81057b4d77 | 163 | server->set_blocking(false, blockingtime); |
WiredHome | 56:df915cb8ba9a | 164 | client.set_blocking(true, blockingtime); //@TODO client is separate from server. any way to combine? |
WiredHome | 3:17928786bdb5 | 165 | ResetPerformanceData(); |
WiredHome | 10:9c8d2c6a3469 | 166 | PerformanceTimer.start(); |
WiredHome | 0:729320f63c5c | 167 | } |
WiredHome | 0:729320f63c5c | 168 | |
WiredHome | 0:729320f63c5c | 169 | HTTPServer::~HTTPServer() |
WiredHome | 0:729320f63c5c | 170 | { |
WiredHome | 8:262583f054f6 | 171 | int i; |
WiredHome | 8:262583f054f6 | 172 | |
WiredHome | 8:262583f054f6 | 173 | for (i=0; i<handlercount; i++) |
WiredHome | 8:262583f054f6 | 174 | myfree(handlers[i].path); |
WiredHome | 8:262583f054f6 | 175 | myfree(headerbuffer); |
WiredHome | 8:262583f054f6 | 176 | myfree(handlers); |
WiredHome | 13:8975d7928678 | 177 | myfree(queryParams); |
WiredHome | 8:262583f054f6 | 178 | myfree(webroot); |
WiredHome | 0:729320f63c5c | 179 | webroot = NULL; |
WiredHome | 0:729320f63c5c | 180 | } |
WiredHome | 0:729320f63c5c | 181 | |
WiredHome | 44:71f09e4255f4 | 182 | void HTTPServer::RegisterFilenameAliasList(const namevalue * namevaluelist) |
WiredHome | 44:71f09e4255f4 | 183 | { |
WiredHome | 44:71f09e4255f4 | 184 | filenameAliasList = namevaluelist; |
WiredHome | 44:71f09e4255f4 | 185 | } |
WiredHome | 44:71f09e4255f4 | 186 | |
WiredHome | 3:17928786bdb5 | 187 | int HTTPServer::GetMaxHeaderSize() |
WiredHome | 3:17928786bdb5 | 188 | { |
WiredHome | 3:17928786bdb5 | 189 | return maxheaderbytes; |
WiredHome | 3:17928786bdb5 | 190 | } |
WiredHome | 3:17928786bdb5 | 191 | |
WiredHome | 0:729320f63c5c | 192 | bool HTTPServer::RegisterHandler(const char * path, Handler callback) |
WiredHome | 0:729320f63c5c | 193 | { |
WiredHome | 0:729320f63c5c | 194 | if (handlercount < maxdynamicpages && path && callback) { |
WiredHome | 8:262583f054f6 | 195 | handlers[handlercount].path = (char *)mymalloc(strlen(path)+1); |
WiredHome | 0:729320f63c5c | 196 | memcpy(handlers[handlercount].path, path, strlen(path)+1); |
WiredHome | 0:729320f63c5c | 197 | handlers[handlercount].callback = callback; |
WiredHome | 0:729320f63c5c | 198 | handlercount++; |
WiredHome | 0:729320f63c5c | 199 | return true; |
WiredHome | 0:729320f63c5c | 200 | } else { |
WiredHome | 0:729320f63c5c | 201 | return false; |
WiredHome | 0:729320f63c5c | 202 | } |
WiredHome | 0:729320f63c5c | 203 | } |
WiredHome | 0:729320f63c5c | 204 | |
WiredHome | 59:9a71ac02c782 | 205 | const char * HTTPServer::GetActiveHandlerPath(void) |
WiredHome | 59:9a71ac02c782 | 206 | { |
WiredHome | 59:9a71ac02c782 | 207 | if (ndxActiveHandler >= 0) |
WiredHome | 59:9a71ac02c782 | 208 | return handlers[ndxActiveHandler].path; |
WiredHome | 59:9a71ac02c782 | 209 | else |
WiredHome | 59:9a71ac02c782 | 210 | return NULL; |
WiredHome | 59:9a71ac02c782 | 211 | } |
WiredHome | 59:9a71ac02c782 | 212 | |
WiredHome | 2:a29c32190037 | 213 | // Poll() |
WiredHome | 0:729320f63c5c | 214 | // |
WiredHome | 0:729320f63c5c | 215 | // *OPEN*GET /x=1 HTTP/1.1 |
WiredHome | 0:729320f63c5c | 216 | // Host: 192.168.1.140 |
WiredHome | 0:729320f63c5c | 217 | // Connection: keep-alive |
WiredHome | 0:729320f63c5c | 218 | // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
WiredHome | 0:729320f63c5c | 219 | // 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 | 220 | // Accept-Encoding: gzip,deflate,sdch |
WiredHome | 0:729320f63c5c | 221 | // Accept-Language: en-US,en;q=0.8 |
WiredHome | 0:729320f63c5c | 222 | // |
WiredHome | 2:a29c32190037 | 223 | void HTTPServer::Poll() |
WiredHome | 0:729320f63c5c | 224 | { |
WiredHome | 3:17928786bdb5 | 225 | typedef enum { |
WiredHome | 3:17928786bdb5 | 226 | Idle, // waiting for a connection |
WiredHome | 29:00116fc9da74 | 227 | ReceivingHeader, // receiving header |
WiredHome | 29:00116fc9da74 | 228 | ReceivingPayload, // receiving a section after the Header |
WiredHome | 3:17928786bdb5 | 229 | Sending, // send the response |
WiredHome | 7:99ad7a67f05e | 230 | WaitingToClose, // small timeout to close |
WiredHome | 7:99ad7a67f05e | 231 | Reset |
WiredHome | 3:17928786bdb5 | 232 | } state; |
WiredHome | 0:729320f63c5c | 233 | static state op = Idle; |
WiredHome | 3:17928786bdb5 | 234 | static char * bPtr = headerbuffer; |
WiredHome | 0:729320f63c5c | 235 | int n; |
WiredHome | 16:6ebacf2946d8 | 236 | static unsigned int t_ref; // reference point for the PerformanceTimer |
WiredHome | 0:729320f63c5c | 237 | |
WiredHome | 20:786aa5749007 | 238 | #if defined(DEBUG) |
WiredHome | 8:262583f054f6 | 239 | static state lastOp = Reset; |
WiredHome | 7:99ad7a67f05e | 240 | if (lastOp != op) { |
WiredHome | 29:00116fc9da74 | 241 | const char *states[] = {"Idle", "ReceivingHeader", "ReceivingPayload", "Sending", "WaitingToClose", "Reset"}; |
WiredHome | 58:5303e962f711 | 242 | INFO("Poll: %-30s ######## %8u us", states[op], (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 7:99ad7a67f05e | 243 | lastOp = op; |
WiredHome | 7:99ad7a67f05e | 244 | } |
WiredHome | 8:262583f054f6 | 245 | #endif |
WiredHome | 0:729320f63c5c | 246 | switch(op) { |
WiredHome | 3:17928786bdb5 | 247 | default: // not expected to arrive here |
WiredHome | 3:17928786bdb5 | 248 | op = Idle; |
WiredHome | 3:17928786bdb5 | 249 | break; |
WiredHome | 8:262583f054f6 | 250 | |
WiredHome | 3:17928786bdb5 | 251 | case Idle: |
WiredHome | 10:9c8d2c6a3469 | 252 | PerformanceTimer.reset(); |
WiredHome | 17:69ff00ce39f4 | 253 | t_ref = (unsigned int)PerformanceTimer.read_us(); |
WiredHome | 3:17928786bdb5 | 254 | bPtr = headerbuffer; |
WiredHome | 11:17d84c41a7b3 | 255 | if (0 == server->accept(client)) { |
WiredHome | 29:00116fc9da74 | 256 | op = ReceivingHeader; |
WiredHome | 17:69ff00ce39f4 | 257 | t_ref = RecordPerformanceData(&perfData.ConnectionAccepted, t_ref); |
WiredHome | 58:5303e962f711 | 258 | INFO("Accepted at %8u us", |
WiredHome | 58:5303e962f711 | 259 | (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 48:078adbe279ac | 260 | } else { |
WiredHome | 49:cd391662f254 | 261 | //INFO("Timeout waiting for accept()"); |
WiredHome | 3:17928786bdb5 | 262 | } |
WiredHome | 0:729320f63c5c | 263 | break; |
WiredHome | 8:262583f054f6 | 264 | |
WiredHome | 29:00116fc9da74 | 265 | case ReceivingHeader: |
WiredHome | 3:17928786bdb5 | 266 | n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer)); |
WiredHome | 58:5303e962f711 | 267 | INFO("client.receive() returned %d, from %s", n, client.get_address()); |
WiredHome | 59:9a71ac02c782 | 268 | INFO(" ReceivingHeader: %s", bPtr); |
WiredHome | 55:238dd90b6fb3 | 269 | if (n == -2) { |
WiredHome | 55:238dd90b6fb3 | 270 | // was hang here waiting ... op = Sending; which causes misses |
WiredHome | 58:5303e962f711 | 271 | op = WaitingToClose; |
WiredHome | 55:238dd90b6fb3 | 272 | } else if (n < 0) { |
WiredHome | 58:5303e962f711 | 273 | op = WaitingToClose; // bail out... |
WiredHome | 3:17928786bdb5 | 274 | } else if (n) { |
WiredHome | 3:17928786bdb5 | 275 | bPtr[n] = '\0'; |
WiredHome | 29:00116fc9da74 | 276 | switch (ParseHeader(headerbuffer)) { |
WiredHome | 29:00116fc9da74 | 277 | case ACCEPT_ERROR: |
WiredHome | 29:00116fc9da74 | 278 | break; |
WiredHome | 29:00116fc9da74 | 279 | case ACCEPT_COMPLETE: |
WiredHome | 29:00116fc9da74 | 280 | op = Sending; |
WiredHome | 29:00116fc9da74 | 281 | t_ref = RecordPerformanceData(&perfData.HeaderParsed, t_ref); |
WiredHome | 58:5303e962f711 | 282 | INFO("Header Parsed at %8u us", |
WiredHome | 58:5303e962f711 | 283 | (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 29:00116fc9da74 | 284 | break; |
WiredHome | 29:00116fc9da74 | 285 | case ACCEPT_CONTINUE: |
WiredHome | 29:00116fc9da74 | 286 | op = ReceivingPayload; |
WiredHome | 29:00116fc9da74 | 287 | break; |
WiredHome | 3:17928786bdb5 | 288 | } |
WiredHome | 3:17928786bdb5 | 289 | bPtr += n; |
WiredHome | 0:729320f63c5c | 290 | } |
WiredHome | 0:729320f63c5c | 291 | break; |
WiredHome | 8:262583f054f6 | 292 | |
WiredHome | 29:00116fc9da74 | 293 | case ReceivingPayload: |
WiredHome | 29:00116fc9da74 | 294 | // After the header, there is a payload that will be handled |
WiredHome | 33:ef165a67ab22 | 295 | #if 1 |
WiredHome | 58:5303e962f711 | 296 | INFO("ReceivingPayload %8u us", |
WiredHome | 58:5303e962f711 | 297 | (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 33:ef165a67ab22 | 298 | n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer)); |
WiredHome | 33:ef165a67ab22 | 299 | if (n < 0) { |
WiredHome | 33:ef165a67ab22 | 300 | op = Sending; |
WiredHome | 33:ef165a67ab22 | 301 | INFO("*** client.receive() => %d", n); |
WiredHome | 33:ef165a67ab22 | 302 | } else if (n) { |
WiredHome | 33:ef165a67ab22 | 303 | bPtr[n] = '\0'; |
WiredHome | 33:ef165a67ab22 | 304 | INFO("*** payload size %d", n); |
WiredHome | 33:ef165a67ab22 | 305 | } |
WiredHome | 33:ef165a67ab22 | 306 | #else |
WiredHome | 29:00116fc9da74 | 307 | op = Sending; |
WiredHome | 33:ef165a67ab22 | 308 | #endif |
WiredHome | 29:00116fc9da74 | 309 | break; |
WiredHome | 29:00116fc9da74 | 310 | |
WiredHome | 0:729320f63c5c | 311 | case Sending: |
WiredHome | 58:5303e962f711 | 312 | INFO("SendResponse at %8u us", |
WiredHome | 58:5303e962f711 | 313 | (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 3:17928786bdb5 | 314 | SendResponse(); |
WiredHome | 0:729320f63c5c | 315 | op = WaitingToClose; |
WiredHome | 17:69ff00ce39f4 | 316 | t_ref = RecordPerformanceData(&perfData.ResponseSent, t_ref); |
WiredHome | 58:5303e962f711 | 317 | INFO("Response Sent at %8u us", |
WiredHome | 58:5303e962f711 | 318 | (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 0:729320f63c5c | 319 | break; |
WiredHome | 9:2ea342765c9d | 320 | |
WiredHome | 0:729320f63c5c | 321 | case WaitingToClose: |
WiredHome | 58:5303e962f711 | 322 | INFO("close_connection %8u us", |
WiredHome | 58:5303e962f711 | 323 | (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 3:17928786bdb5 | 324 | close_connection(); |
WiredHome | 0:729320f63c5c | 325 | op = Idle; |
WiredHome | 17:69ff00ce39f4 | 326 | RecordPerformanceData(&perfData.ConnectionClosed, t_ref); |
WiredHome | 58:5303e962f711 | 327 | INFO("Connection closed exit: %8u us\r\n\r\n", |
WiredHome | 58:5303e962f711 | 328 | (unsigned int)PerformanceTimer.read_us()-t_ref); |
WiredHome | 0:729320f63c5c | 329 | break; |
WiredHome | 0:729320f63c5c | 330 | } |
WiredHome | 0:729320f63c5c | 331 | } |
WiredHome | 0:729320f63c5c | 332 | |
WiredHome | 0:729320f63c5c | 333 | |
WiredHome | 0:729320f63c5c | 334 | const char * HTTPServer::GetSupportedType(const char * filename) |
WiredHome | 0:729320f63c5c | 335 | { |
WiredHome | 0:729320f63c5c | 336 | int i; |
WiredHome | 0:729320f63c5c | 337 | int buflen = strlen(filename); |
WiredHome | 0:729320f63c5c | 338 | int extlen; |
WiredHome | 0:729320f63c5c | 339 | |
WiredHome | 0:729320f63c5c | 340 | for (i=0; extensions[i].ext != 0; i++) { |
WiredHome | 0:729320f63c5c | 341 | extlen = strlen(extensions[i].ext); |
WiredHome | 0:729320f63c5c | 342 | if ( !strncmp(&filename[buflen-extlen], extensions[i].ext, extlen)) { |
WiredHome | 0:729320f63c5c | 343 | return extensions[i].filetype; |
WiredHome | 0:729320f63c5c | 344 | } |
WiredHome | 0:729320f63c5c | 345 | } |
WiredHome | 0:729320f63c5c | 346 | return NULL; |
WiredHome | 0:729320f63c5c | 347 | } |
WiredHome | 0:729320f63c5c | 348 | |
WiredHome | 3:17928786bdb5 | 349 | |
WiredHome | 53:e5d96abe5e9b | 350 | int HTTPServer::send(const char * msg, int bytes) |
WiredHome | 0:729320f63c5c | 351 | { |
WiredHome | 0:729320f63c5c | 352 | if (bytes == -1) |
WiredHome | 0:729320f63c5c | 353 | bytes = strlen(msg); |
WiredHome | 44:71f09e4255f4 | 354 | INFO("Sending %d bytes", bytes); |
WiredHome | 44:71f09e4255f4 | 355 | //INFO("send:\r\n%s", msg); |
WiredHome | 58:5303e962f711 | 356 | int r = client.send_all((char *)msg, bytes); |
WiredHome | 44:71f09e4255f4 | 357 | INFO("client.send returned: %d", r); |
WiredHome | 53:e5d96abe5e9b | 358 | return r; |
WiredHome | 0:729320f63c5c | 359 | } |
WiredHome | 0:729320f63c5c | 360 | |
WiredHome | 44:71f09e4255f4 | 361 | const char * HTTPServer::FindAlias(const HTTPServer::namevalue * haystack, const char * needle) |
WiredHome | 44:71f09e4255f4 | 362 | { |
WiredHome | 44:71f09e4255f4 | 363 | while (haystack && haystack->name) { |
WiredHome | 44:71f09e4255f4 | 364 | if (strcmp(haystack->name,needle) == 0) |
WiredHome | 44:71f09e4255f4 | 365 | return haystack->value; |
WiredHome | 44:71f09e4255f4 | 366 | haystack++; |
WiredHome | 44:71f09e4255f4 | 367 | } |
WiredHome | 44:71f09e4255f4 | 368 | return needle; |
WiredHome | 44:71f09e4255f4 | 369 | } |
WiredHome | 44:71f09e4255f4 | 370 | |
WiredHome | 44:71f09e4255f4 | 371 | uint32_t HTTPServer::FileSize(const char * filename) |
WiredHome | 44:71f09e4255f4 | 372 | { |
WiredHome | 44:71f09e4255f4 | 373 | uint32_t size = 0; |
WiredHome | 44:71f09e4255f4 | 374 | FILE * fh; |
WiredHome | 44:71f09e4255f4 | 375 | |
WiredHome | 44:71f09e4255f4 | 376 | filename = FindAlias(filenameAliasList, filename); |
WiredHome | 44:71f09e4255f4 | 377 | fh = fopen(filename, "r"); |
WiredHome | 44:71f09e4255f4 | 378 | if (fh) { |
WiredHome | 44:71f09e4255f4 | 379 | fseek(fh, 0, SEEK_END); // seek to end of file |
WiredHome | 44:71f09e4255f4 | 380 | size = ftell(fh); // get current file pointer |
WiredHome | 44:71f09e4255f4 | 381 | fclose(fh); |
WiredHome | 44:71f09e4255f4 | 382 | } |
WiredHome | 44:71f09e4255f4 | 383 | return size; |
WiredHome | 44:71f09e4255f4 | 384 | } |
WiredHome | 3:17928786bdb5 | 385 | |
WiredHome | 0:729320f63c5c | 386 | bool HTTPServer::SendFile(const char * filename, const char * filetype) |
WiredHome | 0:729320f63c5c | 387 | { |
WiredHome | 0:729320f63c5c | 388 | FILE * fp; |
WiredHome | 3:17928786bdb5 | 389 | |
WiredHome | 44:71f09e4255f4 | 390 | INFO("SendFile(%s,...)", filename); |
WiredHome | 44:71f09e4255f4 | 391 | filename = FindAlias(filenameAliasList, filename); |
WiredHome | 44:71f09e4255f4 | 392 | INFO(" Alias(%s,...)", filename); |
WiredHome | 0:729320f63c5c | 393 | fp = fopen(filename,"rb"); |
WiredHome | 0:729320f63c5c | 394 | if (fp) { // can open it |
WiredHome | 8:262583f054f6 | 395 | char *fbuffer = (char *)mymalloc(FILESEND_BUF_SIZE); |
WiredHome | 0:729320f63c5c | 396 | int bytes; |
WiredHome | 0:729320f63c5c | 397 | |
WiredHome | 3:17928786bdb5 | 398 | if (fbuffer) { |
WiredHome | 44:71f09e4255f4 | 399 | char ContentLen[30]; |
WiredHome | 44:71f09e4255f4 | 400 | snprintf(ContentLen, sizeof(ContentLen), "Content-Length: %u\r\n", FileSize(filename)); |
WiredHome | 44:71f09e4255f4 | 401 | header(OK, "OK", filetype, ContentLen); |
WiredHome | 44:71f09e4255f4 | 402 | header(""); |
WiredHome | 58:5303e962f711 | 403 | bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE-1,fp); |
WiredHome | 53:e5d96abe5e9b | 404 | INFO("Start with %d bytes", bytes); |
WiredHome | 58:5303e962f711 | 405 | int retryCount = 2; |
WiredHome | 3:17928786bdb5 | 406 | while (bytes > 0) { |
WiredHome | 53:e5d96abe5e9b | 407 | int r = send(fbuffer, bytes); |
WiredHome | 53:e5d96abe5e9b | 408 | if (r >= 0) { |
WiredHome | 58:5303e962f711 | 409 | bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE-1,fp); |
WiredHome | 53:e5d96abe5e9b | 410 | INFO(" Next %d bytes", bytes); |
WiredHome | 53:e5d96abe5e9b | 411 | //INFO("::%s", fbuffer); |
WiredHome | 58:5303e962f711 | 412 | } else if (r == -2) { |
WiredHome | 58:5303e962f711 | 413 | if (retryCount-- == 0) { |
WiredHome | 58:5303e962f711 | 414 | ERR("send failed with %d [retries] - terminating SendFile().", r); |
WiredHome | 58:5303e962f711 | 415 | break; |
WiredHome | 58:5303e962f711 | 416 | } |
WiredHome | 58:5303e962f711 | 417 | INFO("send failed with %d (timeout), hopefully it will recover.", r); |
WiredHome | 58:5303e962f711 | 418 | } else { |
WiredHome | 58:5303e962f711 | 419 | ERR("send failed with %d, but why? - terminating SendFile().", r); |
WiredHome | 58:5303e962f711 | 420 | break; |
WiredHome | 53:e5d96abe5e9b | 421 | } |
WiredHome | 3:17928786bdb5 | 422 | } |
WiredHome | 8:262583f054f6 | 423 | myfree(fbuffer); |
WiredHome | 3:17928786bdb5 | 424 | } else { |
WiredHome | 44:71f09e4255f4 | 425 | header(Server_Error, "Server Error", NULL, "Pragma: err - insufficient memory\r\n"); |
WiredHome | 44:71f09e4255f4 | 426 | header(""); |
WiredHome | 0:729320f63c5c | 427 | } |
WiredHome | 0:729320f63c5c | 428 | fclose(fp); |
WiredHome | 0:729320f63c5c | 429 | return true; |
WiredHome | 0:729320f63c5c | 430 | } else { |
WiredHome | 44:71f09e4255f4 | 431 | header(Not_Found, "Not Found", NULL, "Pragma: err - Can't open file\r\n"); |
WiredHome | 44:71f09e4255f4 | 432 | header(""); |
WiredHome | 0:729320f63c5c | 433 | return false; |
WiredHome | 0:729320f63c5c | 434 | } |
WiredHome | 0:729320f63c5c | 435 | } |
WiredHome | 0:729320f63c5c | 436 | |
WiredHome | 0:729320f63c5c | 437 | int HTTPServer::HexCharToInt(char c) |
WiredHome | 0:729320f63c5c | 438 | { |
WiredHome | 0:729320f63c5c | 439 | if (c >= 'a' && c <= 'f') |
WiredHome | 0:729320f63c5c | 440 | return (c - 'a' + 10); |
WiredHome | 0:729320f63c5c | 441 | else if (c >= 'A' && c <= 'F') |
WiredHome | 0:729320f63c5c | 442 | return (c - 'A' + 10); |
WiredHome | 0:729320f63c5c | 443 | else if (c >= '0' && c <= '9') |
WiredHome | 0:729320f63c5c | 444 | return c - '0'; |
WiredHome | 0:729320f63c5c | 445 | else |
WiredHome | 0:729320f63c5c | 446 | return 0; |
WiredHome | 0:729320f63c5c | 447 | } |
WiredHome | 0:729320f63c5c | 448 | |
WiredHome | 0:729320f63c5c | 449 | char HTTPServer::HexPairToChar(char * p) |
WiredHome | 0:729320f63c5c | 450 | { |
WiredHome | 0:729320f63c5c | 451 | return 16 * HexCharToInt(*p) + HexCharToInt(*(p+1)); |
WiredHome | 0:729320f63c5c | 452 | } |
WiredHome | 0:729320f63c5c | 453 | |
WiredHome | 39:0427544a5c08 | 454 | // modifies in-place |
WiredHome | 0:729320f63c5c | 455 | void HTTPServer::UnescapeString(char * encoded) |
WiredHome | 0:729320f63c5c | 456 | { |
WiredHome | 0:729320f63c5c | 457 | char *p; |
WiredHome | 0:729320f63c5c | 458 | |
WiredHome | 0:729320f63c5c | 459 | // first convert '+' to ' ' |
WiredHome | 0:729320f63c5c | 460 | p = strchr(encoded, '+'); |
WiredHome | 0:729320f63c5c | 461 | while (p) { |
WiredHome | 0:729320f63c5c | 462 | *p = ' '; |
WiredHome | 0:729320f63c5c | 463 | p = strchr(encoded, '+'); |
WiredHome | 0:729320f63c5c | 464 | } |
WiredHome | 0:729320f63c5c | 465 | // then convert hex '%xx' to char 'x' |
WiredHome | 0:729320f63c5c | 466 | p = strchr(encoded, '%'); |
WiredHome | 0:729320f63c5c | 467 | while (p) { |
WiredHome | 0:729320f63c5c | 468 | if (strchr("0123456789ABCDEFabcdef", *(p+1)) |
WiredHome | 0:729320f63c5c | 469 | && strchr("0123456789ABCDEFabcdef", *(p+2)) ) { |
WiredHome | 0:729320f63c5c | 470 | *p = HexPairToChar(p+1); |
WiredHome | 0:729320f63c5c | 471 | p++; // advance past the % |
WiredHome | 0:729320f63c5c | 472 | char * a = p; |
WiredHome | 0:729320f63c5c | 473 | char * b = p + 2; |
WiredHome | 0:729320f63c5c | 474 | do { |
WiredHome | 0:729320f63c5c | 475 | *a++ = *b++; |
WiredHome | 0:729320f63c5c | 476 | } while (*b); |
WiredHome | 0:729320f63c5c | 477 | *a = '\0'; |
WiredHome | 0:729320f63c5c | 478 | } |
WiredHome | 0:729320f63c5c | 479 | p = strchr(p, '%'); |
WiredHome | 0:729320f63c5c | 480 | } |
WiredHome | 0:729320f63c5c | 481 | } |
WiredHome | 0:729320f63c5c | 482 | |
WiredHome | 0:729320f63c5c | 483 | const char * HTTPServer::GetParameter(const char * name) |
WiredHome | 0:729320f63c5c | 484 | { |
WiredHome | 37:0cb2774e2410 | 485 | INFO("GetParameter(%s)", name); |
WiredHome | 13:8975d7928678 | 486 | for (int i=0; i<queryParamCount; i++) { |
WiredHome | 37:0cb2774e2410 | 487 | INFO(" %d: %s = %s", i, queryParams[i].name, queryParams[i].value); |
WiredHome | 13:8975d7928678 | 488 | if (strcmp(queryParams[i].name, name) == 0) { |
WiredHome | 37:0cb2774e2410 | 489 | INFO(" value {%s}", queryParams[i].value); |
WiredHome | 13:8975d7928678 | 490 | return queryParams[i].value; |
WiredHome | 0:729320f63c5c | 491 | } |
WiredHome | 0:729320f63c5c | 492 | } |
WiredHome | 0:729320f63c5c | 493 | return NULL; |
WiredHome | 0:729320f63c5c | 494 | } |
WiredHome | 0:729320f63c5c | 495 | |
WiredHome | 44:71f09e4255f4 | 496 | const HTTPServer::namevalue * HTTPServer::GetParameter(int index) |
WiredHome | 39:0427544a5c08 | 497 | { |
WiredHome | 39:0427544a5c08 | 498 | if (index < queryParamCount) |
WiredHome | 39:0427544a5c08 | 499 | return &queryParams[index]; |
WiredHome | 39:0427544a5c08 | 500 | else |
WiredHome | 39:0427544a5c08 | 501 | return NULL; |
WiredHome | 39:0427544a5c08 | 502 | } |
WiredHome | 39:0427544a5c08 | 503 | |
WiredHome | 39:0427544a5c08 | 504 | const char * HTTPServer::GetPostParameter(const char * name) |
WiredHome | 39:0427544a5c08 | 505 | { |
WiredHome | 39:0427544a5c08 | 506 | INFO("GetPostParameter(%s)", name); |
WiredHome | 39:0427544a5c08 | 507 | for (int i=0; i<postParamCount; i++) { |
WiredHome | 39:0427544a5c08 | 508 | INFO(" %d: %s = %s", i, postParams[i].name, postParams[i].value); |
WiredHome | 39:0427544a5c08 | 509 | if (strcmp(postParams[i].name, name) == 0) { |
WiredHome | 39:0427544a5c08 | 510 | INFO(" value {%s}", postParams[i].value); |
WiredHome | 39:0427544a5c08 | 511 | return postParams[i].value; |
WiredHome | 39:0427544a5c08 | 512 | } |
WiredHome | 39:0427544a5c08 | 513 | } |
WiredHome | 39:0427544a5c08 | 514 | return NULL; |
WiredHome | 39:0427544a5c08 | 515 | } |
WiredHome | 39:0427544a5c08 | 516 | |
WiredHome | 39:0427544a5c08 | 517 | HTTPServer::namevalue * HTTPServer::GetPostParameter(int index) |
WiredHome | 39:0427544a5c08 | 518 | { |
WiredHome | 39:0427544a5c08 | 519 | if (index < postParamCount) |
WiredHome | 39:0427544a5c08 | 520 | return &postParams[index]; |
WiredHome | 39:0427544a5c08 | 521 | else |
WiredHome | 39:0427544a5c08 | 522 | return NULL; |
WiredHome | 39:0427544a5c08 | 523 | } |
WiredHome | 37:0cb2774e2410 | 524 | |
WiredHome | 59:9a71ac02c782 | 525 | int HTTPServer::ParsePostParameters(char * pPostString) |
WiredHome | 59:9a71ac02c782 | 526 | { |
WiredHome | 59:9a71ac02c782 | 527 | return ParseParameters(postParams, &postParamCount, maxPostParams, pPostString); |
WiredHome | 59:9a71ac02c782 | 528 | } |
WiredHome | 59:9a71ac02c782 | 529 | |
WiredHome | 59:9a71ac02c782 | 530 | |
WiredHome | 0:729320f63c5c | 531 | // this=that&who=what&more=stuff... |
WiredHome | 0:729320f63c5c | 532 | // ^ ^ ^ |
WiredHome | 39:0427544a5c08 | 533 | int HTTPServer::ParseParameters(namevalue * qP, int * qpCount, int maxP, char * pName) |
WiredHome | 0:729320f63c5c | 534 | { |
WiredHome | 0:729320f63c5c | 535 | char * pVal; |
WiredHome | 0:729320f63c5c | 536 | char * pNextName; |
WiredHome | 0:729320f63c5c | 537 | |
WiredHome | 13:8975d7928678 | 538 | // Parse queryParams |
WiredHome | 0:729320f63c5c | 539 | pVal = strchr(pName, '#'); // If there is a '#fragment_id', we can ignore it |
WiredHome | 0:729320f63c5c | 540 | if (pVal) |
WiredHome | 3:17928786bdb5 | 541 | *pVal = '\0'; |
WiredHome | 0:729320f63c5c | 542 | do { |
Sissors | 42:0d5b682bb17a | 543 | INFO("ParseParameters(%s), qpCount: %d", pName, *qpCount); |
WiredHome | 39:0427544a5c08 | 544 | qP->name = pName; |
WiredHome | 0:729320f63c5c | 545 | pVal = strchr(pName, '='); |
WiredHome | 0:729320f63c5c | 546 | pNextName = strchr(pName,'&'); |
WiredHome | 0:729320f63c5c | 547 | if (pVal) { |
WiredHome | 0:729320f63c5c | 548 | if (pNextName == NULL || (pNextName && pNextName > pVal)) { |
WiredHome | 0:729320f63c5c | 549 | *pVal++ = '\0'; |
WiredHome | 39:0427544a5c08 | 550 | qP->value = pVal; |
WiredHome | 0:729320f63c5c | 551 | pName = pVal; |
WiredHome | 0:729320f63c5c | 552 | } |
WiredHome | 0:729320f63c5c | 553 | } |
WiredHome | 0:729320f63c5c | 554 | if (pNextName) { |
WiredHome | 0:729320f63c5c | 555 | pName = pNextName; |
WiredHome | 0:729320f63c5c | 556 | *pName++ = '\0'; |
WiredHome | 0:729320f63c5c | 557 | } else { |
WiredHome | 0:729320f63c5c | 558 | pName = NULL; |
WiredHome | 0:729320f63c5c | 559 | } |
WiredHome | 39:0427544a5c08 | 560 | INFO(" param{%s}={%s}", qP->name, qP->value); |
WiredHome | 39:0427544a5c08 | 561 | *qpCount += 1; |
WiredHome | 39:0427544a5c08 | 562 | qP++; |
WiredHome | 39:0427544a5c08 | 563 | } while (pName && *qpCount < maxP); |
WiredHome | 39:0427544a5c08 | 564 | INFO(" count %d", *qpCount); |
WiredHome | 39:0427544a5c08 | 565 | return *qpCount; |
WiredHome | 0:729320f63c5c | 566 | } |
WiredHome | 0:729320f63c5c | 567 | |
WiredHome | 0:729320f63c5c | 568 | |
WiredHome | 44:71f09e4255f4 | 569 | void HTTPServer::header(HTTPServer::HeaderCodes code, const char * code_text, const char * content_type, const char * optional_text) |
WiredHome | 0:729320f63c5c | 570 | { |
WiredHome | 0:729320f63c5c | 571 | char http[100]; |
WiredHome | 0:729320f63c5c | 572 | |
WiredHome | 50:10db483f5154 | 573 | if (optional_text == NULL) |
WiredHome | 50:10db483f5154 | 574 | optional_text = ""; |
WiredHome | 53:e5d96abe5e9b | 575 | INFO("header(%d, %s, %s, %s)", code, code_text, content_type, optional_text); |
WiredHome | 44:71f09e4255f4 | 576 | snprintf(http, sizeof(http), "%s %i %s\r\n", hdr_httpver, code, code_text); |
WiredHome | 0:729320f63c5c | 577 | send(http); |
WiredHome | 0:729320f63c5c | 578 | if (content_type) { |
WiredHome | 0:729320f63c5c | 579 | send(content_type); |
WiredHome | 0:729320f63c5c | 580 | } |
WiredHome | 50:10db483f5154 | 581 | if (*optional_text != '\0') { |
WiredHome | 50:10db483f5154 | 582 | send(optional_text); |
WiredHome | 44:71f09e4255f4 | 583 | } else { |
WiredHome | 50:10db483f5154 | 584 | if (strlen(hdr_age) + strlen(hdr_server) + strlen(hdr_close) + strlen(nl) < sizeof(http)) { |
WiredHome | 58:5303e962f711 | 585 | strcpy_s(http, 100, hdr_age); |
WiredHome | 58:5303e962f711 | 586 | strcat_s(http, 100, hdr_server); |
WiredHome | 58:5303e962f711 | 587 | strcat_s(http, 100, hdr_close); |
WiredHome | 58:5303e962f711 | 588 | strcat_s(http, 100, nl); |
WiredHome | 50:10db483f5154 | 589 | send(http); |
WiredHome | 50:10db483f5154 | 590 | } else { |
WiredHome | 50:10db483f5154 | 591 | send(hdr_age); |
WiredHome | 50:10db483f5154 | 592 | send(hdr_server); |
WiredHome | 50:10db483f5154 | 593 | send(hdr_close); |
WiredHome | 50:10db483f5154 | 594 | send(nl); |
WiredHome | 50:10db483f5154 | 595 | } |
WiredHome | 0:729320f63c5c | 596 | } |
WiredHome | 0:729320f63c5c | 597 | } |
WiredHome | 0:729320f63c5c | 598 | |
WiredHome | 44:71f09e4255f4 | 599 | void HTTPServer::header(const char * partialheader) |
WiredHome | 44:71f09e4255f4 | 600 | { |
WiredHome | 44:71f09e4255f4 | 601 | if (!partialheader || *partialheader == '\0') |
WiredHome | 44:71f09e4255f4 | 602 | send(nl); |
WiredHome | 44:71f09e4255f4 | 603 | else |
WiredHome | 44:71f09e4255f4 | 604 | send(partialheader); |
WiredHome | 44:71f09e4255f4 | 605 | } |
WiredHome | 14:19c5f6151319 | 606 | |
WiredHome | 7:99ad7a67f05e | 607 | bool HTTPServer::close_connection() |
WiredHome | 0:729320f63c5c | 608 | { |
WiredHome | 7:99ad7a67f05e | 609 | bool res; |
WiredHome | 7:99ad7a67f05e | 610 | |
Sissors | 41:6f2f1fb96742 | 611 | res = client.close(); |
WiredHome | 27:90a1f5a5392f | 612 | INFO("close connection returned %d", res); |
WiredHome | 7:99ad7a67f05e | 613 | return res; |
WiredHome | 0:729320f63c5c | 614 | } |
WiredHome | 0:729320f63c5c | 615 | |
WiredHome | 14:19c5f6151319 | 616 | |
WiredHome | 0:729320f63c5c | 617 | bool HTTPServer::Extract(char * haystack, char * needle, char ** string) |
WiredHome | 0:729320f63c5c | 618 | { |
WiredHome | 0:729320f63c5c | 619 | bool ret = false; // assume failure until proven otherwise |
WiredHome | 0:729320f63c5c | 620 | char * qs = NULL; |
WiredHome | 0:729320f63c5c | 621 | char * eqs = NULL; |
WiredHome | 0:729320f63c5c | 622 | char * container = NULL; |
WiredHome | 0:729320f63c5c | 623 | char * get = strstr(haystack, needle); // what if not at the front? |
WiredHome | 44:71f09e4255f4 | 624 | |
WiredHome | 0:729320f63c5c | 625 | if (get) { |
WiredHome | 44:71f09e4255f4 | 626 | // Seems to be a valid "GET /QueryString HTTP/1.1" |
WiredHome | 44:71f09e4255f4 | 627 | // or "POST /upnp/control/metainfo1 HTTP/1.0" |
WiredHome | 8:262583f054f6 | 628 | // or "...<needle>param..." |
WiredHome | 0:729320f63c5c | 629 | qs = get + strlen(needle); // in case the needle didn't have space delimiters |
WiredHome | 0:729320f63c5c | 630 | while (*qs == ' ') |
WiredHome | 0:729320f63c5c | 631 | qs++; |
WiredHome | 0:729320f63c5c | 632 | // /QueryString\0HTTP/1.1\0\0 |
WiredHome | 0:729320f63c5c | 633 | if (*string) // recycle old string when working a new one |
WiredHome | 8:262583f054f6 | 634 | myfree(*string); |
WiredHome | 58:5303e962f711 | 635 | int ctSize = strlen(qs); |
WiredHome | 58:5303e962f711 | 636 | container = (char *)mymalloc(ctSize); |
WiredHome | 0:729320f63c5c | 637 | if (container) { |
WiredHome | 58:5303e962f711 | 638 | strcpy_s(container, ctSize, qs); |
WiredHome | 0:729320f63c5c | 639 | eqs = strchr(container, ' '); |
WiredHome | 0:729320f63c5c | 640 | if (eqs) |
WiredHome | 0:729320f63c5c | 641 | *eqs = '\0'; |
WiredHome | 0:729320f63c5c | 642 | *string = container; |
WiredHome | 27:90a1f5a5392f | 643 | INFO("Extract(%s) = %s", needle, container); |
WiredHome | 0:729320f63c5c | 644 | ret = true; |
WiredHome | 0:729320f63c5c | 645 | } else { |
WiredHome | 0:729320f63c5c | 646 | *string = NULL; // something bad happened... no memory |
WiredHome | 0:729320f63c5c | 647 | } |
WiredHome | 0:729320f63c5c | 648 | } |
WiredHome | 0:729320f63c5c | 649 | return ret; |
WiredHome | 0:729320f63c5c | 650 | } |
WiredHome | 0:729320f63c5c | 651 | |
WiredHome | 14:19c5f6151319 | 652 | |
WiredHome | 0:729320f63c5c | 653 | char * HTTPServer::rewriteWithDefaultFile(char * queryString) |
WiredHome | 0:729320f63c5c | 654 | { |
WiredHome | 58:5303e962f711 | 655 | int tSize = strlen(queryString) + strlen(DEFAULT_FILENAME) + 1; |
WiredHome | 58:5303e962f711 | 656 | char * temp = (char *)mymalloc(tSize); |
WiredHome | 0:729320f63c5c | 657 | |
WiredHome | 0:729320f63c5c | 658 | if (temp) { |
WiredHome | 0:729320f63c5c | 659 | *temp = '\0'; |
WiredHome | 58:5303e962f711 | 660 | strcpy_s(temp, tSize, queryString); |
WiredHome | 58:5303e962f711 | 661 | strcat_s(temp, tSize, DEFAULT_FILENAME); |
WiredHome | 8:262583f054f6 | 662 | myfree(queryString); |
WiredHome | 0:729320f63c5c | 663 | return temp; |
WiredHome | 0:729320f63c5c | 664 | } else { |
WiredHome | 0:729320f63c5c | 665 | return queryString; |
WiredHome | 0:729320f63c5c | 666 | } |
WiredHome | 0:729320f63c5c | 667 | } |
WiredHome | 0:729320f63c5c | 668 | |
WiredHome | 14:19c5f6151319 | 669 | |
WiredHome | 0:729320f63c5c | 670 | char * HTTPServer::rewritePrependWebroot(char * queryString) |
WiredHome | 0:729320f63c5c | 671 | { |
WiredHome | 58:5303e962f711 | 672 | int tSize = strlen(webroot) + strlen(queryString) + 2; |
WiredHome | 58:5303e962f711 | 673 | char * temp = (char *)mymalloc(tSize); // save room for '/' |
WiredHome | 0:729320f63c5c | 674 | |
WiredHome | 0:729320f63c5c | 675 | if (temp) { |
WiredHome | 24:062431453abb | 676 | char *lastC; |
WiredHome | 0:729320f63c5c | 677 | *temp = '\0'; |
WiredHome | 58:5303e962f711 | 678 | strcpy_s(temp, tSize, webroot); |
WiredHome | 24:062431453abb | 679 | lastC = &temp[strlen(temp)-1]; |
WiredHome | 24:062431453abb | 680 | if (*lastC == '/' && *queryString == '/') |
WiredHome | 24:062431453abb | 681 | queryString++; // don't create two '/' |
WiredHome | 24:062431453abb | 682 | else if (*lastC != '/' && *queryString != '/') |
WiredHome | 58:5303e962f711 | 683 | strcat_s(temp, tSize, "/"); |
WiredHome | 58:5303e962f711 | 684 | strcat_s(temp, tSize, queryString); |
WiredHome | 8:262583f054f6 | 685 | myfree(queryString); |
WiredHome | 0:729320f63c5c | 686 | return temp; |
WiredHome | 0:729320f63c5c | 687 | } else { |
WiredHome | 0:729320f63c5c | 688 | return queryString; |
WiredHome | 0:729320f63c5c | 689 | } |
WiredHome | 0:729320f63c5c | 690 | } |
WiredHome | 0:729320f63c5c | 691 | |
WiredHome | 14:19c5f6151319 | 692 | |
WiredHome | 3:17928786bdb5 | 693 | bool HTTPServer::CheckDynamicHandlers() |
WiredHome | 3:17928786bdb5 | 694 | { |
WiredHome | 3:17928786bdb5 | 695 | bool regHandled = false; |
WiredHome | 0:729320f63c5c | 696 | |
WiredHome | 3:17928786bdb5 | 697 | // If this queryString is in the list of registered handlers, call that |
WiredHome | 59:9a71ac02c782 | 698 | for (ndxActiveHandler=0; ndxActiveHandler<handlercount; ndxActiveHandler++) { |
WiredHome | 59:9a71ac02c782 | 699 | if (strcmp(handlers[ndxActiveHandler].path, queryString) == 0) { |
WiredHome | 59:9a71ac02c782 | 700 | INFO("CheckDynamicHandlers - SEND_PAGE %s", handlers[ndxActiveHandler].path); |
WiredHome | 59:9a71ac02c782 | 701 | (*handlers[ndxActiveHandler].callback)(this, SEND_PAGE, queryString, queryParams, queryParamCount); |
WiredHome | 3:17928786bdb5 | 702 | regHandled = true; |
WiredHome | 59:9a71ac02c782 | 703 | ndxActiveHandler = -1; |
WiredHome | 3:17928786bdb5 | 704 | break; // we only execute the first one |
WiredHome | 3:17928786bdb5 | 705 | } |
WiredHome | 3:17928786bdb5 | 706 | } |
WiredHome | 3:17928786bdb5 | 707 | return regHandled; |
WiredHome | 3:17928786bdb5 | 708 | } |
WiredHome | 3:17928786bdb5 | 709 | |
WiredHome | 14:19c5f6151319 | 710 | |
WiredHome | 3:17928786bdb5 | 711 | void HTTPServer::SendResponse() |
WiredHome | 3:17928786bdb5 | 712 | { |
WiredHome | 58:5303e962f711 | 713 | INFO("SendResponse(%5s) at %8u us", |
WiredHome | 58:5303e962f711 | 714 | queryType, (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 44:71f09e4255f4 | 715 | if (strcmp(queryType, "GET ") == 0 || strcmp(queryType, "POST ") == 0) { |
WiredHome | 3:17928786bdb5 | 716 | if (!(queryString[0] == '.' && queryString[1] == '.')) { |
WiredHome | 3:17928786bdb5 | 717 | const char * fType; |
WiredHome | 3:17928786bdb5 | 718 | |
WiredHome | 3:17928786bdb5 | 719 | if (!CheckDynamicHandlers()) { |
WiredHome | 3:17928786bdb5 | 720 | // Otherwise, this queryString must be trying to reference a static file |
WiredHome | 3:17928786bdb5 | 721 | if (queryString[strlen(queryString)-1] == '/') { |
WiredHome | 3:17928786bdb5 | 722 | queryString = rewriteWithDefaultFile(queryString); |
WiredHome | 3:17928786bdb5 | 723 | } |
WiredHome | 44:71f09e4255f4 | 724 | INFO(" queryString: %s", queryString); |
WiredHome | 3:17928786bdb5 | 725 | // see if we support this file type |
WiredHome | 3:17928786bdb5 | 726 | fType = GetSupportedType(queryString); |
WiredHome | 3:17928786bdb5 | 727 | if (fType) { |
WiredHome | 3:17928786bdb5 | 728 | queryString = rewritePrependWebroot(queryString); |
WiredHome | 44:71f09e4255f4 | 729 | INFO(" SendFile(%s,%s)", queryString, fType); |
WiredHome | 3:17928786bdb5 | 730 | SendFile(queryString, fType); |
WiredHome | 3:17928786bdb5 | 731 | } else { |
WiredHome | 44:71f09e4255f4 | 732 | header(Not_Found, "Not Found", NULL, "Pragma: err - Unsupported type\r\n"); |
WiredHome | 44:71f09e4255f4 | 733 | header(""); |
WiredHome | 3:17928786bdb5 | 734 | } |
WiredHome | 3:17928786bdb5 | 735 | } |
WiredHome | 3:17928786bdb5 | 736 | } else { |
WiredHome | 44:71f09e4255f4 | 737 | header(Bad_Request, "Bad Request", NULL, "Pragma: err - Unsupported path\r\n"); |
WiredHome | 44:71f09e4255f4 | 738 | header(""); |
WiredHome | 3:17928786bdb5 | 739 | } |
WiredHome | 3:17928786bdb5 | 740 | } else { |
WiredHome | 44:71f09e4255f4 | 741 | header(Bad_Request, "Bad Request", NULL, "Pragma: err - Unsupported query type\r\n"); |
WiredHome | 44:71f09e4255f4 | 742 | header(""); |
WiredHome | 3:17928786bdb5 | 743 | } |
WiredHome | 58:5303e962f711 | 744 | INFO(" SendResponse complete at %8u us", |
WiredHome | 58:5303e962f711 | 745 | (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 17:69ff00ce39f4 | 746 | |
WiredHome | 59:9a71ac02c782 | 747 | if (postQueryString) { |
WiredHome | 59:9a71ac02c782 | 748 | myfree(postQueryString); |
WiredHome | 59:9a71ac02c782 | 749 | postQueryString = NULL; |
WiredHome | 59:9a71ac02c782 | 750 | } |
WiredHome | 3:17928786bdb5 | 751 | if (queryType) { |
WiredHome | 8:262583f054f6 | 752 | myfree(queryType); |
WiredHome | 3:17928786bdb5 | 753 | queryType = NULL; |
WiredHome | 3:17928786bdb5 | 754 | } |
WiredHome | 3:17928786bdb5 | 755 | if (queryString) { |
WiredHome | 8:262583f054f6 | 756 | myfree(queryString); |
WiredHome | 3:17928786bdb5 | 757 | queryString = NULL; |
WiredHome | 3:17928786bdb5 | 758 | } |
WiredHome | 58:5303e962f711 | 759 | INFO(" SendResponse free at %8u us", |
WiredHome | 58:5303e962f711 | 760 | (unsigned int)PerformanceTimer.read_us()); |
WiredHome | 3:17928786bdb5 | 761 | } |
WiredHome | 3:17928786bdb5 | 762 | |
WiredHome | 14:19c5f6151319 | 763 | |
WiredHome | 29:00116fc9da74 | 764 | HTTPServer::CallBackResults HTTPServer::ParseHeader(char * buffer) |
WiredHome | 3:17928786bdb5 | 765 | { |
WiredHome | 3:17928786bdb5 | 766 | char * dblCR; |
WiredHome | 29:00116fc9da74 | 767 | CallBackResults advanceState = ACCEPT_ERROR; |
WiredHome | 3:17928786bdb5 | 768 | int bytecount; |
WiredHome | 7:99ad7a67f05e | 769 | |
WiredHome | 3:17928786bdb5 | 770 | // Buffer could have partial, but the double \r\n is the key |
WiredHome | 13:8975d7928678 | 771 | // GET /QueryString?this=that&sky=blue HTTP/1.1\r\n |
WiredHome | 8:262583f054f6 | 772 | // GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Con |
WiredHome | 8:262583f054f6 | 773 | // GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Control: max-age=0\r\n\r\n |
WiredHome | 39:0427544a5c08 | 774 | // POST /dyn2 HTTP/1.2\r\nAccept: text/html, application/xhtml+xml, */*\r\n\r\n |
WiredHome | 3:17928786bdb5 | 775 | dblCR = strstr(buffer,"\r\n\r\n"); |
WiredHome | 3:17928786bdb5 | 776 | if (dblCR) { // Have to scan from the beginning in case split on \r |
WiredHome | 44:71f09e4255f4 | 777 | INFO("\r\n==\r\n%s\r\n==", buffer); |
WiredHome | 3:17928786bdb5 | 778 | char * soRec = buffer; // start of the next record of text |
WiredHome | 13:8975d7928678 | 779 | char * eoRec = strchr(soRec, '\n'); // search for end of the current record |
WiredHome | 7:99ad7a67f05e | 780 | |
WiredHome | 13:8975d7928678 | 781 | headerParamCount = 0; |
WiredHome | 3:17928786bdb5 | 782 | bytecount = strlen(buffer); |
WiredHome | 3:17928786bdb5 | 783 | if (bytecount > maxheaderbytes) |
WiredHome | 3:17928786bdb5 | 784 | maxheaderbytes = bytecount; |
WiredHome | 3:17928786bdb5 | 785 | while (eoRec) { |
WiredHome | 3:17928786bdb5 | 786 | *eoRec = '\0'; |
WiredHome | 3:17928786bdb5 | 787 | if (*(eoRec-1) == '\r') |
WiredHome | 3:17928786bdb5 | 788 | *(eoRec-1) = '\0'; |
WiredHome | 44:71f09e4255f4 | 789 | INFO("method: %s", soRec); |
WiredHome | 44:71f09e4255f4 | 790 | #if 1 |
WiredHome | 44:71f09e4255f4 | 791 | const QueryMethod * qm = QueryMethodList; |
WiredHome | 44:71f09e4255f4 | 792 | while (*qm->queryType) { |
WiredHome | 44:71f09e4255f4 | 793 | if (strstr(soRec, qm->queryType) == soRec) { |
WiredHome | 44:71f09e4255f4 | 794 | Extract(soRec, qm->queryType, &queryString); |
WiredHome | 44:71f09e4255f4 | 795 | if (queryString) { |
WiredHome | 58:5303e962f711 | 796 | int qSize = strlen(qm->queryType)+1; |
WiredHome | 58:5303e962f711 | 797 | queryType = (char *)mymalloc(qSize); |
WiredHome | 58:5303e962f711 | 798 | strcpy_s(queryType, qSize, qm->queryType); |
WiredHome | 44:71f09e4255f4 | 799 | } |
WiredHome | 44:71f09e4255f4 | 800 | } |
WiredHome | 44:71f09e4255f4 | 801 | qm++; |
WiredHome | 44:71f09e4255f4 | 802 | } |
WiredHome | 44:71f09e4255f4 | 803 | #else |
WiredHome | 8:262583f054f6 | 804 | // Inspect the supported query types (GET, POST) and ignore (HEAD, PUT, OPTION, DELETE, TRACE, CONNECT] |
WiredHome | 44:71f09e4255f4 | 805 | // This is presently very clumsy |
WiredHome | 44:71f09e4255f4 | 806 | if (strstr(soRec, "GET ") == soRec) { |
WiredHome | 8:262583f054f6 | 807 | Extract(soRec, "GET", &queryString); |
WiredHome | 8:262583f054f6 | 808 | if (queryString) { |
WiredHome | 58:5303e962f711 | 809 | int qSize = strlen("GET")+1; |
WiredHome | 58:5303e962f711 | 810 | queryType = (char *)mymalloc(qSize); |
WiredHome | 58:5303e962f711 | 811 | strcpy_s(queryType, qSize, "GET"); |
WiredHome | 8:262583f054f6 | 812 | } |
WiredHome | 8:262583f054f6 | 813 | } else if (strstr(soRec, "POST ") == soRec) { |
WiredHome | 39:0427544a5c08 | 814 | Extract(soRec, "POST", &queryString); |
WiredHome | 8:262583f054f6 | 815 | if (queryString) { |
WiredHome | 58:5303e962f711 | 816 | int qSize = strlen("POST")+1; |
WiredHome | 58:5303e962f711 | 817 | queryType = (char *)mymalloc(qSize); |
WiredHome | 58:5303e962f711 | 818 | strcpy_s(queryType, qSize, "POST"); |
WiredHome | 8:262583f054f6 | 819 | } |
WiredHome | 10:9c8d2c6a3469 | 820 | } |
WiredHome | 44:71f09e4255f4 | 821 | #endif |
WiredHome | 44:71f09e4255f4 | 822 | |
WiredHome | 13:8975d7928678 | 823 | // if there is a ": " delimiter, we have a header item to parse into name,value pair |
WiredHome | 13:8975d7928678 | 824 | // "Connection: keep-alive" becomes "Connection" "keep-alive" |
WiredHome | 13:8975d7928678 | 825 | char *delim = strstr(soRec, ": "); |
WiredHome | 13:8975d7928678 | 826 | char *chkSpace = strchr(soRec, ' '); // a field-name has no space ahead of the ":" |
WiredHome | 44:71f09e4255f4 | 827 | //INFO("hpc:%d,mhp:%d, {%s}", headerParamCount, maxheaderParams, soRec); |
WiredHome | 29:00116fc9da74 | 828 | if (delim |
WiredHome | 29:00116fc9da74 | 829 | && (!chkSpace || (chkSpace && delim < chkSpace)) |
WiredHome | 29:00116fc9da74 | 830 | && headerParamCount < maxheaderParams) { |
WiredHome | 14:19c5f6151319 | 831 | *delim++ = '\0'; // replace ": " with null |
WiredHome | 13:8975d7928678 | 832 | *delim++ = '\0'; |
WiredHome | 13:8975d7928678 | 833 | headerParams[headerParamCount].name = soRec; |
WiredHome | 13:8975d7928678 | 834 | headerParams[headerParamCount].value = delim; |
WiredHome | 37:0cb2774e2410 | 835 | INFO("%d: headerParams[%s] = {%s}", headerParamCount, |
WiredHome | 37:0cb2774e2410 | 836 | headerParams[headerParamCount].name, headerParams[headerParamCount].value); |
WiredHome | 13:8975d7928678 | 837 | headerParamCount++; |
WiredHome | 13:8975d7928678 | 838 | } |
WiredHome | 3:17928786bdb5 | 839 | soRec = eoRec + 1; |
WiredHome | 3:17928786bdb5 | 840 | eoRec = strchr(soRec, '\n'); |
WiredHome | 44:71f09e4255f4 | 841 | if (soRec > dblCR) // Just walked past the end of the header |
WiredHome | 44:71f09e4255f4 | 842 | break; |
WiredHome | 3:17928786bdb5 | 843 | } |
WiredHome | 29:00116fc9da74 | 844 | |
WiredHome | 3:17928786bdb5 | 845 | if (queryString) { |
WiredHome | 3:17928786bdb5 | 846 | // We have enough to try to reply |
WiredHome | 37:0cb2774e2410 | 847 | INFO("create reply queryType{%s}, queryString{%s}", queryType, queryString); |
WiredHome | 13:8975d7928678 | 848 | // parse queryParams - if any |
WiredHome | 3:17928786bdb5 | 849 | // /file.htm?name1=value1&name2=value2... |
WiredHome | 3:17928786bdb5 | 850 | // /file.htm?name1&name2=value2... |
WiredHome | 13:8975d7928678 | 851 | queryParamCount = 0; |
WiredHome | 3:17928786bdb5 | 852 | char * paramDelim = strchr(queryString, '?'); |
WiredHome | 3:17928786bdb5 | 853 | if (paramDelim) { |
WiredHome | 3:17928786bdb5 | 854 | *paramDelim++ = '\0'; |
WiredHome | 3:17928786bdb5 | 855 | UnescapeString(paramDelim); // everything after the '?' |
WiredHome | 39:0427544a5c08 | 856 | ParseParameters(queryParams, &queryParamCount, maxqueryParams, paramDelim); // pointing past the NULL, and there are queryParams here |
WiredHome | 3:17928786bdb5 | 857 | } |
WiredHome | 3:17928786bdb5 | 858 | } else { |
WiredHome | 29:00116fc9da74 | 859 | ERR("queryString not found in (%s) [this should never happen]", soRec); |
WiredHome | 3:17928786bdb5 | 860 | } |
WiredHome | 44:71f09e4255f4 | 861 | advanceState = ACCEPT_COMPLETE; // Should be ACCEPT_CONTINUE and the stuff below moves out of here |
WiredHome | 3:17928786bdb5 | 862 | buffer[0] = 0; |
WiredHome | 3:17928786bdb5 | 863 | |
WiredHome | 3:17928786bdb5 | 864 | // This part parses the extra data on a POST method. |
WiredHome | 3:17928786bdb5 | 865 | // Since there has to be a dynamic handler registered for this |
WiredHome | 3:17928786bdb5 | 866 | // it would make sense to move some of this responsibility to |
WiredHome | 3:17928786bdb5 | 867 | // that handler. It could then choose if it wanted to allocate |
WiredHome | 3:17928786bdb5 | 868 | // the requested 'Content-Length' amount of memory. |
WiredHome | 13:8975d7928678 | 869 | int postBytes = atoi(GetHeaderValue("Content-Length")); |
WiredHome | 28:f93ef41b78e1 | 870 | CallBackResults acceptIt = ACCEPT_ERROR; |
WiredHome | 44:71f09e4255f4 | 871 | INFO("Content-Length: %d", postBytes); |
WiredHome | 44:71f09e4255f4 | 872 | if (strcmp(queryType, "POST ") == 0 ) { |
WiredHome | 44:71f09e4255f4 | 873 | INFO("parse POST data %d bytes", postBytes); // We might have no idea how much data is coming... |
WiredHome | 44:71f09e4255f4 | 874 | int ndxHandler = 0; |
WiredHome | 44:71f09e4255f4 | 875 | bool regHandled = false; |
WiredHome | 44:71f09e4255f4 | 876 | // Registered Dynamic Handler |
WiredHome | 44:71f09e4255f4 | 877 | // Callback and ask if they want to accept this data |
WiredHome | 44:71f09e4255f4 | 878 | for (ndxHandler=0; ndxHandler<handlercount; ndxHandler++) { |
WiredHome | 44:71f09e4255f4 | 879 | INFO("is '%s' a handler for '%s' ?", handlers[ndxHandler].path, queryString); |
WiredHome | 44:71f09e4255f4 | 880 | if (strcmp(handlers[ndxHandler].path, queryString) == 0) { |
WiredHome | 44:71f09e4255f4 | 881 | acceptIt = (*handlers[ndxHandler].callback)(this, CONTENT_LENGTH_REQUEST, queryString, queryParams, queryParamCount); |
WiredHome | 44:71f09e4255f4 | 882 | regHandled = true; |
WiredHome | 44:71f09e4255f4 | 883 | break; // only one callback per path allowed |
WiredHome | 3:17928786bdb5 | 884 | } |
WiredHome | 44:71f09e4255f4 | 885 | } |
WiredHome | 44:71f09e4255f4 | 886 | INFO("reghandled: %d, acceptIt: %d", regHandled, acceptIt); |
WiredHome | 44:71f09e4255f4 | 887 | if (regHandled && acceptIt != ACCEPT_ERROR) { |
WiredHome | 44:71f09e4255f4 | 888 | // @todo need to refactor - if the thing is bigger than the buffer, |
WiredHome | 44:71f09e4255f4 | 889 | // then we can receive it a chunk at a time, and hand off |
WiredHome | 44:71f09e4255f4 | 890 | // the chunks to the callback. May need callbacks that |
WiredHome | 44:71f09e4255f4 | 891 | // are: DATA_TRANSFER: self-detect to extract the filename/object name, |
WiredHome | 44:71f09e4255f4 | 892 | // DATA_TRANSFER: subsequent chunk of data, |
WiredHome | 44:71f09e4255f4 | 893 | // DATA_TRANSFER_END: signals that last chunk is enclosed. |
WiredHome | 44:71f09e4255f4 | 894 | // |
WiredHome | 44:71f09e4255f4 | 895 | // If so, we'll make space for it |
WiredHome | 44:71f09e4255f4 | 896 | postQueryString = (char *)mymalloc(CHUNK_SIZE); |
WiredHome | 44:71f09e4255f4 | 897 | //INFO("Free space %d", Free()); |
WiredHome | 44:71f09e4255f4 | 898 | INFO("postQueryString %p", postQueryString); |
WiredHome | 44:71f09e4255f4 | 899 | if (postQueryString) { |
WiredHome | 44:71f09e4255f4 | 900 | int len = 0; |
WiredHome | 44:71f09e4255f4 | 901 | int ttlCount; |
WiredHome | 44:71f09e4255f4 | 902 | Timer escapePlan; |
WiredHome | 44:71f09e4255f4 | 903 | bool escape = false; |
WiredHome | 44:71f09e4255f4 | 904 | |
WiredHome | 44:71f09e4255f4 | 905 | INFO("Processing tail..."); |
WiredHome | 44:71f09e4255f4 | 906 | escapePlan.start(); |
WiredHome | 44:71f09e4255f4 | 907 | dblCR += 4; // There may be some after the double CR that we need |
WiredHome | 44:71f09e4255f4 | 908 | ttlCount = strlen(dblCR); |
WiredHome | 58:5303e962f711 | 909 | strcpy_s(postQueryString, CHUNK_SIZE, dblCR); |
WiredHome | 59:9a71ac02c782 | 910 | INFO(" {%s}", postQueryString); |
WiredHome | 44:71f09e4255f4 | 911 | while ((!postBytes || ttlCount < postBytes) && !escape) { |
WiredHome | 44:71f09e4255f4 | 912 | INFO("ttlCount: %d < postBytes: %d, of max chunk alloc %d", ttlCount, postBytes, CHUNK_SIZE); |
WiredHome | 44:71f09e4255f4 | 913 | len = client.receive(postQueryString + ttlCount, CHUNK_SIZE - ttlCount); |
WiredHome | 44:71f09e4255f4 | 914 | if (len > 0) { |
WiredHome | 44:71f09e4255f4 | 915 | ttlCount += len; |
WiredHome | 44:71f09e4255f4 | 916 | postQueryString[ttlCount] = '\0'; // Whether binary or ASCII, this is ok as it's after the data |
WiredHome | 44:71f09e4255f4 | 917 | INFO(" postBytes %d: [%s], [%d]", postBytes, postQueryString, ndxHandler); |
WiredHome | 44:71f09e4255f4 | 918 | escapePlan.reset(); |
WiredHome | 44:71f09e4255f4 | 919 | } else if (len < 0) { |
WiredHome | 44:71f09e4255f4 | 920 | INFO("*** connection closed ***"); |
WiredHome | 44:71f09e4255f4 | 921 | break; // no more data, before the plan |
WiredHome | 44:71f09e4255f4 | 922 | } else { // n == 0 |
WiredHome | 44:71f09e4255f4 | 923 | ; |
WiredHome | 3:17928786bdb5 | 924 | } |
WiredHome | 44:71f09e4255f4 | 925 | if (escapePlan.read_ms() > HANG_TIMEOUT_MS) { // if no Content-Length, we wait... |
WiredHome | 44:71f09e4255f4 | 926 | escape = true; |
WiredHome | 44:71f09e4255f4 | 927 | WARN("Escape plan activated."); |
WiredHome | 44:71f09e4255f4 | 928 | } |
WiredHome | 44:71f09e4255f4 | 929 | if (postBytes > 0 && ttlCount >= postBytes) |
WiredHome | 44:71f09e4255f4 | 930 | break; |
WiredHome | 3:17928786bdb5 | 931 | } |
WiredHome | 44:71f09e4255f4 | 932 | //postParamCount = 0; |
WiredHome | 59:9a71ac02c782 | 933 | INFO("post: %s", postQueryString); |
WiredHome | 44:71f09e4255f4 | 934 | //We're after the header, so there is "body" stuff which could be anything... |
WiredHome | 44:71f09e4255f4 | 935 | //but probably html or xml stuff... |
WiredHome | 44:71f09e4255f4 | 936 | //ParseParameters(postParams, &postParamCount, maxPostParams, postQueryString); |
WiredHome | 44:71f09e4255f4 | 937 | acceptIt = (*handlers[ndxHandler].callback)(this, DATA_TRANSFER, postQueryString, NULL, ttlCount); |
WiredHome | 59:9a71ac02c782 | 938 | INFO("..processing exit [%d]", acceptIt); |
WiredHome | 44:71f09e4255f4 | 939 | acceptIt = (*handlers[ndxHandler].callback)(this, DATA_TRANSFER_END, NULL, NULL, 0); |
WiredHome | 59:9a71ac02c782 | 940 | INFO("..transfer end[%d]", acceptIt); |
WiredHome | 3:17928786bdb5 | 941 | } else { |
WiredHome | 44:71f09e4255f4 | 942 | ERR("attempt to allocate %d failed.", CHUNK_SIZE); |
WiredHome | 44:71f09e4255f4 | 943 | } |
WiredHome | 44:71f09e4255f4 | 944 | } else { |
WiredHome | 44:71f09e4255f4 | 945 | // Simply copy it to the bitbucket |
WiredHome | 44:71f09e4255f4 | 946 | WARN("No handler, so to the bit bucket it goes ..."); |
WiredHome | 44:71f09e4255f4 | 947 | int bytesToDump = postBytes; |
WiredHome | 44:71f09e4255f4 | 948 | char * bitbucket = (char *)mymalloc(201); |
WiredHome | 44:71f09e4255f4 | 949 | |
WiredHome | 44:71f09e4255f4 | 950 | dblCR += 4; |
WiredHome | 44:71f09e4255f4 | 951 | while (*dblCR && *dblCR <= ' ') |
WiredHome | 44:71f09e4255f4 | 952 | dblCR++; |
WiredHome | 44:71f09e4255f4 | 953 | bytesToDump -= strlen(dblCR); |
WiredHome | 44:71f09e4255f4 | 954 | while (bytesToDump > 0) { |
WiredHome | 44:71f09e4255f4 | 955 | int n = (bytesToDump > 200) ? 200 : bytesToDump; |
WiredHome | 44:71f09e4255f4 | 956 | n = client.receive(bitbucket, n); |
WiredHome | 44:71f09e4255f4 | 957 | if (n < 0) { |
WiredHome | 44:71f09e4255f4 | 958 | ERR("to the bitbucket."); |
WiredHome | 44:71f09e4255f4 | 959 | break; |
WiredHome | 3:17928786bdb5 | 960 | } |
WiredHome | 44:71f09e4255f4 | 961 | bytesToDump -= n; |
WiredHome | 3:17928786bdb5 | 962 | } |
WiredHome | 44:71f09e4255f4 | 963 | myfree(bitbucket); |
WiredHome | 3:17928786bdb5 | 964 | } |
WiredHome | 3:17928786bdb5 | 965 | } |
WiredHome | 3:17928786bdb5 | 966 | } |
WiredHome | 3:17928786bdb5 | 967 | return advanceState; |
WiredHome | 3:17928786bdb5 | 968 | } |
WiredHome | 3:17928786bdb5 | 969 | |
WiredHome | 14:19c5f6151319 | 970 | |
WiredHome | 27:90a1f5a5392f | 971 | |
WiredHome | 13:8975d7928678 | 972 | const char * HTTPServer::GetHeaderValue(const char * hdr) |
WiredHome | 13:8975d7928678 | 973 | { |
WiredHome | 13:8975d7928678 | 974 | int i; |
WiredHome | 29:00116fc9da74 | 975 | |
WiredHome | 29:00116fc9da74 | 976 | for (i=0; i<headerParamCount; i++) { |
WiredHome | 13:8975d7928678 | 977 | if (strcmp(hdr, headerParams[i].name) == 0) |
WiredHome | 13:8975d7928678 | 978 | return headerParams[i].value; |
WiredHome | 29:00116fc9da74 | 979 | } |
WiredHome | 13:8975d7928678 | 980 | return NULL; |
WiredHome | 13:8975d7928678 | 981 | } |
WiredHome | 13:8975d7928678 | 982 | |
WiredHome | 12:109bf1558300 | 983 | |
WiredHome | 7:99ad7a67f05e | 984 | void HTTPServer::GetPerformanceData(SW_PerformanceData * p) |
WiredHome | 7:99ad7a67f05e | 985 | { |
WiredHome | 3:17928786bdb5 | 986 | memcpy(p, &perfData, sizeof(perfData)); |
WiredHome | 3:17928786bdb5 | 987 | } |
WiredHome | 3:17928786bdb5 | 988 | |
WiredHome | 17:69ff00ce39f4 | 989 | unsigned int HTTPServer::GetPerformanceClock() |
WiredHome | 17:69ff00ce39f4 | 990 | { |
WiredHome | 17:69ff00ce39f4 | 991 | return (unsigned int)PerformanceTimer.read_us(); |
WiredHome | 17:69ff00ce39f4 | 992 | } |
WiredHome | 14:19c5f6151319 | 993 | |
WiredHome | 16:6ebacf2946d8 | 994 | unsigned int HTTPServer::RecordPerformanceData(SW_PerformanceParam * param, unsigned int refTime) |
WiredHome | 7:99ad7a67f05e | 995 | { |
WiredHome | 16:6ebacf2946d8 | 996 | unsigned int t_now = (unsigned int)PerformanceTimer.read_us(); |
WiredHome | 3:17928786bdb5 | 997 | param->TotalTime_us += (t_now - refTime); |
WiredHome | 3:17928786bdb5 | 998 | param->Samples++; |
WiredHome | 3:17928786bdb5 | 999 | if ((t_now - refTime) > param->MaxTime_us) |
WiredHome | 3:17928786bdb5 | 1000 | param->MaxTime_us = (t_now - refTime); |
WiredHome | 3:17928786bdb5 | 1001 | return t_now; |
WiredHome | 3:17928786bdb5 | 1002 | } |
WiredHome | 3:17928786bdb5 | 1003 | |
WiredHome | 14:19c5f6151319 | 1004 | |
WiredHome | 7:99ad7a67f05e | 1005 | void HTTPServer::ResetPerformanceData() |
WiredHome | 7:99ad7a67f05e | 1006 | { |
WiredHome | 3:17928786bdb5 | 1007 | memset(&perfData, 0, sizeof(perfData)); |
WiredHome | 3:17928786bdb5 | 1008 | } |
WiredHome | 3:17928786bdb5 | 1009 |