A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.
Dependents: Smart-WiFly-WebServer WattEye X10Svr SSDP_Server
Revision 37:0cb2774e2410, committed 2014-05-11
- Comitter:
- WiredHome
- Date:
- Sun May 11 21:16:42 2014 +0000
- Parent:
- 36:1bb5fa6b109c
- Child:
- 38:c8fa31e6fe02
- Commit message:
- Enhanced to support POST as well as GET methods.
Changed in this revision
SW_HTTPServer.cpp | Show annotated file Show diff for this revision Revisions of this file |
SW_HTTPServer.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/SW_HTTPServer.cpp Fri May 02 00:26:06 2014 +0000 +++ b/SW_HTTPServer.cpp Sun May 11 21:16:42 2014 +0000 @@ -17,20 +17,6 @@ #include "SW_HTTPServer.h" // define DEBUG before this -#if 0 -#if (defined(DEBUG) && !defined(TARGET_LPC11U24)) -#define DBG(x, ...) pc->printf("[DBG %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define WARN(x, ...) pc->printf("[WRN %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define ERR(x, ...) pc->printf("[ERR %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define INFO(x, ...) pc->printf("[INF %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#else -#define DBG(x, ...) -#define WARN(x, ...) -#define ERR(x, ...) -#define INFO(x, ...) -#endif -#endif - #define CHUNK_SIZE 1500 // max size of a single chunk (probably limited by Ethernet to 1500) #define HANG_TIMEOUT_MS 250 // If we're waiting on the host, which may never respond, this is the timeout @@ -368,17 +354,21 @@ const char * HTTPServer::GetParameter(const char * name) { + INFO("GetParameter(%s)", name); for (int i=0; i<queryParamCount; i++) { + INFO(" %d: %s = %s", i, queryParams[i].name, queryParams[i].value); if (strcmp(queryParams[i].name, name) == 0) { + INFO(" value {%s}", queryParams[i].value); return queryParams[i].value; } } return NULL; } + // this=that&who=what&more=stuff... // ^ ^ ^ -void HTTPServer::ParseParameters(char * pName) +int HTTPServer::ParseParameters(char * pName) { char * pVal; char * pNextName; @@ -388,6 +378,7 @@ if (pVal) *pVal = '\0'; do { + INFO("ParseParameters(%s)", pName); queryParams[queryParamCount].name = pName; pVal = strchr(pName, '='); pNextName = strchr(pName,'&'); @@ -398,14 +389,17 @@ pName = pVal; } } - queryParamCount++; if (pNextName) { pName = pNextName; *pName++ = '\0'; } else { pName = NULL; } + INFO(" param{%s}={%s}", queryParams[queryParamCount].name, queryParams[queryParamCount].value); + queryParamCount++; } while (pName && queryParamCount < maxqueryParams); + INFO(" count %d", queryParamCount); + return queryParamCount; } @@ -580,6 +574,7 @@ // GET /QueryString?this=that&sky=blue HTTP/1.1\r\n // GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Con // GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Control: max-age=0\r\n\r\n + // POST /dyn2 HTTP/1.2\r\nAccept: text/html, application/xhtml+xml, */*\r\n\r\nle dblCR = strstr(buffer,"\r\n\r\n"); if (dblCR) { // Have to scan from the beginning in case split on \r INFO("\r\n==\r\n%s==", buffer); @@ -596,14 +591,15 @@ *(eoRec-1) = '\0'; // Inspect the supported query types (GET, POST) and ignore (HEAD, PUT, OPTION, DELETE, TRACE, CONNECT] // This is very clumsy - if (strstr(soRec, "GET ") == soRec) { + INFO("method: %s", soRec); + if (strstr(soRec, "GET ") == soRec) { Extract(soRec, "GET", &queryString); if (queryString) { queryType = (char *)mymalloc(strlen("GET")+1); strcpy(queryType, "GET"); } } else if (strstr(soRec, "POST ") == soRec) { - Extract(soRec, "POST", &queryString); + Extract(soRec, "POST", &queryString); if (queryString) { queryType = (char *)mymalloc(strlen("POST")+1); strcpy(queryType, "POST"); @@ -621,8 +617,8 @@ *delim++ = '\0'; headerParams[headerParamCount].name = soRec; headerParams[headerParamCount].value = delim; - //INFO("%d: headerParams[%s] = {%s}", headerParamCount, - // headerParams[headerParamCount].name, headerParams[headerParamCount].value); + INFO("%d: headerParams[%s] = {%s}", headerParamCount, + headerParams[headerParamCount].name, headerParams[headerParamCount].value); headerParamCount++; } soRec = eoRec + 1; @@ -631,7 +627,7 @@ if (queryString) { // We have enough to try to reply - INFO("create reply queryType{%s}, queryString{%s}", "GET", queryString); + INFO("create reply queryType{%s}, queryString{%s}", queryType, queryString); // parse queryParams - if any // /file.htm?name1=value1&name2=value2... // /file.htm?name1&name2=value2... @@ -640,7 +636,7 @@ if (paramDelim) { *paramDelim++ = '\0'; UnescapeString(paramDelim); // everything after the '?' - ParseParameters(paramDelim); // pointing at the NULL, but there are queryParams beyond + ParseParameters(paramDelim); // pointing past the NULL, and there are queryParams here } } else { ERR("queryString not found in (%s) [this should never happen]", soRec); @@ -656,19 +652,21 @@ int postBytes = atoi(GetHeaderValue("Content-Length")); CallBackResults acceptIt = ACCEPT_ERROR; if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) { + INFO("parse POST data %d bytes", postBytes); if (postBytes) { int ndxHandler = 0; bool regHandled = false; // Registered Dynamic Handler // Callback and ask if they want to accept this data for (ndxHandler=0; ndxHandler<handlercount; ndxHandler++) { + INFO("is '%s' a handler for '%s' ?", handlers[ndxHandler].path, queryString); if (strcmp(handlers[ndxHandler].path, queryString) == 0) { acceptIt = (*handlers[ndxHandler].callback)(this, CONTENT_LENGTH_REQUEST, queryString, queryParams, queryParamCount); regHandled = true; break; // only one callback per path allowed } } - + INFO("reghandled: %d, acceptIt: %d", regHandled, acceptIt); if (regHandled && acceptIt != ACCEPT_ERROR) { // @todo need to refactor - if the thing is bigger than the buffer, // then we can receive it a chunk at a time, and hand off @@ -679,22 +677,28 @@ // // If so, we'll make space for it postQueryString = (char *)mymalloc(CHUNK_SIZE); - INFO("Free space %d", Free()); + //INFO("Free space %d", Free()); + INFO("postQueryString %p", postQueryString); if (postQueryString) { - int len; - int ttlCount = 4; // includes the doubleCR in the total count from Content-Length + int len = 0; + int ttlCount; Timer escapePlan; bool escape = false; + INFO("Processing tail..."); escapePlan.start(); - //INFO("Processing"); - while (ttlCount < postBytes && !escape) { - len = client.receive_all(postQueryString, CHUNK_SIZE); + dblCR += 4; // There may be some after the double CR that we need + ttlCount = strlen(dblCR); + strcpy(postQueryString, dblCR); + INFO(" {%s}", postQueryString); + while (ttlCount <= postBytes && !escape) { + INFO("ttlCount: %d < postBytes: %d, of chunk %d", ttlCount, postBytes, CHUNK_SIZE); + len = client.receive_all(postQueryString + ttlCount, CHUNK_SIZE - ttlCount); if (len > 0) { + INFO(" len: %d, ttlCount: %d < postBytes %d, {%s}", len, ttlCount, postBytes, postQueryString); ttlCount += len; postQueryString[len] = '\0'; // Whether binary or ASCII, this is ok as it's after the data - //INFO("%d bytes is %d of %d: [%s]", len, ttlCount, postBytes, postQueryString); - acceptIt = (*handlers[ndxHandler].callback)(this, DATA_TRANSFER, postQueryString, NULL, len); + INFO(" postBytes %d: [%s], [%d]", postBytes, postQueryString, ndxHandler); escapePlan.reset(); } else if (len < 0) { INFO("*** connection closed ***"); @@ -707,7 +711,8 @@ WARN("Escape plan activated."); } } - //INFO("..processing exit"); + acceptIt = (*handlers[ndxHandler].callback)(this, DATA_TRANSFER, postQueryString, NULL, 0); + INFO("..processing exit"); acceptIt = (*handlers[ndxHandler].callback)(this, DATA_TRANSFER_END, NULL, NULL, 0); myfree(postQueryString); } else { @@ -715,6 +720,7 @@ } } else { // Simply copy it to the bitbucket + WARN("to the bit bucket..."); int bytesToDump = postBytes; char * bitbucket = (char *)mymalloc(201);
--- a/SW_HTTPServer.h Fri May 02 00:26:06 2014 +0000 +++ b/SW_HTTPServer.h Sun May 11 21:16:42 2014 +0000 @@ -202,7 +202,9 @@ /** * This is the prototype for custom handlers that are activated via a callback * - * This callback gets overloaded for a few purposes, which can be identified by the \see CallBackType parameter + * This callback gets overloaded for a few purposes, which can be identified by the + * \see CallBackType parameter. + * * @li CONTENT_LENGTH_REQUEST - the server is asking the callback if it wants to receive the message, * which may require significant memory. If the request is accepted, true should be returned. * If the request is denied, false should be returned. @@ -220,14 +222,17 @@ * * @param svr is a handle to this class, so the callback has access to member functions * @param type is the callback type @see CallBackType - * @param path is the pointer to a large block of information being transferred + * @param path is the pointer to a large block of information being transferred. This pointer + * references a dynamically managed resource, so any information of value must be + * extracted from here, and not referenced into this memory space. * @param queryParams is a pointer based on the callback type. * @param count is the number of items - for type = CONTENT_LENGTH_REQUEST this is the number of * name=value pars in the queryParams parameter, and for the DATA_TRANSFER this is the * number of bytes being passed in the path parameters. * @return one of the @see CallBackResults signals indicating error or successes */ - typedef CallBackResults (* Handler)(HTTPServer * svr, CallBackType type, const char *path, const namevalue *queryParams, int queryParamCount); + typedef CallBackResults (* Handler)(HTTPServer * svr, CallBackType type, const char *path, + const namevalue *queryParams, int queryParamCount); /** * Create the HTTPServer object. @@ -236,16 +241,19 @@ * @param port is the optional parameter for the port number to use, default is 80. * @param webroot is a file system path to the root folder for the web space. If any trailing '/' * is included (e.g. "/web/path/") it will be removed (to "/web/path"). - * @param maxheaderParams defines the maximum number of parameters to extract from a header (Host: 192..\r\nConnection: keep-alive\r\n...) - * @param maxqueryParams defines the maximum number of query parameters to a dynamic function (and the memory to support them). + * @param maxheaderParams defines the maximum number of parameters to extract from a header + * (Host: 192..\r\nConnection: keep-alive\r\n...) + * @param maxqueryParams defines the maximum number of query parameters to a dynamic function + * (and the memory to support them). * @param maxdynamicpages defines the maximum number of dynamic pages that can be registered. * @param pc is the serial port for debug information (I should transform this to a log interface) * @param allocforheader is the memory allocation to support the largest expected header from a client - * @param allocforfile is the memory allocation to support sending a file to the client. This is typically sized to fit - * an ethernet frame. + * @param allocforfile is the memory allocation to support sending a file to the client. This is + * typically sized to fit an ethernet frame. */ - HTTPServer(Wifly * wifly, int port = 80, const char * webroot = "/", int maxheaderParams = 15, int maxqueryParams = 30, int maxdynamicpages = 10, - PC * pc = NULL, int _allocforheader = MAX_HEADER_SIZE, int _allocforfile = FILESEND_BUF_SIZE); + HTTPServer(Wifly * wifly, int port = 80, const char * webroot = "/", int maxheaderParams = 15, + int maxqueryParams = 30, int maxdynamicpages = 10, + PC * pc = NULL, int _allocforheader = MAX_HEADER_SIZE, int _allocforfile = FILESEND_BUF_SIZE); /** * Destructor, which can clean up memory. @@ -287,7 +295,8 @@ * @param optional_text is a pointer to any other text that is part of the header, which must * have \r\n termination. */ - void header(int code = 404, const char * code_text = "Not Found", const char * content_type = NULL, const char * optional_text = NULL); + void header(int code = 404, const char * code_text = "Not Found", const char * content_type = NULL, + const char * optional_text = NULL); /** * Send text to the client @@ -326,7 +335,8 @@ * svr.RegisterHandler("/dyn1", SimpleDynamicPage);svr.RegisterHandler("/dyn1", SimpleDynamicPage); * ... * - * bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *queryParams, int queryParamCount) { + * bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, + * const HTTPServer::namevalue *queryParams, int queryParamCount) { * char buf[100]; * bool ret = false; * @@ -414,8 +424,10 @@ * #fragment_id on the end of the string, it will be removed. * * @param pString is a pointer to the string. + * @returns The total number of items that have been parsed, + * which can include a count from a url query string. */ - void ParseParameters(char * pString); + int ParseParameters(char * pString); /** * Unescape string converts a coded string "in place" into a normal string.