A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.
Dependents: Smart-WiFly-WebServer WattEye X10Svr SSDP_Server
Diff: SW_HTTPServer.cpp
- Revision:
- 29:00116fc9da74
- Parent:
- 28:f93ef41b78e1
- Child:
- 30:864843965b40
diff -r f93ef41b78e1 -r 00116fc9da74 SW_HTTPServer.cpp --- a/SW_HTTPServer.cpp Fri Oct 11 02:33:46 2013 +0000 +++ b/SW_HTTPServer.cpp Fri Oct 11 23:47:31 2013 +0000 @@ -11,16 +11,17 @@ // author David Smart, Smartware Computing // #include "mbed.h" -#include "SW_HTTPServer.h" + #include "Utility.h" #define DEBUG "HTTP" +#include "SW_HTTPServer.h" #if (defined(DEBUG) && !defined(TARGET_LPC11U24)) -#define DBG(x, ...) std::printf("[DBG %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define WARN(x, ...) std::printf("[WRN %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define ERR(x, ...) std::printf("[ERR %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define INFO(x, ...) std::printf("[INF %s%4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); +#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, ...) @@ -62,12 +63,12 @@ // This uses standard library dynamic memory management, but for an // embedded system there are alternates that may make better sense - // search the web for embedded system malloc alternates. -static void * MyMalloc(int x, int y) +void * HTTPServer::MyMalloc(int x, int y) { - std::printf("[INF HTTP%4d] malloc(%d)\r\n", y, x); + pc->printf("[INF HTTP%4d] malloc(%d)\r\n", y, x); return malloc(x); } -static char toP(void * x) +char HTTPServer::toP(void * x) { char * c = (char *) x; if (*c >= ' ' && *c < 0x7F) @@ -169,7 +170,8 @@ { typedef enum { Idle, // waiting for a connection - Receiving, // receiving data + ReceivingHeader, // receiving header + ReceivingPayload, // receiving a section after the Header Sending, // send the response WaitingToClose, // small timeout to close Reset @@ -182,7 +184,7 @@ #if defined(DEBUG) static state lastOp = Reset; if (lastOp != op) { - const char *states[] = {"Idle", "Receiving", "Sending", "WaitingToClose", "Reset"}; + const char *states[] = {"Idle", "ReceivingHeader", "ReceivingPayload", "Sending", "WaitingToClose", "Reset"}; INFO("Poll: %s", states[op]); lastOp = op; } @@ -197,27 +199,40 @@ t_ref = (unsigned int)PerformanceTimer.read_us(); bPtr = headerbuffer; if (0 == server->accept(client)) { - op = Receiving; + op = ReceivingHeader; t_ref = RecordPerformanceData(&perfData.ConnectionAccepted, t_ref); INFO("Accepted at %u", (unsigned int)PerformanceTimer.read_us()); } break; - case Receiving: + case ReceivingHeader: n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer)); if (n < 0) { + op = Sending; INFO("*** client.receive() => %d", n); } else if (n) { bPtr[n] = '\0'; - if (ParseHeader(headerbuffer)) { - op = Sending; - t_ref = RecordPerformanceData(&perfData.HeaderParsed, t_ref); - INFO("Header Parsed at %u", (unsigned int)PerformanceTimer.read_us()); + switch (ParseHeader(headerbuffer)) { + case ACCEPT_ERROR: + break; + case ACCEPT_COMPLETE: + op = Sending; + t_ref = RecordPerformanceData(&perfData.HeaderParsed, t_ref); + INFO("Header Parsed at %u", (unsigned int)PerformanceTimer.read_us()); + break; + case ACCEPT_CONTINUE: + op = ReceivingPayload; + break; } bPtr += n; } break; + case ReceivingPayload: + // After the header, there is a payload that will be handled + op = Sending; + break; + case Sending: SendResponse(); op = WaitingToClose; @@ -537,10 +552,10 @@ } -bool HTTPServer::ParseHeader(char * buffer) +HTTPServer::CallBackResults HTTPServer::ParseHeader(char * buffer) { char * dblCR; - bool advanceState = false; + CallBackResults advanceState = ACCEPT_ERROR; int bytecount; // Buffer could have partial, but the double \r\n is the key @@ -549,7 +564,7 @@ // GET /QueryString HTTP/1.1\r\nHost: 192.168.1.140\r\nCache-Control: max-age=0\r\n\r\n dblCR = strstr(buffer,"\r\n\r\n"); if (dblCR) { // Have to scan from the beginning in case split on \r - INFO("==\r\n%s==\r\n", buffer); + INFO("\r\n==\r\n%s==", buffer); char * soRec = buffer; // start of the next record of text char * eoRec = strchr(soRec, '\n'); // search for end of the current record @@ -581,20 +596,21 @@ // "Connection: keep-alive" becomes "Connection" "keep-alive" char *delim = strstr(soRec, ": "); char *chkSpace = strchr(soRec, ' '); // a field-name has no space ahead of the ":" - if (delim - && (!chkSpace || (chkSpace && delim < chkSpace)) - && headerParamCount < maxheaderParams) { + if (delim + && (!chkSpace || (chkSpace && delim < chkSpace)) + && headerParamCount < maxheaderParams) { *delim++ = '\0'; // replace ": " with null *delim++ = '\0'; headerParams[headerParamCount].name = soRec; headerParams[headerParamCount].value = delim; INFO("%d: headerParams[%s] = {%s}", headerParamCount, - headerParams[headerParamCount].name, headerParams[headerParamCount].value); + headerParams[headerParamCount].name, headerParams[headerParamCount].value); headerParamCount++; } soRec = eoRec + 1; eoRec = strchr(soRec, '\n'); } + if (queryString) { // We have enough to try to reply INFO("create reply queryType{%s}, queryString{%s}", "GET", queryString); @@ -606,12 +622,12 @@ if (paramDelim) { *paramDelim++ = '\0'; UnescapeString(paramDelim); // everything after the '?' - ParseParameters(paramDelim); // pointing at the NULL, but there are queryParams beyond + ParseParameters(paramDelim); // pointing at the NULL, but there are queryParams beyond } } else { - ERR("queryString not found in (%s)", soRec); + ERR("queryString not found in (%s) [this should never happen]", soRec); } - advanceState = true; + advanceState = ACCEPT_COMPLETE; buffer[0] = 0; // This part parses the extra data on a POST method. @@ -619,8 +635,6 @@ // it would make sense to move some of this responsibility to // that handler. It could then choose if it wanted to allocate // the requested 'Content-Length' amount of memory. - // Should we check the 'Content-Type' to see if it is - // 'application/x-www-form-urlencoded'? int postBytes = atoi(GetHeaderValue("Content-Length")); CallBackResults acceptIt = ACCEPT_ERROR; if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) { @@ -633,7 +647,7 @@ if (strcmp(handlers[ndxHandler].path, queryString) == 0) { acceptIt = (*handlers[ndxHandler].callback)(this, CONTENT_LENGTH_REQUEST, queryString, queryParams, queryParamCount); regHandled = true; - break; // we only execute the first one + break; // only one callback per path allowed } } @@ -665,7 +679,7 @@ offset[n] = '\0'; INFO("HTTPd: %d of %d: [%s]", len, postBytes, offset); } else if (n < 0) { - INFO("HTTPd; n=%d", n); + INFO("*** receive returned %d ***", n); break; // no more data, before the plan } } @@ -685,6 +699,7 @@ // Simply copy it to the bitbucket int bytesToDump = postBytes; char * bitbucket = (char *)mymalloc(201); + dblCR += 4; while (*dblCR && *dblCR <= ' ') dblCR++; @@ -692,6 +707,10 @@ while (bytesToDump > 0) { int n = (bytesToDump > 200) ? 200 : bytesToDump; n = client.receive(bitbucket, n); + if (n < 0) { + ERR("to the bitbucket."); + break; + } bytesToDump -= n; } myfree(bitbucket); @@ -707,12 +726,11 @@ const char * HTTPServer::GetHeaderValue(const char * hdr) { int i; - - for (i=0; i<headerParamCount; i++) - { + + for (i=0; i<headerParamCount; i++) { if (strcmp(hdr, headerParams[i].name) == 0) return headerParams[i].value; - } + } return NULL; }