diff --git a/src/app/WEBServer.h b/src/app/WEBServer.h index 00c1bb1..56ed206 100644 --- a/src/app/WEBServer.h +++ b/src/app/WEBServer.h @@ -13,7 +13,15 @@ template class WEBServer : public TCPServer, public HttpConstants { public: - enum HttpParserStatus {HTTP_VERB, HTTP_RESSOURCE, HTTP_VERSION, HTTP_PARAMS, POST_DATA, HEADER_PARAMS}; + enum HttpParserStatus + { + PARSE_HTTP_VERB, + PARSE_HTTP_RESOURCE, + PARSE_HTTP_VERSION, + PARSE_HTTP_RESOURCE_QUERY, + PARSE_HTTP_POST_DATA, + PARSE_HTTP_HEADER_PARAMS + }; enum WEBClientState {ACCEPTED, PARSING, QUERY_PARSED, RESPONSE_SENT, DONE}; struct HttpRequestData @@ -23,7 +31,6 @@ class WEBServer : public TCPServer, public HttpConstants HttpMIMEType HMT; Dictionary getParams; - char *getParamsDataPointer; //Used in the getParams algorithm Dictionary postParams; char *postParamsDataPointer; //Used in the postParams algorithm @@ -33,7 +40,7 @@ class WEBServer : public TCPServer, public HttpConstants uint16_t maxBodyBuffer; }; - WEBServer(uint16_t port = 80, SDClass *sdClass = NULL, uint8_t maxClient = MAX_CLIENT, uint16_t clientDataBufferSize = 255) : TCPServer(port, maxClient, clientDataBufferSize), _sdClass(sdClass) {} + WEBServer(uint16_t port = 80, SDClass *sdClass = NULL, uint8_t maxClient = MAX_CLIENT, uint16_t clientDataBufferSize = 256) : TCPServer(port, maxClient, clientDataBufferSize), _sdClass(sdClass) {} boolean addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*), void *pData, HttpRequestMethod HRM = UNDEFINED) { @@ -112,13 +119,13 @@ class WEBServer : public TCPServer, public HttpConstants { switch(client->_httpParserState) { - case HttpParserStatus::HTTP_VERB: + case HttpParserStatus::PARSE_HTTP_VERB: { #ifdef DEBUG_WEBS Serial.println((char *)client->_data); #endif - char *pVerb = strstr((char *)client->_data, " "); + char *pVerb(strchr((char *)client->_data, ' ')); if(pVerb != NULL) { @@ -138,7 +145,7 @@ class WEBServer : public TCPServer, public HttpConstants Serial.println((char *)client->_data); #endif - client->_httpParserState = HttpParserStatus::HTTP_RESSOURCE; + client->_httpParserState = HttpParserStatus::PARSE_HTTP_RESOURCE; } else { @@ -147,24 +154,46 @@ class WEBServer : public TCPServer, public HttpConstants } } break; - case HttpParserStatus::HTTP_RESSOURCE: + case HttpParserStatus::PARSE_HTTP_RESOURCE: { - char *pRsrc = strstr((char *)client->_data, " "); - - if(pRsrc != NULL) + char *pRsrc(strchr((char *)client->_data, ' ')), *pRsrcQuery(strchr((char *)client->_data, '?')); + //The case where we have the resource complete or not complete with query parameters like : GET /some/path/resource.rsrc?param1=one¶m2 HTTP/1.1 + if(pRsrc || pRsrcQuery) { - *pRsrc = '\0'; - uint16_t safeLength = pRsrc - (char *)client->_data <= client->_httpRequestData.maxResourceBuffer ? pRsrc - (char *)client->_data : client->_httpRequestData.maxResourceBuffer; - - #ifdef DEBUG_WEBS - Serial.print("Resrc length : ");Serial.println(safeLength); - #endif - - client->_httpRequestData.httpResource = (char *) malloc(sizeof(char) * (safeLength+1) ); //for \0 - if(client->_httpRequestData.httpResource != NULL) + uint16_t rawLengthOfResource(0); + if(pRsrcQuery) { - strncpy(client->_httpRequestData.httpResource, (char *)client->_data, safeLength); - client->_httpRequestData.httpResource[safeLength] = '\0'; + *pRsrcQuery = '\0'; // The ? is the end of the resource string + rawLengthOfResource = pRsrcQuery - (char *)client->_data; + #ifdef DEBUG_WEBS + Serial.printf("Resource w/ query\n"); + #endif + } + else + { + *pRsrc = '\0'; + rawLengthOfResource = pRsrc - (char *)client->_data; + #ifdef DEBUG_WEBS + Serial.printf("Resource w/o query\n"); + #endif + } + + if(rawLengthOfResource >= client->_httpRequestData.maxResourceBuffer) //Then we cannot handle the full resource and there is no point of truncating it + //better tell the client about it ... + { + #ifdef DEBUG_WEBS + Serial.printf("Resource too long\nResource raw length is : %u (\\0 included)\nMax length is : %u (\\0 included)\nclient->_data : #%s#\n", rawLengthOfResource + 1, client->_httpRequestData.maxResourceBuffer, (char *)client->_data); + #endif + sendInfoResponse(HTTP_CODE::HTTP_CODE_URI_TOO_LONG, client, "Resource too long"); + client->_clientState = TCPClient::ClientState::DISCARDED; + break; + } + + client->_httpRequestData.httpResource = (char *) malloc(sizeof(char) * (rawLengthOfResource + 1)); // +1 for the \0 + if(client->_httpRequestData.httpResource != nullptr) + { + strncpy(client->_httpRequestData.httpResource, (char *)client->_data, rawLengthOfResource); + client->_httpRequestData.httpResource[rawLengthOfResource] = '\0'; } else //Error 500 { @@ -172,39 +201,39 @@ class WEBServer : public TCPServer, public HttpConstants client->_clientState = TCPClient::ClientState::DISCARDED; break; } - - client->freeDataBuffer(safeLength + 1); - + + client->freeDataBuffer(rawLengthOfResource + 1); //+1 to go past the \0, only the query parameters left in the buffer '?' excluded + #ifdef DEBUG_WEBS - Serial.print("Resrc : ");Serial.println(client->_httpRequestData.httpResource); - Serial.println((char *)client->_data); + Serial.printf("Resource length : %u\nRsrc : %s\nclient->_data : #%s#\n", + rawLengthOfResource, + client->_httpRequestData.httpResource, + (char *)client->_data); #endif + + if(pRsrcQuery) + client->_httpParserState = HttpParserStatus::PARSE_HTTP_RESOURCE_QUERY; + else + client->_httpParserState = HttpParserStatus::PARSE_HTTP_VERSION; } - else //Resource is probably too long, so we truncate it + else //The URL is too long to fit in the buffer, we dont know it's length nor we now if it has query parameters. + //TODO : Maybe the query is incomplete and the client will send more data, case to handle. { - client->_httpRequestData.httpResource = (char *) malloc(sizeof(char) * (client->_httpRequestData.maxResourceBuffer+1) ); //for \0 - if(client->_httpRequestData.httpResource != NULL) - { - strncpy(client->_httpRequestData.httpResource, (char *)client->_data, client->_httpRequestData.maxResourceBuffer); - client->_httpRequestData.httpResource[client->_httpRequestData.maxResourceBuffer] = '\0'; - } - else //Error 500 - { - sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for resources"); - client->_clientState = TCPClient::ClientState::DISCARDED; - break; - } - - client->freeDataBuffer(client->_httpRequestData.maxResourceBuffer + 1); + #ifdef DEBUG_WEBS + Serial.printf("Could not find ' ' or '?' delimiter\nclient->_data : #%s#\n", + (char *)client->_data); + #endif + sendInfoResponse(HTTP_CODE::HTTP_CODE_URI_TOO_LONG, client, "Resource too long"); + client->_clientState = TCPClient::ClientState::DISCARDED; + break; } - client->_httpParserState = HttpParserStatus::HTTP_PARAMS; } break; - case HttpParserStatus::HTTP_VERSION: + case HttpParserStatus::PARSE_HTTP_VERSION: { char *pEndline = strstr((char *)client->_data, "\r\n"); - if(pEndline == NULL) pEndline = strstr((char *)client->_data, "\n"); + if(pEndline == NULL) pEndline = strchr((char *)client->_data, '\n'); char *pVers = strstr((char *)client->_data, "HTTP/"); @@ -224,25 +253,29 @@ class WEBServer : public TCPServer, public HttpConstants Serial.println((char *)client->_data); #endif - client->_httpParserState = HttpParserStatus::HEADER_PARAMS; + client->_httpParserState = HttpParserStatus::PARSE_HTTP_HEADER_PARAMS; } } break; - case HttpParserStatus::HTTP_PARAMS: //index.htm?var1=1&var2=2... + + //index.htm?var1=1&var2=2... + //----------^^^^^^^^^^^^^^ + case HttpParserStatus::PARSE_HTTP_RESOURCE_QUERY: + //If we are here, it means we are sure that there is at least one parameter if(!httpRsrcParamParser(client)) { #ifdef DEBUG_WEBS - Serial.print("Resrc : ");Serial.println(client->_httpRequestData.httpResource); Serial.println("Get params :"); - for(int i = 0; i < client->_httpRequestData.getParams.count(); i++) + for(unsigned int i = 0; i < client->_httpRequestData.getParams.count(); i++) { - Serial.print(client->_httpRequestData.getParams.getParameter(i));Serial.print(" : ");Serial.println(client->_httpRequestData.getParams.getAt(i)->getString()); + Serial.printf("%s : %s\n", client->_httpRequestData.getParams.getParameter(i), client->_httpRequestData.getParams.getAt(i)->getString()); } + Serial.printf("client->_data : #%s#\n", client->_data); #endif - client->_httpParserState = HttpParserStatus::HTTP_VERSION; + client->_httpParserState = HttpParserStatus::PARSE_HTTP_VERSION; } break; - case HttpParserStatus::HEADER_PARAMS: //Here we parse the different header params until we arrive to \r\n\r\n + case HttpParserStatus::PARSE_HTTP_HEADER_PARAMS: //Here we parse the different header params until we arrive to \r\n\r\n { char *pEndLine = strstr((char *)client->_data, "\r\n"); @@ -254,7 +287,7 @@ class WEBServer : public TCPServer, public HttpConstants if(*(pEndLine+2) == '\r') //We got \r\n\r\n -> so we go to the post data section { - client->_httpParserState = HttpParserStatus::POST_DATA; + client->_httpParserState = HttpParserStatus::PARSE_HTTP_POST_DATA; client->freeDataBuffer((pEndLine - (char *)client->_data) +3); //client->_data must not be empty... break; } @@ -265,11 +298,11 @@ class WEBServer : public TCPServer, public HttpConstants } else //Error : indeed, we should at least have : \r\n. We go to the next step anyway { - client->_httpParserState = HttpParserStatus::POST_DATA; + client->_httpParserState = HttpParserStatus::PARSE_HTTP_POST_DATA; } } break; - case HttpParserStatus::POST_DATA: + case HttpParserStatus::PARSE_HTTP_POST_DATA: switch(client->_httpRequestData.HMT) { @@ -391,65 +424,59 @@ class WEBServer : public TCPServer, public HttpConstants } /* - * This function is here to parse resources parameters + * This function is here to parse resources query parameters */ boolean httpRsrcParamParser(T *client) { - char *pGetParam = strchr((char *)client->_httpRequestData.httpResource, '?'); - - - if(pGetParam != NULL) //There are some params to be parsed - { - if(client->_httpRequestData.getParamsDataPointer == NULL) - { - client->_httpRequestData.getParamsDataPointer = pGetParam +1;//We save the starting position of the string to parse - } - - char *key = strchr(client->_httpRequestData.getParamsDataPointer, '='); - char *value = strchr(client->_httpRequestData.getParamsDataPointer, '&'); + char *end(strchr((char *)client->_data, ' ')); - if(key == NULL && value == NULL) //Only the key is present - { - client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(NULL)); - *pGetParam = '\0'; - return false; - } - else if(key != NULL && value != NULL) - { - if(key < value)*key = '\0'; - *value = '\0'; - - client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(key > value ? NULL : key + 1)); - memmove(client->_httpRequestData.getParamsDataPointer, value+1, strlen(value+1)+1); - - #ifdef DEBUG_WEBS - Serial.print("Params pointer : ");Serial.println(client->_httpRequestData.getParamsDataPointer); - #endif - } - else if(key != NULL && value == NULL) //Only one key/value pair present - { - *key = '\0'; - - client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(key+1)); - *pGetParam = '\0'; - return false; - } - else if(key == NULL && value != NULL) - { - *value = '\0'; + //If we find the end we mark it, this is needed for subsequent strchr + if(end)*end = '\0'; - client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(NULL)); - memmove(client->_httpRequestData.getParamsDataPointer, value+1, strlen(value+1)+1); - } - } - else //nothing to parse or done + char *key(strchr((char *)client->_data, '=')), *value(strchr((char *)client->_data, '&')); + + if(key == nullptr && value == nullptr) //Only the key is present { + client->_httpRequestData.getParams.add((char *)client->_data, new DictionaryHelper::StringEntity(NULL)); + client->freeDataBuffer(strlen((char *)client->_data) + 1); #ifdef DEBUG_WEBS - Serial.println("Nothing to parse or done"); + Serial.printf("client->_data : #%s#\n", client->_data); #endif return false; } + else if(key != nullptr && value != nullptr) + { + if(key < value)*key = '\0'; + *value = '\0'; + + client->_httpRequestData.getParams.add((char *)client->_data, new DictionaryHelper::StringEntity(key > value ? NULL : key + 1)); + client->freeDataBuffer((value - (char *)client->_data) + 1); + + #ifdef DEBUG_WEBS + Serial.printf("client->_data : #%s#\n", client->_data); + #endif + } + else if(key != nullptr && value == nullptr) //Only one key/value pair present + { + *key = '\0'; + + client->_httpRequestData.getParams.add((char *)client->_data, new DictionaryHelper::StringEntity(key+1)); + client->freeDataBuffer((key - (char *)client->_data) + strlen(key+1) + 2); + #ifdef DEBUG_WEBS + Serial.printf("client->_data : #%s#\n", client->_data); + #endif + return false; + } + else if(key == nullptr && value != nullptr) + { + *value = '\0'; + client->_httpRequestData.getParams.add((char *)client->_data, new DictionaryHelper::StringEntity(NULL)); + client->freeDataBuffer((value - (char *)client->_data) + 1); + #ifdef DEBUG_WEBS + Serial.printf("client->_data : #%s#\n", client->_data); + #endif + } return true; } @@ -674,15 +701,18 @@ class WEBServer : public TCPServer, public HttpConstants case HTTP_CODE_BAD_REQUEST: strcpy_P(codeLiteral,PSTR("Bad Request")); break; - case HTTP_CODE_NOT_FOUND: - strcpy_P(codeLiteral,PSTR("Not Found")); - break; case HTTP_CODE_FORBIDDEN: strcpy_P(codeLiteral,PSTR("Forbidden")); break; + case HTTP_CODE_NOT_FOUND: + strcpy_P(codeLiteral,PSTR("Not Found")); + break; case HTTP_CODE_METHOD_NOT_ALLOWED: strcpy_P(codeLiteral,PSTR("Method Not Allowed")); break; + case HTTP_CODE_URI_TOO_LONG: + strcpy_P(codeLiteral,PSTR("URI Too Long")); + break; case HTTP_CODE_INTERNAL_SERVER_ERROR: strcpy_P(codeLiteral,PSTR("Internal Server Error")); break; @@ -745,8 +775,7 @@ class WEBServer : public TCPServer, public HttpConstants static char *getFileExtension(char *name) { - char *p(lastIndexOf(name, '.')); - + char *p(strrchr(name, '.')); return p != NULL ? p+1 : NULL; }