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 Apr 08 19:39:51 2017 +0000
Revision:
48:078adbe279ac
Parent:
47:4c29c8f0cff2
Child:
49:cd391662f254
Minor change - an additional optional parameter on the constructor that defines the blocking time (in msec) when Poll() is called. The default was way too long for most needs.

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