From 944c11477b6fb1762527090d480621ca774e042c Mon Sep 17 00:00:00 2001 From: anschrammh Date: Thu, 16 Jul 2020 22:20:23 +0200 Subject: [PATCH] Updated the HttpClient class and the WEBServer class --- src/app/HttpClient.cpp | 106 +++++++++++++++++++++++++--------------- src/app/HttpClient.h | 20 +++++--- src/app/HttpConstants.h | 93 +++++++++++++++++++++++------------ src/app/WEBServer.h | 43 +++++++--------- 4 files changed, 160 insertions(+), 102 deletions(-) diff --git a/src/app/HttpClient.cpp b/src/app/HttpClient.cpp index 3ca82ac..0f074d3 100644 --- a/src/app/HttpClient.cpp +++ b/src/app/HttpClient.cpp @@ -1,20 +1,21 @@ #include "HttpClient.h" #include -#define DEBUG_HTTP_CLIENT +//#define DEBUG_HTTP_CLIENT /* * End of HttpClientHelper */ -HttpClient::HttpClient(const char *address, uint16_t port) : WiFiClient(), _connectionStatus(NO_ATTEMPT), _resource(NULL), _pAddress(address), _port(0), _keepAlive(false), _maxRetries(-1), _retries(0), _isIp(false), _httpCode(HTTP_CODE::UNDEFINED_CODE), _httpCodeParsed(false) +HttpClient::HttpClient(const char *address, uint16_t port, uint32_t timeout) : WiFiClient(), _connectionStatus(NO_ATTEMPT), _resource(NULL), _pAddress(address), _port(0), _keepAlive(false), _maxRetries(-1), _retries(0), _isIp(false), _httpCode(HTTP_CODE::UNDEFINED_CODE), _httpCodeParsed(false) { _port = port; + setTimeout(timeout); connectByHostOrIp(); } -HttpClient::HttpClient(const char *address, const char *resource, uint16_t port) : HttpClient(address, port) +HttpClient::HttpClient(const char *address, const char *resource, uint16_t port, uint32_t timeout) : HttpClient(address, port, timeout) { if(resource != NULL) { @@ -51,6 +52,9 @@ HttpClient::~HttpClient() boolean HttpClient::connectByHostOrIp() { IPAddress ipAddress; + #ifdef DEBUG_HTTP_CLIENT + Serial.printf("About to connect\n"); + #endif if(ipAddress.fromString(_pAddress)) { _isIp = true; @@ -71,7 +75,7 @@ boolean HttpClient::connectByHostOrIp() return _connectionStatus == SUCCESSFUL; } -boolean HttpClient::sendHttpQuery(const char *resource, HttpRequestMethod method, Dictionary *getData, Dictionary *postData) +boolean HttpClient::sendHttpQuery(const char *resource, HttpRequestMethod method, Dictionary *getData, Dictionary *postData, Dictionary *headerData) { if(resource != NULL) //We overwrite the resource if it has been already defined { @@ -83,10 +87,10 @@ boolean HttpClient::sendHttpQuery(const char *resource, HttpRequestMethod method } //We check the result - return sendHttpQuery(method, getData, postData); + return sendHttpQuery(method, getData, postData, headerData); } -boolean HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary *getData, Dictionary *postData) +boolean HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary *getData, Dictionary *postData, Dictionary *headerData) { //We reset this two flags _httpCode = HTTP_CODE::UNDEFINED_CODE; @@ -139,14 +143,14 @@ boolean HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary 99 ? 99 : bytesCount; + uint8_t bytesRed = buffer[peekBytes((uint8_t*)buffer,safeSize)] = '\0'; //We look for the end of the first line ie : HTTP/1.1 200 OK\r\n char *pNewLine = strstr(buffer, "\r\n"); //If we found the new line, we can retrieve the code @@ -207,15 +215,17 @@ HttpClient::HTTP_CODE HttpClient::isReplyAvailable(uint16_t timeout) #endif - _httpCode = numberToHTTP_CODE(strtoul(code+1, NULL, 10)); + _httpCode = (HTTP_CODE)strtoul(code+1, NULL, 10); //We can now discard the first line #ifdef DEBUG_HTTP_CLIENT Serial.printf("First line length : %u\n",(pNewLine - buffer) + 2); #endif read((uint8_t *)buffer, (pNewLine - buffer) + 2); #ifdef DEBUG_HTTP_CLIENT - buffer[peekBytes((uint8_t*)buffer,99)] = '\0'; - Serial.printf("Next chunk is : %s\n",buffer); + bytesCount = available(); + safeSize = bytesCount > 99 ? 99 : bytesCount; + buffer[peekBytes((uint8_t*)buffer,safeSize)] = '\0'; + Serial.printf("Next chunk is (size %u) : %s\n",safeSize,buffer); #endif } } @@ -238,39 +248,42 @@ HttpClient::HTTP_CODE HttpClient::isReplyAvailable(uint16_t timeout) } else//We found the HTTP code, now we discard all the header data { - char *bodyDelimiter(NULL); - char *newLineDelimiter; - do + int safeSize = bytesCount > 99 ? 99 : bytesCount; + buffer[peekBytes((uint8_t*)buffer,safeSize)] = '\0'; + #ifdef DEBUG_HTTP_CLIENT + Serial.printf("Peeked for (size %u) : %s\n",safeSize,buffer); + #endif + + bodyDelimiter = strstr(buffer, "\r\n\r\n"); + newLineDelimiter = strstr(buffer, "\r\n"); + if(!bodyDelimiter) { - buffer[peekBytes((uint8_t*)buffer,99)] = '\0'; - bodyDelimiter = strstr(buffer, "\r\n\r\n"); - newLineDelimiter = strstr(buffer, "\r\n"); - if(!bodyDelimiter) + if(newLineDelimiter) { - if(newLineDelimiter) - { - read((uint8_t *)buffer, (newLineDelimiter - buffer) + 2); - } - else - { - read((uint8_t *)buffer, strlen(buffer)); - } + read((uint8_t *)buffer, (newLineDelimiter - buffer) + 2); } else { - #ifdef DEBUG_HTTP_CLIENT - Serial.println("We found the body delimiter"); - #endif - read((uint8_t *)buffer, (bodyDelimiter - buffer) + 4); - #ifdef DEBUG_HTTP_CLIENT - buffer[peekBytes((uint8_t*)buffer,99)] = '\0'; - Serial.printf("Body chunk is : %s\n",buffer); - #endif + read((uint8_t *)buffer, strlen(buffer)); } - }while(!bodyDelimiter); - break; + } + else + { + #ifdef DEBUG_HTTP_CLIENT + Serial.println("We found the body delimiter"); + #endif + read((uint8_t *)buffer, (bodyDelimiter - buffer) + 4); + #ifdef DEBUG_HTTP_CLIENT + safeSize = available() > 99 ? 99 : available(); + buffer[peekBytes((uint8_t*)buffer,safeSize)] = '\0'; + Serial.printf("Body chunk is : %s\n",buffer); + #endif + break; + } } + ts = millis(); } + yield(); } #ifdef DEBUG_HTTP_CLIENT @@ -343,7 +356,7 @@ void HttpClient::keepAlive(boolean enabled) _keepAlive = enabled; } -void HttpClient::sendHeader(HttpMIMEType contentType, uint64_t contentLength, HttpVersion httpVersion) +void HttpClient::sendHeader(HttpMIMEType contentType, uint64_t contentLength, Dictionary *headerData, HttpVersion httpVersion) { char mime[255] = "", httpVer[15] = ""; //Host could be an IP address or a host name @@ -357,6 +370,19 @@ void HttpClient::sendHeader(HttpMIMEType contentType, uint64_t contentLength, Ht printf("Content-Length: %u\r\n", contentLength); printf("Content-Type: %s\r\n", httpMIMETypeToString(contentType, mime)); } + + //We send the headerData if not NULL + if(headerData != NULL) + { + uint8_t count(headerData->count()); + if(count > 0) + { + for(uint8_t i(0); i < count; i++) + { + printf("%s: %s\r\n", headerData->getParameter(i), headerData->getAt(i) != NULL ? headerData->getAt(i)->getString() : ""); + } + } + } printf("\r\n"); } diff --git a/src/app/HttpClient.h b/src/app/HttpClient.h index 85c1318..f4a7fa7 100644 --- a/src/app/HttpClient.h +++ b/src/app/HttpClient.h @@ -19,18 +19,26 @@ class HttpClient : public WiFiClient, public HttpConstants public: enum ConnectionStatus { NO_ATTEMPT = 0, SUCCESSFUL, FAILED }; - HttpClient(const char *address, uint16_t port = 80); - HttpClient(const char *address, const char *resource, uint16_t port = 80); + HttpClient(const char *address, uint16_t port = 80, uint32_t timeout = 5000); + HttpClient(const char *address, const char *resource, uint16_t port = 80, uint32_t timeout = 5000); HttpClient(const HttpClient &object); virtual ~HttpClient(); - boolean sendHttpQuery(const char *ressource, HttpRequestMethod method = HttpRequestMethod::GET, Dictionary *getData = NULL, Dictionary *postData = NULL); - boolean sendHttpQuery(HttpRequestMethod method = HttpRequestMethod::GET, Dictionary *getData = NULL, Dictionary *postData = NULL); + boolean sendHttpQuery(const char *ressource, + HttpRequestMethod method = HttpRequestMethod::GET, + Dictionary *getData = NULL, + Dictionary *postData = NULL, + Dictionary *headerData = NULL); + + boolean sendHttpQuery(HttpRequestMethod method = HttpRequestMethod::GET, + Dictionary *getData = NULL, + Dictionary *postData = NULL, + Dictionary *headerData = NULL); void keepAlive(boolean enabled); void setMaxRetries(int16_t retries); //100 ms is the default timeout - HTTP_CODE isReplyAvailable(uint16_t timeout = 100); + HTTP_CODE isReplyAvailable(uint16_t timeout = 10000); uint16_t readHttpBody(uint8_t *buffer, uint32_t size); protected: @@ -38,7 +46,7 @@ class HttpClient : public WiFiClient, public HttpConstants boolean connectByHostOrIp(); void sendUriWithGetParams(Dictionary *data); void sendPostData(Dictionary *data); - void sendHeader(HttpMIMEType contentType = HttpMIMEType::UNKNOWN_MIME, uint64_t contentLength = 0, HttpVersion httpVersion = HttpVersion::HTTP_1_1); + void sendHeader(HttpMIMEType contentType = HttpMIMEType::UNKNOWN_MIME, uint64_t contentLength = 0, Dictionary *headerData = NULL, HttpVersion httpVersion = HttpVersion::HTTP_1_1); uint64_t computeBodyLength(Dictionary *data); ConnectionStatus _connectionStatus; diff --git a/src/app/HttpConstants.h b/src/app/HttpConstants.h index a53bad9..37636d5 100644 --- a/src/app/HttpConstants.h +++ b/src/app/HttpConstants.h @@ -7,7 +7,67 @@ class HttpConstants enum HttpRequestMethod {UNDEFINED, GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH}; enum HttpVersion {UNKNOWN, HTTP_0_9, HTTP_1_1, HTTP_1_0, HTTP_2_0}; enum HttpMIMEType {UNKNOWN_MIME, TEXT_PLAIN, TEXT_CSS, TEXT_HTML, TEXT_JAVASCRIPT, APPLICATION_JSON, APPLICATION_X_WWW_FORM_URLENCODED, IMAGE_PNG, IMAGE_JPEG, AUDIO_MPEG, APPLICATION_OCTET_STREAM}; - enum HTTP_CODE {UNDEFINED_CODE, _100, _101, _200, _400, _401, _403, _404, _405, _500, _501}; + enum HTTP_CODE { + UNDEFINED_CODE = 0, + HTTP_CODE_CONTINUE = 100, + HTTP_CODE_SWITCHING_PROTOCOLS = 101, + HTTP_CODE_PROCESSING = 102, + HTTP_CODE_OK = 200, + HTTP_CODE_CREATED = 201, + HTTP_CODE_ACCEPTED = 202, + HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203, + HTTP_CODE_NO_CONTENT = 204, + HTTP_CODE_RESET_CONTENT = 205, + HTTP_CODE_PARTIAL_CONTENT = 206, + HTTP_CODE_MULTI_STATUS = 207, + HTTP_CODE_ALREADY_REPORTED = 208, + HTTP_CODE_IM_USED = 226, + HTTP_CODE_MULTIPLE_CHOICES = 300, + HTTP_CODE_MOVED_PERMANENTLY = 301, + HTTP_CODE_FOUND = 302, + HTTP_CODE_SEE_OTHER = 303, + HTTP_CODE_NOT_MODIFIED = 304, + HTTP_CODE_USE_PROXY = 305, + HTTP_CODE_TEMPORARY_REDIRECT = 307, + HTTP_CODE_PERMANENT_REDIRECT = 308, + HTTP_CODE_BAD_REQUEST = 400, + HTTP_CODE_UNAUTHORIZED = 401, + HTTP_CODE_PAYMENT_REQUIRED = 402, + HTTP_CODE_FORBIDDEN = 403, + HTTP_CODE_NOT_FOUND = 404, + HTTP_CODE_METHOD_NOT_ALLOWED = 405, + HTTP_CODE_NOT_ACCEPTABLE = 406, + HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407, + HTTP_CODE_REQUEST_TIMEOUT = 408, + HTTP_CODE_CONFLICT = 409, + HTTP_CODE_GONE = 410, + HTTP_CODE_LENGTH_REQUIRED = 411, + HTTP_CODE_PRECONDITION_FAILED = 412, + HTTP_CODE_PAYLOAD_TOO_LARGE = 413, + HTTP_CODE_URI_TOO_LONG = 414, + HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415, + HTTP_CODE_RANGE_NOT_SATISFIABLE = 416, + HTTP_CODE_EXPECTATION_FAILED = 417, + HTTP_CODE_MISDIRECTED_REQUEST = 421, + HTTP_CODE_UNPROCESSABLE_ENTITY = 422, + HTTP_CODE_LOCKED = 423, + HTTP_CODE_FAILED_DEPENDENCY = 424, + HTTP_CODE_UPGRADE_REQUIRED = 426, + HTTP_CODE_PRECONDITION_REQUIRED = 428, + HTTP_CODE_TOO_MANY_REQUESTS = 429, + HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HTTP_CODE_INTERNAL_SERVER_ERROR = 500, + HTTP_CODE_NOT_IMPLEMENTED = 501, + HTTP_CODE_BAD_GATEWAY = 502, + HTTP_CODE_SERVICE_UNAVAILABLE = 503, + HTTP_CODE_GATEWAY_TIMEOUT = 504, + HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505, + HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506, + HTTP_CODE_INSUFFICIENT_STORAGE = 507, + HTTP_CODE_LOOP_DETECTED = 508, + HTTP_CODE_NOT_EXTENDED = 510, + HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511 + }; static char *httpVersionToString(HttpVersion v, char *buffer) { @@ -57,36 +117,7 @@ class HttpConstants return buffer; } - - static HTTP_CODE numberToHTTP_CODE(uint32_t code) - { - switch(code) - { - case 100: - return HTTP_CODE::_100; - case 101: - return HTTP_CODE::_101; - case 200: - return HTTP_CODE::_200; - case 400: - return HTTP_CODE::_400; - case 401: - return HTTP_CODE::_401; - case 403: - return HTTP_CODE::_403; - case 404: - return HTTP_CODE::_404; - case 405: - return HTTP_CODE::_405; - case 500: - return HTTP_CODE::_500; - case 501: - return HTTP_CODE::_501; - default : - return HTTP_CODE::UNDEFINED_CODE; - } - } - + protected: private: }; diff --git a/src/app/WEBServer.h b/src/app/WEBServer.h index 3684325..d86af5d 100644 --- a/src/app/WEBServer.h +++ b/src/app/WEBServer.h @@ -126,7 +126,7 @@ class WEBServer : public TCPServer, public HttpConstants if(client->_httpRequestData.HRM == HttpRequestMethod::UNDEFINED) //Error 400 { - sendInfoResponse(HTTP_CODE::_400, client, "The server could not understand the request due to invalid syntax"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_BAD_REQUEST, client, "The server could not understand the request due to invalid syntax"); client->_clientState = TCPClient::ClientState::DISCARDED; break; } @@ -140,7 +140,7 @@ class WEBServer : public TCPServer, public HttpConstants } else { - sendInfoResponse(HTTP_CODE::_400, client, "The server could not understand the request due to invalid syntax"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_BAD_REQUEST, client, "The server could not understand the request due to invalid syntax"); client->_clientState = TCPClient::ClientState::DISCARDED; } } @@ -166,7 +166,7 @@ class WEBServer : public TCPServer, public HttpConstants } else //Error 500 { - sendInfoResponse(HTTP_CODE::_500, client, "Failed to allocate memory for resources"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for resources"); client->_clientState = TCPClient::ClientState::DISCARDED; break; } @@ -188,7 +188,7 @@ class WEBServer : public TCPServer, public HttpConstants } else //Error 500 { - sendInfoResponse(HTTP_CODE::_500, client, "Failed to allocate memory for resources"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for resources"); client->_clientState = TCPClient::ClientState::DISCARDED; break; } @@ -298,7 +298,7 @@ class WEBServer : public TCPServer, public HttpConstants break; default : - sendInfoResponse(HTTP_CODE::_500, client, "WEB server error"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "WEB server error"); client->_clientState = TCPClient::ClientState::DISCARDED; break; } @@ -483,7 +483,7 @@ class WEBServer : public TCPServer, public HttpConstants filePath = getFilePathByHttpResource(client->_httpRequestData.httpResource); if(filePath == NULL) { - sendInfoResponse(HTTP_CODE::_500, client, "Failed to allocate memory for the filePath"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the filePath"); return false; } @@ -503,12 +503,12 @@ class WEBServer : public TCPServer, public HttpConstants response = (char *) malloc(sizeof(char) * (strlen_P((PGM_P)F("Resource : not found on this server")) + strlen(client->_httpRequestData.httpResource) + 1)); if(response == NULL) { - sendInfoResponse(HTTP_CODE::_500, client, "Failed to allocate memory for the response"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the response"); return false; } sprintf(response, "Resource : %s not found on this server", client->_httpRequestData.httpResource); - sendInfoResponse(HTTP_CODE::_404, client, response); + sendInfoResponse(HTTP_CODE::HTTP_CODE_NOT_FOUND, client, response); free(response);response = NULL; return false; @@ -525,7 +525,7 @@ class WEBServer : public TCPServer, public HttpConstants { pageToSend.close(); - sendInfoResponse(HTTP_CODE::_403, client, "The file you want to access is a folder"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_FORBIDDEN, client, "The file you want to access is a folder"); return false; } @@ -545,7 +545,7 @@ class WEBServer : public TCPServer, public HttpConstants if(header == NULL) { - sendInfoResponse(HTTP_CODE::_500, client, "Failed to allocate memory for the header"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the header"); pageToSend.close(); return false; } @@ -584,13 +584,13 @@ class WEBServer : public TCPServer, public HttpConstants pageToSend.close(); break; default: //If not supported - sendInfoResponse(HTTP_CODE::_500, client, "The method used is not allowed"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_METHOD_NOT_ALLOWED, client, "The method used is not allowed"); return false; break; } }else { - sendInfoResponse(HTTP_CODE::_500, client, "Unable to access the SDCard"); + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Unable to access the SDCard"); return false; } @@ -601,36 +601,29 @@ class WEBServer : public TCPServer, public HttpConstants static void sendInfoResponse(HTTP_CODE http_code, T *client, const char *message) { - uint16_t code(0); char codeLiteral[100]; switch(http_code) { - case _400: - code = 400; + case HTTP_CODE_BAD_REQUEST: strcpy_P(codeLiteral,PSTR("Bad Request")); break; - case _404: - code = 404; + case HTTP_CODE_NOT_FOUND: strcpy_P(codeLiteral,PSTR("Not Found")); break; - case _403: - code = 403; + case HTTP_CODE_FORBIDDEN: strcpy_P(codeLiteral,PSTR("Forbidden")); break; - case _405: - code = 405; + case HTTP_CODE_METHOD_NOT_ALLOWED: strcpy_P(codeLiteral,PSTR("Method Not Allowed")); break; - case _500: - code = 500; + case HTTP_CODE_INTERNAL_SERVER_ERROR: strcpy_P(codeLiteral,PSTR("Internal Server Error")); break; default: - code = 000; strcpy_P(codeLiteral,PSTR("Error Not Defined")); break; } - client->_client.printf_P(PSTR("HTTP/1.1 %d %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n\r\n\r\n

Error %d

%s

\r\n"), code, codeLiteral, strlen(message) + 56 + (code != 0 ? 3:1), code , message); + client->_client.printf_P(PSTR("HTTP/1.1 %d %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n\r\n\r\n

Error %d

%s

\r\n"), http_code, codeLiteral, strlen(message) + 56 + (http_code != 0 ? 3:1), http_code , message); } static HttpRequestMethod getHttpVerbEnumValue(const char *parseBuffer)