A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.

Dependents:   Smart-WiFly-WebServer WattEye X10Svr SSDP_Server

Committer:
WiredHome
Date:
Sat Mar 26 20:38:59 2016 +0000
Revision:
44:71f09e4255f4
Parent:
43:3fc773c2986e
Child:
45:360c7c1d07b7
Significant changes; unfortunately as a breaking change. The header() API is where the breaking change is, and will require a small amount of source-code change by the users. Improvements to efficiency, parsing of different methods, and responses.

Who changed what in which revision?

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