From a19d6a2f4f17025cd519bb3826c6f0b71dc957a6 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 17 Aug 2024 12:14:21 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=9A=20Updates=20psychic=20version=20to?= =?UTF-8?q?=201.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esp32/lib/PsychicHttp/src/ChunkPrinter.cpp | 1 - esp32/lib/PsychicHttp/src/PsychicClient.cpp | 43 ++- esp32/lib/PsychicHttp/src/PsychicCore.h | 1 - .../PsychicHttp/src/PsychicEventSource.cpp | 2 - .../PsychicHttp/src/PsychicFileResponse.cpp | 13 +- esp32/lib/PsychicHttp/src/PsychicHandler.cpp | 10 +- esp32/lib/PsychicHttp/src/PsychicHandler.h | 7 +- esp32/lib/PsychicHttp/src/PsychicHttp.h | 12 +- .../lib/PsychicHttp/src/PsychicHttpServer.cpp | 12 +- esp32/lib/PsychicHttp/src/PsychicHttpServer.h | 2 +- .../PsychicHttp/src/PsychicHttpsServer.cpp | 17 +- .../lib/PsychicHttp/src/PsychicHttpsServer.h | 11 +- esp32/lib/PsychicHttp/src/PsychicJson.cpp | 112 ++++--- esp32/lib/PsychicHttp/src/PsychicRequest.cpp | 294 ++++++++---------- esp32/lib/PsychicHttp/src/PsychicRequest.h | 4 +- esp32/lib/PsychicHttp/src/PsychicResponse.cpp | 17 +- .../src/PsychicStaticFileHander.cpp | 12 - .../PsychicHttp/src/PsychicStreamResponse.cpp | 2 +- .../PsychicHttp/src/PsychicUploadHandler.cpp | 9 +- .../lib/PsychicHttp/src/PsychicWebHandler.cpp | 2 +- .../lib/PsychicHttp/src/PsychicWebSocket.cpp | 119 ++++--- esp32/lib/PsychicHttp/src/TemplatePrinter.cpp | 90 ++++++ esp32/lib/PsychicHttp/src/TemplatePrinter.h | 51 +++ esp32/lib/PsychicHttp/src/async_worker.cpp | 2 +- 24 files changed, 474 insertions(+), 371 deletions(-) create mode 100644 esp32/lib/PsychicHttp/src/TemplatePrinter.cpp create mode 100644 esp32/lib/PsychicHttp/src/TemplatePrinter.h diff --git a/esp32/lib/PsychicHttp/src/ChunkPrinter.cpp b/esp32/lib/PsychicHttp/src/ChunkPrinter.cpp index de98e61..b3f8713 100644 --- a/esp32/lib/PsychicHttp/src/ChunkPrinter.cpp +++ b/esp32/lib/PsychicHttp/src/ChunkPrinter.cpp @@ -34,7 +34,6 @@ size_t ChunkPrinter::write(uint8_t c) size_t ChunkPrinter::write(const uint8_t *buffer, size_t size) { - esp_err_t err; size_t written = 0; while (written < size) diff --git a/esp32/lib/PsychicHttp/src/PsychicClient.cpp b/esp32/lib/PsychicHttp/src/PsychicClient.cpp index 1ef7067..fd7820d 100644 --- a/esp32/lib/PsychicHttp/src/PsychicClient.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicClient.cpp @@ -2,24 +2,21 @@ #include "PsychicHttpServer.h" #include -PsychicClient::PsychicClient(httpd_handle_t server, int socket) : _server(server), - _socket(socket), - _friend(NULL), - isNew(false) -{ +PsychicClient::PsychicClient(httpd_handle_t server, int socket) : + _server(server), + _socket(socket), + _friend(NULL), + isNew(false) +{} + +PsychicClient::~PsychicClient() { } -PsychicClient::~PsychicClient() -{ -} - -httpd_handle_t PsychicClient::server() -{ +httpd_handle_t PsychicClient::server() { return _server; } -int PsychicClient::socket() -{ +int PsychicClient::socket() { return _socket; } @@ -27,28 +24,27 @@ int PsychicClient::socket() esp_err_t PsychicClient::close() { esp_err_t err = httpd_sess_trigger_close(_server, _socket); - // PsychicHttpServer::closeCallback(_server, _socket); // call this immediately so the client is taken off the list. + //PsychicHttpServer::closeCallback(_server, _socket); // call this immediately so the client is taken off the list. return err; } IPAddress PsychicClient::localIP() { - IPAddress address(0, 0, 0, 0); + IPAddress address(0,0,0,0); char ipstr[INET6_ADDRSTRLEN]; - struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing + struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing socklen_t addr_size = sizeof(addr); - if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) - { + if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) { ESP_LOGE(PH_TAG, "Error getting client IP"); return address; } // Convert to IPv4 string inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr)); - ESP_LOGI(PH_TAG, "Client Local IP => %s", ipstr); + //ESP_LOGD(PH_TAG, "Client Local IP => %s", ipstr); address.fromString(ipstr); return address; @@ -56,21 +52,20 @@ IPAddress PsychicClient::localIP() IPAddress PsychicClient::remoteIP() { - IPAddress address(0, 0, 0, 0); + IPAddress address(0,0,0,0); char ipstr[INET6_ADDRSTRLEN]; - struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing + struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing socklen_t addr_size = sizeof(addr); - if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) - { + if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) { ESP_LOGE(PH_TAG, "Error getting client IP"); return address; } // Convert to IPv4 string inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr)); - ESP_LOGV(PH_TAG, "Client Remote IP => %s", ipstr); + //ESP_LOGD(PH_TAG, "Client Remote IP => %s", ipstr); address.fromString(ipstr); return address; diff --git a/esp32/lib/PsychicHttp/src/PsychicCore.h b/esp32/lib/PsychicHttp/src/PsychicCore.h index 799d8ad..fe88b38 100644 --- a/esp32/lib/PsychicHttp/src/PsychicCore.h +++ b/esp32/lib/PsychicHttp/src/PsychicCore.h @@ -30,7 +30,6 @@ #ifdef ARDUINO #include - #include #endif #include diff --git a/esp32/lib/PsychicHttp/src/PsychicEventSource.cpp b/esp32/lib/PsychicHttp/src/PsychicEventSource.cpp index 044ebba..25f947d 100644 --- a/esp32/lib/PsychicHttp/src/PsychicEventSource.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicEventSource.cpp @@ -96,7 +96,6 @@ void PsychicEventSource::openCallback(PsychicClient *client) { PsychicEventSourceClient *buddy = getClient(client); if (buddy == NULL) { - TRACE(); return; } @@ -108,7 +107,6 @@ void PsychicEventSource::closeCallback(PsychicClient *client) { PsychicEventSourceClient *buddy = getClient(client); if (buddy == NULL) { - TRACE(); return; } diff --git a/esp32/lib/PsychicHttp/src/PsychicFileResponse.cpp b/esp32/lib/PsychicHttp/src/PsychicFileResponse.cpp index 9280234..5fc9822 100644 --- a/esp32/lib/PsychicHttp/src/PsychicFileResponse.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicFileResponse.cpp @@ -104,9 +104,16 @@ esp_err_t PsychicFileResponse::send() if (size < FILE_CHUNK_SIZE) { uint8_t *buffer = (uint8_t *)malloc(size); - int readSize = _content.readBytes((char *)buffer, size); + if (buffer == NULL) + { + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory."); + return ESP_FAIL; + } - this->setContent(buffer, size); + size_t readSize = _content.readBytes((char *)buffer, size); + + this->setContent(buffer, readSize); err = PsychicResponse::send(); free(buffer); @@ -143,7 +150,7 @@ esp_err_t PsychicFileResponse::send() if (err == ESP_OK) { - ESP_LOGI(PH_TAG, "File sending complete"); + ESP_LOGD(PH_TAG, "File sending complete"); this->finishChunking(); } } diff --git a/esp32/lib/PsychicHttp/src/PsychicHandler.cpp b/esp32/lib/PsychicHttp/src/PsychicHandler.cpp index f02971f..097b30e 100644 --- a/esp32/lib/PsychicHttp/src/PsychicHandler.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicHandler.cpp @@ -7,7 +7,8 @@ PsychicHandler::PsychicHandler() : _password(""), _method(DIGEST_AUTH), _realm(""), - _authFailMsg("") + _authFailMsg(""), + _subprotocol("") {} PsychicHandler::~PsychicHandler() { @@ -26,6 +27,13 @@ bool PsychicHandler::filter(PsychicRequest *request){ return _filter == NULL || _filter(request); } +void PsychicHandler::setSubprotocol(const String& subprotocol) { + this->_subprotocol = subprotocol; +} +const char* PsychicHandler::getSubprotocol() const { + return _subprotocol.c_str(); +} + PsychicHandler* PsychicHandler::setAuthentication(const char *username, const char *password, HTTPAuthMethod method, const char *realm, const char *authFailMsg) { _username = String(username); _password = String(password); diff --git a/esp32/lib/PsychicHttp/src/PsychicHandler.h b/esp32/lib/PsychicHttp/src/PsychicHandler.h index 4d832f9..bad62bf 100644 --- a/esp32/lib/PsychicHttp/src/PsychicHandler.h +++ b/esp32/lib/PsychicHttp/src/PsychicHandler.h @@ -24,11 +24,13 @@ class PsychicHandler { String _realm; String _authFailMsg; + String _subprotocol; + std::list _clients; public: PsychicHandler(); - ~PsychicHandler(); + virtual ~PsychicHandler(); PsychicHandler* setFilter(PsychicRequestFilterFunction fn); bool filter(PsychicRequest *request); @@ -39,6 +41,9 @@ class PsychicHandler { virtual bool isWebSocket() { return false; }; + void setSubprotocol(const String& subprotocol); + const char* getSubprotocol() const; + PsychicClient * checkForNewClient(PsychicClient *client); void checkForClosedClient(PsychicClient *client); diff --git a/esp32/lib/PsychicHttp/src/PsychicHttp.h b/esp32/lib/PsychicHttp/src/PsychicHttp.h index efe7663..3e9da55 100644 --- a/esp32/lib/PsychicHttp/src/PsychicHttp.h +++ b/esp32/lib/PsychicHttp/src/PsychicHttp.h @@ -3,19 +3,19 @@ //#define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread -#include "PsychicEndpoint.h" -#include "PsychicEventSource.h" -#include "PsychicFileResponse.h" -#include "PsychicHandler.h" +#include #include "PsychicHttpServer.h" -#include "PsychicJson.h" #include "PsychicRequest.h" #include "PsychicResponse.h" +#include "PsychicEndpoint.h" +#include "PsychicHandler.h" #include "PsychicStaticFileHandler.h" +#include "PsychicFileResponse.h" #include "PsychicStreamResponse.h" #include "PsychicUploadHandler.h" #include "PsychicWebSocket.h" -#include +#include "PsychicEventSource.h" +#include "PsychicJson.h" #ifdef ENABLE_ASYNC #include "async_worker.h" diff --git a/esp32/lib/PsychicHttp/src/PsychicHttpServer.cpp b/esp32/lib/PsychicHttp/src/PsychicHttpServer.cpp index e48a73b..628f38b 100644 --- a/esp32/lib/PsychicHttp/src/PsychicHttpServer.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicHttpServer.cpp @@ -56,8 +56,7 @@ PsychicHttpServer::~PsychicHttpServer() void PsychicHttpServer::destroy(void *ctx) { - PsychicHttpServer *temp = (PsychicHttpServer *)ctx; - delete temp; + // do not release any resource for PsychicHttpServer in order to be able to restart it after stopping } esp_err_t PsychicHttpServer::listen(uint16_t port) @@ -141,7 +140,8 @@ PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, Psyc .method = method, .handler = PsychicEndpoint::requestCallback, .user_ctx = endpoint, - .is_websocket = handler->isWebSocket() + .is_websocket = handler->isWebSocket(), + .supported_subprotocol = handler->getSubprotocol() }; // Register endpoint with ESP-IDF server @@ -186,7 +186,7 @@ PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, Psyc void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn) { PsychicWebHandler *handler = new PsychicWebHandler(); - handler->onRequest(fn); + handler->onRequest(fn == nullptr ? PsychicHttpServer::defaultNotFoundHandler : fn); this->defaultEndpoint->setHandler(handler); } @@ -232,7 +232,7 @@ void PsychicHttpServer::onOpen(PsychicClientCallback handler) { esp_err_t PsychicHttpServer::openCallback(httpd_handle_t hd, int sockfd) { - ESP_LOGI(PH_TAG, "New client connected %d", sockfd); + ESP_LOGD(PH_TAG, "New client connected %d", sockfd); //get our global server reference PsychicHttpServer *server = (PsychicHttpServer*)httpd_get_global_user_ctx(hd); @@ -258,7 +258,7 @@ void PsychicHttpServer::onClose(PsychicClientCallback handler) { void PsychicHttpServer::closeCallback(httpd_handle_t hd, int sockfd) { - ESP_LOGI(PH_TAG, "Client disconnected %d", sockfd); + ESP_LOGD(PH_TAG, "Client disconnected %d", sockfd); PsychicHttpServer *server = (PsychicHttpServer*)httpd_get_global_user_ctx(hd); diff --git a/esp32/lib/PsychicHttp/src/PsychicHttpServer.h b/esp32/lib/PsychicHttp/src/PsychicHttpServer.h index e572c1b..40e5442 100644 --- a/esp32/lib/PsychicHttp/src/PsychicHttpServer.h +++ b/esp32/lib/PsychicHttp/src/PsychicHttpServer.h @@ -25,7 +25,7 @@ class PsychicHttpServer public: PsychicHttpServer(); - ~PsychicHttpServer(); + virtual ~PsychicHttpServer(); //esp-idf specific stuff httpd_handle_t server; diff --git a/esp32/lib/PsychicHttp/src/PsychicHttpsServer.cpp b/esp32/lib/PsychicHttp/src/PsychicHttpsServer.cpp index e62dd4e..5df225e 100644 --- a/esp32/lib/PsychicHttp/src/PsychicHttpsServer.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicHttpsServer.cpp @@ -1,5 +1,7 @@ #include "PsychicHttpsServer.h" +#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE + PsychicHttpsServer::PsychicHttpsServer() : PsychicHttpServer() { //for a SSL server @@ -25,8 +27,15 @@ esp_err_t PsychicHttpsServer::listen(uint16_t port, const char *cert, const char this->_use_ssl = true; this->ssl_config.port_secure = port; - this->ssl_config.cacert_pem = (uint8_t *)cert; - this->ssl_config.cacert_len = strlen(cert)+1; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 2) + this->ssl_config.servercert = (uint8_t *)cert; + this->ssl_config.servercert_len = strlen(cert)+1; +#else + this->ssl_config.cacert_pem = (uint8_t *)cert; + this->ssl_config.cacert_len = strlen(cert)+1; +#endif + this->ssl_config.prvtkey_pem = (uint8_t *)private_key; this->ssl_config.prvtkey_len = strlen(private_key)+1; @@ -47,4 +56,6 @@ void PsychicHttpsServer::stop() httpd_ssl_stop(this->server); else httpd_stop(this->server); -} \ No newline at end of file +} + +#endif // CONFIG_ESP_HTTPS_SERVER_ENABLE \ No newline at end of file diff --git a/esp32/lib/PsychicHttp/src/PsychicHttpsServer.h b/esp32/lib/PsychicHttp/src/PsychicHttpsServer.h index f61043d..e6542c6 100644 --- a/esp32/lib/PsychicHttp/src/PsychicHttpsServer.h +++ b/esp32/lib/PsychicHttp/src/PsychicHttpsServer.h @@ -1,10 +1,13 @@ #ifndef PsychicHttpsServer_h #define PsychicHttpsServer_h +#include + +#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE + #include "PsychicCore.h" #include "PsychicHttpServer.h" #include - #if !CONFIG_HTTPD_WS_SUPPORT #error PsychicHttpsServer cannot be used unless HTTPD_WS_SUPPORT is enabled in esp-http-server component configuration #endif @@ -29,4 +32,8 @@ class PsychicHttpsServer : public PsychicHttpServer virtual void stop() override final; }; -#endif // PsychicHttpsServer_h \ No newline at end of file +#endif // PsychicHttpsServer_h + +#else + #error ESP-IDF https server support not enabled. +#endif // CONFIG_ESP_HTTPS_SERVER_ENABLE \ No newline at end of file diff --git a/esp32/lib/PsychicHttp/src/PsychicJson.cpp b/esp32/lib/PsychicHttp/src/PsychicJson.cpp index 09cbba1..ca0f0a5 100644 --- a/esp32/lib/PsychicHttp/src/PsychicJson.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicJson.cpp @@ -1,24 +1,25 @@ #include "PsychicJson.h" #ifdef ARDUINOJSON_6_COMPATIBILITY -PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray, size_t maxJsonBufferSize) : PsychicResponse(request), - _jsonBuffer(maxJsonBufferSize) -{ - setContentType(JSON_MIMETYPE); - if (isArray) - _root = _jsonBuffer.createNestedArray(); - else - _root = _jsonBuffer.createNestedObject(); -} + PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray, size_t maxJsonBufferSize) : + PsychicResponse(request), + _jsonBuffer(maxJsonBufferSize) + { + setContentType(JSON_MIMETYPE); + if (isArray) + _root = _jsonBuffer.createNestedArray(); + else + _root = _jsonBuffer.createNestedObject(); + } #else -PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray) : PsychicResponse(request) -{ - setContentType(JSON_MIMETYPE); - if (isArray) - _root = _jsonBuffer.add(); - else - _root = _jsonBuffer.add(); -} + PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray) : PsychicResponse(request) + { + setContentType(JSON_MIMETYPE); + if (isArray) + _root = _jsonBuffer.add(); + else + _root = _jsonBuffer.add(); + } #endif JsonVariant &PsychicJsonResponse::getRoot() { return _root; } @@ -35,28 +36,21 @@ esp_err_t PsychicJsonResponse::send() size_t buffer_size; char *buffer; - // DUMP(length); - - // how big of a buffer do we want? + //how big of a buffer do we want? if (length < JSON_BUFFER_SIZE) - buffer_size = length + 1; + buffer_size = length+1; else buffer_size = JSON_BUFFER_SIZE; - // DUMP(buffer_size); - buffer = (char *)malloc(buffer_size); - if (buffer == NULL) - { + if (buffer == NULL) { httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory."); return ESP_FAIL; } - // send it in one shot or no? + //send it in one shot or no? if (length < JSON_BUFFER_SIZE) { - // TRACE(); - serializeJson(_root, buffer, buffer_size); this->setContent((uint8_t *)buffer, length); @@ -66,67 +60,71 @@ esp_err_t PsychicJsonResponse::send() } else { - // helper class that acts as a stream to print chunked responses + //helper class that acts as a stream to print chunked responses ChunkPrinter dest(this, (uint8_t *)buffer, buffer_size); - // keep our headers + //keep our headers this->sendHeaders(); serializeJson(_root, dest); - // send the last bits + //send the last bits dest.flush(); - // done with our chunked response too + //done with our chunked response too err = this->finishChunking(); } - // let the buffer go + //let the buffer go free(buffer); return err; } #ifdef ARDUINOJSON_6_COMPATIBILITY -PsychicJsonHandler::PsychicJsonHandler(size_t maxJsonBufferSize) : _onRequest(NULL), - _maxJsonBufferSize(maxJsonBufferSize){}; + PsychicJsonHandler::PsychicJsonHandler(size_t maxJsonBufferSize) : + _onRequest(NULL), + _maxJsonBufferSize(maxJsonBufferSize) + {}; -PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest, size_t maxJsonBufferSize) : _onRequest(onRequest), - _maxJsonBufferSize(maxJsonBufferSize) -{ -} + PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest, size_t maxJsonBufferSize) : + _onRequest(onRequest), + _maxJsonBufferSize(maxJsonBufferSize) + {} #else -PsychicJsonHandler::PsychicJsonHandler() : _onRequest(NULL){}; + PsychicJsonHandler::PsychicJsonHandler() : + _onRequest(NULL) + {}; -PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest) : _onRequest(onRequest) -{ -} + PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest) : + _onRequest(onRequest) + {} #endif void PsychicJsonHandler::onRequest(PsychicJsonRequestCallback fn) { _onRequest = fn; } esp_err_t PsychicJsonHandler::handleRequest(PsychicRequest *request) { - // process basic stuff + //process basic stuff PsychicWebHandler::handleRequest(request); if (_onRequest) { -#ifdef ARDUINOJSON_6_COMPATIBILITY - DynamicJsonDocument jsonBuffer(this->_maxJsonBufferSize); - DeserializationError error = deserializeJson(jsonBuffer, request->body()); - if (error) - return request->reply(400); + #ifdef ARDUINOJSON_6_COMPATIBILITY + DynamicJsonDocument jsonBuffer(this->_maxJsonBufferSize); + DeserializationError error = deserializeJson(jsonBuffer, request->body()); + if (error) + return request->reply(400); - JsonVariant json = jsonBuffer.as(); -#else - JsonDocument jsonBuffer; - DeserializationError error = deserializeJson(jsonBuffer, request->body()); - if (error) - return request->reply(400); + JsonVariant json = jsonBuffer.as(); + #else + JsonDocument jsonBuffer; + DeserializationError error = deserializeJson(jsonBuffer, request->body()); + if (error) + return request->reply(400); - JsonVariant json = jsonBuffer.as(); -#endif + JsonVariant json = jsonBuffer.as(); + #endif return _onRequest(request, json); } diff --git a/esp32/lib/PsychicHttp/src/PsychicRequest.cpp b/esp32/lib/PsychicHttp/src/PsychicRequest.cpp index 3aaeaca..4244358 100644 --- a/esp32/lib/PsychicHttp/src/PsychicRequest.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicRequest.cpp @@ -2,17 +2,19 @@ #include "http_status.h" #include "PsychicHttpServer.h" -PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) : _server(server), - _req(req), - _method(HTTP_GET), - _query(""), - _body(""), - _tempObject(NULL) + +PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) : + _server(server), + _req(req), + _method(HTTP_GET), + _query(""), + _body(""), + _tempObject(NULL) { - // load up our client. + //load up our client. this->_client = server->getClient(req); - // handle our session data + //handle our session data if (req->sess_ctx != NULL) this->_session = (SessionData *)req->sess_ctx; else @@ -21,22 +23,22 @@ PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) : _s req->sess_ctx = this->_session; } - // callback for freeing the session later + //callback for freeing the session later req->free_ctx = this->freeSession; - // load up some data + //load up some data this->_uri = String(this->_req->uri); } PsychicRequest::~PsychicRequest() { - // temorary user object + //temorary user object if (_tempObject != NULL) free(_tempObject); - // our web parameters + //our web parameters for (auto *param : _params) - delete (param); + delete(param); _params.clear(); } @@ -44,29 +46,26 @@ void PsychicRequest::freeSession(void *ctx) { if (ctx != NULL) { - SessionData *session = (SessionData *)ctx; + SessionData *session = (SessionData*)ctx; delete session; } } -PsychicHttpServer *PsychicRequest::server() -{ +PsychicHttpServer * PsychicRequest::server() { return _server; } -httpd_req_t *PsychicRequest::request() -{ +httpd_req_t * PsychicRequest::request() { return _req; } -PsychicClient *PsychicRequest::client() -{ +PsychicClient * PsychicRequest::client() { return _client; } const String PsychicRequest::getFilename() { - // parse the content-disposition header + //parse the content-disposition header if (this->hasHeader("Content-Disposition")) { ContentDisposition cd = this->getContentDisposition(); @@ -74,19 +73,19 @@ const String PsychicRequest::getFilename() return cd.filename; } - // fall back to passed in query string + //fall back to passed in query string PsychicWebParameter *param = getParam("_filename"); if (param != NULL) return param->name(); - // fall back to parsing it from url (useful for wildcard uploads) + //fall back to parsing it from url (useful for wildcard uploads) String uri = this->uri(); int filenameStart = uri.lastIndexOf('/') + 1; String filename = uri.substring(filenameStart); if (filename != "") return filename; - // finally, unknown. + //finally, unknown. ESP_LOGE(PH_TAG, "Did not get a valid filename from the upload."); return "unknown.txt"; } @@ -104,21 +103,21 @@ const ContentDisposition PsychicRequest::getContentDisposition() cd.disposition = ATTACHMENT; else if (header.indexOf("inline") == 0) cd.disposition = INLINE; - else + else cd.disposition = NONE; start = header.indexOf("filename="); if (start) { - end = header.indexOf('"', start + 10); - cd.filename = header.substring(start + 10, end - 1); + end = header.indexOf('"', start+10); + cd.filename = header.substring(start+10, end-1); } start = header.indexOf("name="); if (start) { - end = header.indexOf('"', start + 6); - cd.name = header.substring(start + 6, end - 1); + end = header.indexOf('"', start+6); + cd.name = header.substring(start+6, end-1); } return cd; @@ -133,22 +132,18 @@ esp_err_t PsychicRequest::loadBody() size_t remaining = this->_req->content_len; size_t actuallyReceived = 0; char *buf = (char *)malloc(remaining + 1); - if (buf == NULL) - { + if (buf == NULL) { ESP_LOGE(PH_TAG, "Failed to allocate memory for body"); return ESP_FAIL; } - while (remaining > 0) - { + while (remaining > 0) { int received = httpd_req_recv(this->_req, buf + actuallyReceived, remaining); - if (received == HTTPD_SOCK_ERR_TIMEOUT) - { + if (received == HTTPD_SOCK_ERR_TIMEOUT) { continue; } - else if (received == HTTPD_SOCK_ERR_FAIL) - { + else if (received == HTTPD_SOCK_ERR_FAIL) { ESP_LOGE(PH_TAG, "Failed to receive data."); err = ESP_FAIL; break; @@ -164,32 +159,27 @@ esp_err_t PsychicRequest::loadBody() return err; } -http_method PsychicRequest::method() -{ +http_method PsychicRequest::method() { return (http_method)this->_req->method; } -const String PsychicRequest::methodStr() -{ +const String PsychicRequest::methodStr() { return String(http_method_str((http_method)this->_req->method)); } -const String PsychicRequest::path() -{ +const String PsychicRequest::path() { int index = _uri.indexOf("?"); - if (index == -1) + if(index == -1) return _uri; else - return _uri.substring(0, index); + return _uri.substring(0, index); } -const String &PsychicRequest::uri() -{ +const String& PsychicRequest::uri() { return this->_uri; } -const String &PsychicRequest::query() -{ +const String& PsychicRequest::query() { return this->_query; } @@ -202,10 +192,10 @@ const String PsychicRequest::header(const char *name) { size_t header_len = httpd_req_get_hdr_value_len(this->_req, name); - // if we've got one, allocated it and load it + //if we've got one, allocated it and load it if (header_len) { - char header[header_len + 1]; + char header[header_len+1]; httpd_req_get_hdr_value_str(this->_req, name, header, sizeof(header)); return String(header); } @@ -218,29 +208,26 @@ bool PsychicRequest::hasHeader(const char *name) return httpd_req_get_hdr_value_len(this->_req, name) > 0; } -const String PsychicRequest::host() -{ +const String PsychicRequest::host() { return this->header("Host"); } -const String PsychicRequest::contentType() -{ +const String PsychicRequest::contentType() { return header("Content-Type"); } -size_t PsychicRequest::contentLength() -{ +size_t PsychicRequest::contentLength() { return this->_req->content_len; } -const String &PsychicRequest::body() +const String& PsychicRequest::body() { return this->_body; } bool PsychicRequest::isMultipart() { - const String &type = this->contentType(); + const String& type = this->contentType(); return (this->contentType().indexOf("multipart/form-data") >= 0); } @@ -260,7 +247,7 @@ bool PsychicRequest::hasCookie(const char *key) size_t cookieSize = MAX_COOKIE_SIZE; esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize); - // did we get anything? + //did we get anything? if (err == ESP_OK) return true; else if (err == ESP_ERR_HTTPD_RESULT_TRUNC) @@ -275,7 +262,7 @@ const String PsychicRequest::getCookie(const char *key) size_t cookieSize = MAX_COOKIE_SIZE; esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize); - // did we get anything? + //did we get anything? if (err == ESP_OK) return String(cookie); else @@ -284,54 +271,49 @@ const String PsychicRequest::getCookie(const char *key) void PsychicRequest::loadParams() { - // did we get a query string? + //did we get a query string? size_t query_len = httpd_req_get_url_query_len(_req); if (query_len) { - char query[query_len + 1]; + char query[query_len+1]; httpd_req_get_url_query_str(_req, query, sizeof(query)); - _query = ""; _query.concat(query); - // parse them. - _addParams(_query); + //parse them. + _addParams(_query, false); } - // did we get form data as body? - if (this->method() == HTTP_POST && this->contentType() == "application/x-www-form-urlencoded") + //did we get form data as body? + if (this->method() == HTTP_POST && this->contentType().startsWith("application/x-www-form-urlencoded")) { - _addParams(_body); + _addParams(_body, true); } } -void PsychicRequest::_addParams(const String ¶ms) -{ +void PsychicRequest::_addParams(const String& params, bool post){ size_t start = 0; - while (start < params.length()) - { + while (start < params.length()){ int end = params.indexOf('&', start); - if (end < 0) - end = params.length(); + if (end < 0) end = params.length(); int equal = params.indexOf('=', start); - if (equal < 0 || equal > end) - equal = end; + if (equal < 0 || equal > end) equal = end; String name = params.substring(start, equal); String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); - addParam(name, value); + addParam(name, value, true, post); start = end + 1; } } -PsychicWebParameter *PsychicRequest::addParam(const String &name, const String &value, bool decode) +PsychicWebParameter * PsychicRequest::addParam(const String &name, const String &value, bool decode, bool post) { if (decode) - return addParam(new PsychicWebParameter(urlDecode(name.c_str()), urlDecode(value.c_str()))); + return addParam(new PsychicWebParameter(urlDecode(name.c_str()), urlDecode(value.c_str()), post)); else - return addParam(new PsychicWebParameter(name, value)); + return addParam(new PsychicWebParameter(name, value, post)); } -PsychicWebParameter *PsychicRequest::addParam(PsychicWebParameter *param) -{ +PsychicWebParameter * PsychicRequest::addParam(PsychicWebParameter *param) { + // ESP_LOGD(PH_TAG, "Adding param: '%s' = '%s'", param->name().c_str(), param->value().c_str()); _params.push_back(param); return param; } @@ -341,7 +323,7 @@ bool PsychicRequest::hasParam(const char *key) return getParam(key) != NULL; } -PsychicWebParameter *PsychicRequest::getParam(const char *key) +PsychicWebParameter * PsychicRequest::getParam(const char *key) { for (auto *param : _params) if (param->name().equals(key)) @@ -350,12 +332,12 @@ PsychicWebParameter *PsychicRequest::getParam(const char *key) return NULL; } -bool PsychicRequest::hasSessionKey(const String &key) +bool PsychicRequest::hasSessionKey(const String& key) { return this->_session->find(key) != this->_session->end(); } -const String PsychicRequest::getSessionKey(const String &key) +const String PsychicRequest::getSessionKey(const String& key) { auto it = this->_session->find(key); if (it != this->_session->end()) @@ -364,13 +346,12 @@ const String PsychicRequest::getSessionKey(const String &key) return ""; } -void PsychicRequest::setSessionKey(const String &key, const String &value) +void PsychicRequest::setSessionKey(const String& key, const String& value) { this->_session->insert(std::pair(key, value)); } -static const String md5str(const String &in) -{ +static const String md5str(const String &in){ MD5Builder md5 = MD5Builder(); md5.begin(); md5.add(in); @@ -378,32 +359,28 @@ static const String md5str(const String &in) return md5.toString(); } -bool PsychicRequest::authenticate(const char *username, const char *password) +bool PsychicRequest::authenticate(const char * username, const char * password) { - if (hasHeader("Authorization")) + if(hasHeader("Authorization")) { String authReq = header("Authorization"); - if (authReq.startsWith("Basic")) - { + if(authReq.startsWith("Basic")){ authReq = authReq.substring(6); authReq.trim(); - char toencodeLen = strlen(username) + strlen(password) + 1; + char toencodeLen = strlen(username)+strlen(password)+1; char *toencode = new char[toencodeLen + 1]; - if (toencode == NULL) - { + if(toencode == NULL){ authReq = ""; return false; } - char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; - if (encoded == NULL) - { + char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; + if(encoded == NULL){ authReq = ""; delete[] toencode; return false; } sprintf(toencode, "%s:%s", username, password); - if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) - { + if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) { authReq = ""; delete[] toencode; delete[] encoded; @@ -412,81 +389,63 @@ bool PsychicRequest::authenticate(const char *username, const char *password) delete[] toencode; delete[] encoded; } - else if (authReq.startsWith(F("Digest"))) + else if(authReq.startsWith(F("Digest"))) { authReq = authReq.substring(7); - String _username = _extractParam(authReq, F("username=\""), '\"'); - if (!_username.length() || _username != String(username)) - { + String _username = _extractParam(authReq,F("username=\""),'\"'); + if(!_username.length() || _username != String(username)) { authReq = ""; return false; } // extracting required parameters for RFC 2069 simpler Digest - String _realm = _extractParam(authReq, F("realm=\""), '\"'); - String _nonce = _extractParam(authReq, F("nonce=\""), '\"'); - String _uri = _extractParam(authReq, F("uri=\""), '\"'); - String _resp = _extractParam(authReq, F("response=\""), '\"'); - String _opaque = _extractParam(authReq, F("opaque=\""), '\"'); + String _realm = _extractParam(authReq, F("realm=\""),'\"'); + String _nonce = _extractParam(authReq, F("nonce=\""),'\"'); + String _uri = _extractParam(authReq, F("uri=\""),'\"'); + String _resp = _extractParam(authReq, F("response=\""),'\"'); + String _opaque = _extractParam(authReq, F("opaque=\""),'\"'); - if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_resp.length()) || (!_opaque.length())) - { + if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_resp.length()) || (!_opaque.length())) { authReq = ""; return false; } - if ((_opaque != this->getSessionKey("opaque")) || (_nonce != this->getSessionKey("nonce")) || (_realm != this->getSessionKey("realm"))) + if((_opaque != this->getSessionKey("opaque")) || (_nonce != this->getSessionKey("nonce")) || (_realm != this->getSessionKey("realm"))) { - // DUMP(_opaque); - // DUMP(this->getSessionKey("opaque")); - // DUMP(_nonce); - // DUMP(this->getSessionKey("nonce")); - // DUMP(_realm); - // DUMP(this->getSessionKey("realm")); authReq = ""; return false; } // parameters for the RFC 2617 newer Digest - String _nc, _cnonce; - if (authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) - { + String _nc,_cnonce; + if(authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) { _nc = _extractParam(authReq, F("nc="), ','); - _cnonce = _extractParam(authReq, F("cnonce=\""), '\"'); + _cnonce = _extractParam(authReq, F("cnonce=\""),'\"'); } + String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password)); - ESP_LOGD(PH_TAG, "Hash of user:realm:pass=%s", _H1.c_str()); + //ESP_LOGD(PH_TAG, "Hash of user:realm:pass=%s", _H1.c_str()); + String _H2 = ""; - if (_method == HTTP_GET) - { - _H2 = md5str(String(F("GET:")) + _uri); + if(_method == HTTP_GET){ + _H2 = md5str(String(F("GET:")) + _uri); + }else if(_method == HTTP_POST){ + _H2 = md5str(String(F("POST:")) + _uri); + }else if(_method == HTTP_PUT){ + _H2 = md5str(String(F("PUT:")) + _uri); + }else if(_method == HTTP_DELETE){ + _H2 = md5str(String(F("DELETE:")) + _uri); + }else{ + _H2 = md5str(String(F("GET:")) + _uri); } - else if (_method == HTTP_POST) - { - _H2 = md5str(String(F("POST:")) + _uri); - } - else if (_method == HTTP_PUT) - { - _H2 = md5str(String(F("PUT:")) + _uri); - } - else if (_method == HTTP_DELETE) - { - _H2 = md5str(String(F("DELETE:")) + _uri); - } - else - { - _H2 = md5str(String(F("GET:")) + _uri); - } - ESP_LOGD(PH_TAG, "Hash of GET:uri=%s", _H2.c_str()); + //ESP_LOGD(PH_TAG, "Hash of GET:uri=%s", _H2.c_str()); + String _responsecheck = ""; - if (authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) - { - _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); + if(authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) { + _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); + } else { + _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2); } - else - { - _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2); - } - ESP_LOGD(PH_TAG, "The Proper response=%s", _responsecheck.c_str()); - if (_resp == _responsecheck) - { + + //ESP_LOGD(PH_TAG, "The Proper response=%s", _responsecheck.c_str()); + if(_resp == _responsecheck){ authReq = ""; return true; } @@ -496,29 +455,28 @@ bool PsychicRequest::authenticate(const char *username, const char *password) return false; } -const String PsychicRequest::_extractParam(const String &authReq, const String ¶m, const char delimit) +const String PsychicRequest::_extractParam(const String& authReq, const String& param, const char delimit) { int _begin = authReq.indexOf(param); if (_begin == -1) return ""; - return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length())); + return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length())); } const String PsychicRequest::_getRandomHexString() { - char buffer[33]; // buffer to hold 32 Hex Digit + /0 + char buffer[33]; // buffer to hold 32 Hex Digit + /0 int i; - for (i = 0; i < 4; i++) - { - sprintf(buffer + (i * 8), "%08lx", esp_random()); + for(i = 0; i < 4; i++) { + sprintf (buffer + (i*8), "%08lx", (unsigned long int)esp_random()); } return String(buffer); } -esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char *realm, const char *authFailMsg) +esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char* realm, const char* authFailMsg) { - // what is thy realm, sire? - if (!strcmp(realm, "")) + //what is thy realm, sire? + if(!strcmp(realm, "")) this->setSessionKey("realm", "Login Required"); else this->setSessionKey("realm", realm); @@ -526,17 +484,17 @@ esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char PsychicResponse response(this); String authStr; - // what kind of auth? - if (mode == BASIC_AUTH) + //what kind of auth? + if(mode == BASIC_AUTH) { authStr = "Basic realm=\"" + this->getSessionKey("realm") + "\""; response.addHeader("WWW-Authenticate", authStr.c_str()); } else { - // only make new ones if we havent sent them yet + //only make new ones if we havent sent them yet if (this->getSessionKey("nonce").isEmpty()) - this->setSessionKey("nonce", _getRandomHexString()); + this->setSessionKey("nonce", _getRandomHexString()); if (this->getSessionKey("opaque").isEmpty()) this->setSessionKey("opaque", _getRandomHexString()); @@ -544,8 +502,6 @@ esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char response.addHeader("WWW-Authenticate", authStr.c_str()); } - // DUMP(authStr); - response.setCode(401); response.setContentType("text/html"); response.setContent(authStr.c_str()); diff --git a/esp32/lib/PsychicHttp/src/PsychicRequest.h b/esp32/lib/PsychicHttp/src/PsychicRequest.h index e4393c7..fe48a1b 100644 --- a/esp32/lib/PsychicHttp/src/PsychicRequest.h +++ b/esp32/lib/PsychicHttp/src/PsychicRequest.h @@ -33,7 +33,7 @@ class PsychicRequest { std::list _params; - void _addParams(const String& params); + void _addParams(const String& params, bool post); void _parseGETParams(); void _parsePOSTParams(); @@ -80,7 +80,7 @@ class PsychicRequest { void loadParams(); PsychicWebParameter * addParam(PsychicWebParameter *param); - PsychicWebParameter * addParam(const String &name, const String &value, bool decode = true); + PsychicWebParameter * addParam(const String &name, const String &value, bool decode = true, bool post = false); bool hasParam(const char *key); PsychicWebParameter * getParam(const char *name); diff --git a/esp32/lib/PsychicHttp/src/PsychicResponse.cpp b/esp32/lib/PsychicHttp/src/PsychicResponse.cpp index 515674e..5046441 100644 --- a/esp32/lib/PsychicHttp/src/PsychicResponse.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicResponse.cpp @@ -13,7 +13,7 @@ PsychicResponse::PsychicResponse(PsychicRequest *request) : PsychicResponse::~PsychicResponse() { - //clean up our header variables. we have to do this since httpd_resp_send doesn't store copies + //clean up our header variables. we have to do this on desctruct since httpd_resp_send doesn't store copies for (HTTPHeader header : _headers) { free(header.field); @@ -24,7 +24,7 @@ PsychicResponse::~PsychicResponse() void PsychicResponse::addHeader(const char *field, const char *value) { - //these get freed during send() + //these get freed after send by the destructor HTTPHeader header; header.field =(char *)malloc(strlen(field)+1); header.value = (char *)malloc(strlen(value)+1); @@ -65,9 +65,6 @@ void PsychicResponse::setCookie(const char *name, const char *value, unsigned lo addHeader("Set-Cookie", output.c_str()); } - // time_t now = time(nullptr); - // // Set the cookie with the "expires" attribute - void PsychicResponse::setCode(int code) { _code = code; @@ -128,6 +125,16 @@ void PsychicResponse::sendHeaders() //now do our individual headers for (HTTPHeader header : _headers) httpd_resp_set_hdr(this->_request->request(), header.field, header.value); + + // DO NOT RELEASE HEADERS HERE... released in the PsychicResponse destructor after they have been sent. + // httpd_resp_set_hdr just passes on the pointer, but its needed after this call. + // clean up our header variables after send + // for (HTTPHeader header : _headers) + // { + // free(header.field); + // free(header.value); + // } + // _headers.clear(); } esp_err_t PsychicResponse::sendChunk(uint8_t *chunk, size_t chunksize) diff --git a/esp32/lib/PsychicHttp/src/PsychicStaticFileHander.cpp b/esp32/lib/PsychicHttp/src/PsychicStaticFileHander.cpp index e58f3ad..54fafc4 100644 --- a/esp32/lib/PsychicHttp/src/PsychicStaticFileHander.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicStaticFileHander.cpp @@ -141,24 +141,16 @@ esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest *request) { if (_file == true) { - DUMP(_filename); - //is it not modified? String etag = String(_file.size()); if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) { - DUMP("Last Modified Hit"); - TRACE(); _file.close(); request->reply(304); // Not modified } //does our Etag match? else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) { - DUMP("Etag Hit"); - DUMP(etag); - DUMP(_cache_control); - _file.close(); PsychicResponse response(request); @@ -170,10 +162,6 @@ esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest *request) //nope, send them the full file. else { - DUMP("No cache hit"); - DUMP(_last_modified); - DUMP(_cache_control); - PsychicFileResponse response(request, _fs, _filename); if (_last_modified.length()) diff --git a/esp32/lib/PsychicHttp/src/PsychicStreamResponse.cpp b/esp32/lib/PsychicHttp/src/PsychicStreamResponse.cpp index 1ba615a..5132104 100644 --- a/esp32/lib/PsychicHttp/src/PsychicStreamResponse.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicStreamResponse.cpp @@ -16,7 +16,7 @@ PsychicStreamResponse::PsychicStreamResponse(PsychicRequest *request, const Stri setContentType(contentType.c_str()); char buf[26+name.length()]; - snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", name); + snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", name.c_str()); addHeader("Content-Disposition", buf); } diff --git a/esp32/lib/PsychicHttp/src/PsychicUploadHandler.cpp b/esp32/lib/PsychicHttp/src/PsychicUploadHandler.cpp index bb7d76a..d8a55e3 100644 --- a/esp32/lib/PsychicHttp/src/PsychicUploadHandler.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicUploadHandler.cpp @@ -29,7 +29,6 @@ esp_err_t PsychicUploadHandler::handleRequest(PsychicRequest *request) //save it for later (multipart) _request = request; _parsedLength = 0; - /* File cannot be larger than a limit */ if (request->contentLength() > request->server()->maxUploadSize) { @@ -37,7 +36,7 @@ esp_err_t PsychicUploadHandler::handleRequest(PsychicRequest *request) /* Respond with 400 Bad Request */ char error[50]; - sprintf(error, "File size must be less than %u bytes!", request->server()->maxUploadSize); + sprintf(error, "File size must be less than %lu bytes!", request->server()->maxUploadSize); httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error); /* Return failure to close underlying connection else the incoming file content will keep the socket busy */ @@ -94,7 +93,7 @@ esp_err_t PsychicUploadHandler::_basicUploadHandler(PsychicRequest *request) httpd_sess_update_lru_counter(request->server()->server, request->client()->socket()); #endif - ESP_LOGI(PH_TAG, "Remaining size : %d", remaining); + //ESP_LOGD(PH_TAG, "Remaining size : %d", remaining); /* Receive the file part by part into a buffer */ if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0) @@ -162,7 +161,7 @@ esp_err_t PsychicUploadHandler::_multipartUploadHandler(PsychicRequest *request) httpd_sess_update_lru_counter(request->server()->server, request->client()->socket()); #endif - ESP_LOGI(PH_TAG, "Remaining size : %d", remaining); + //ESP_LOGD(PH_TAG, "Remaining size : %d", remaining); /* Receive the file part by part into a buffer */ if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0) @@ -393,4 +392,4 @@ void PsychicUploadHandler::_parseMultipartPostByte(uint8_t data, bool last) itemWriteByte('\r'); _parseMultipartPostByte(data, last); } } -} \ No newline at end of file +} diff --git a/esp32/lib/PsychicHttp/src/PsychicWebHandler.cpp b/esp32/lib/PsychicHttp/src/PsychicWebHandler.cpp index d1b7916..1e4997e 100644 --- a/esp32/lib/PsychicHttp/src/PsychicWebHandler.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicWebHandler.cpp @@ -26,7 +26,7 @@ esp_err_t PsychicWebHandler::handleRequest(PsychicRequest *request) /* Respond with 400 Bad Request */ char error[60]; - sprintf(error, "Request body must be less than %u bytes!", request->server()->maxRequestBodySize); + sprintf(error, "Request body must be less than %lu bytes!", request->server()->maxRequestBodySize); httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error); /* Return failure to close underlying connection else the incoming file content will keep the socket busy */ diff --git a/esp32/lib/PsychicHttp/src/PsychicWebSocket.cpp b/esp32/lib/PsychicHttp/src/PsychicWebSocket.cpp index 73ee43f..7a3c6b1 100644 --- a/esp32/lib/PsychicHttp/src/PsychicWebSocket.cpp +++ b/esp32/lib/PsychicHttp/src/PsychicWebSocket.cpp @@ -4,8 +4,9 @@ /* PsychicWebSocketRequest */ /*************************************/ -PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest *req) : PsychicRequest(req->server(), req->request()), - _client(req->client()) +PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest *req) : + PsychicRequest(req->server(), req->request()), + _client(req->client()) { } @@ -13,22 +14,21 @@ PsychicWebSocketRequest::~PsychicWebSocketRequest() { } -PsychicWebSocketClient *PsychicWebSocketRequest::client() -{ +PsychicWebSocketClient * PsychicWebSocketRequest::client() { return &_client; } -esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t *ws_pkt) +esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t * ws_pkt) { return httpd_ws_send_frame(this->_req, ws_pkt); -} +} esp_err_t PsychicWebSocketRequest::reply(httpd_ws_type_t op, const void *data, size_t len) { httpd_ws_frame_t ws_pkt; memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); - ws_pkt.payload = (uint8_t *)data; + ws_pkt.payload = (uint8_t*)data; ws_pkt.len = len; ws_pkt.type = op; @@ -45,25 +45,24 @@ esp_err_t PsychicWebSocketRequest::reply(const char *buf) /*************************************/ PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient *client) - : PsychicClient(client->server(), client->socket()) + : PsychicClient(client->server(), client->socket()) { } -PsychicWebSocketClient::~PsychicWebSocketClient() -{ +PsychicWebSocketClient::~PsychicWebSocketClient() { } -esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t *ws_pkt) +esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t * ws_pkt) { return httpd_ws_send_frame_async(this->server(), this->socket(), ws_pkt); -} +} esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_type_t op, const void *data, size_t len) { httpd_ws_frame_t ws_pkt; memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); - ws_pkt.payload = (uint8_t *)data; + ws_pkt.payload = (uint8_t*)data; ws_pkt.len = len; ws_pkt.type = op; @@ -75,18 +74,18 @@ esp_err_t PsychicWebSocketClient::sendMessage(const char *buf) return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf)); } -PsychicWebSocketHandler::PsychicWebSocketHandler() : PsychicHandler(), - _onOpen(NULL), - _onFrame(NULL), - _onClose(NULL) -{ +PsychicWebSocketHandler::PsychicWebSocketHandler() : + PsychicHandler(), + _onOpen(NULL), + _onFrame(NULL), + _onClose(NULL) + { + } + +PsychicWebSocketHandler::~PsychicWebSocketHandler() { } -PsychicWebSocketHandler::~PsychicWebSocketHandler() -{ -} - -PsychicWebSocketClient *PsychicWebSocketHandler::getClient(int socket) +PsychicWebSocketClient * PsychicWebSocketHandler::getClient(int socket) { PsychicClient *client = PsychicHandler::getClient(socket); if (client == NULL) @@ -94,37 +93,31 @@ PsychicWebSocketClient *PsychicWebSocketHandler::getClient(int socket) if (client->_friend == NULL) { - DUMP(socket); return NULL; } return (PsychicWebSocketClient *)client->_friend; } -PsychicWebSocketClient *PsychicWebSocketHandler::getClient(PsychicClient *client) -{ +PsychicWebSocketClient * PsychicWebSocketHandler::getClient(PsychicClient *client) { return getClient(client->socket()); } -void PsychicWebSocketHandler::addClient(PsychicClient *client) -{ +void PsychicWebSocketHandler::addClient(PsychicClient *client) { client->_friend = new PsychicWebSocketClient(client); PsychicHandler::addClient(client); } -void PsychicWebSocketHandler::removeClient(PsychicClient *client) -{ +void PsychicWebSocketHandler::removeClient(PsychicClient *client) { PsychicHandler::removeClient(client); - delete (PsychicWebSocketClient *)client->_friend; + delete (PsychicWebSocketClient*)client->_friend; client->_friend = NULL; } -void PsychicWebSocketHandler::openCallback(PsychicClient *client) -{ +void PsychicWebSocketHandler::openCallback(PsychicClient *client) { PsychicWebSocketClient *buddy = getClient(client); if (buddy == NULL) { - TRACE(); return; } @@ -132,12 +125,10 @@ void PsychicWebSocketHandler::openCallback(PsychicClient *client) _onOpen(getClient(buddy)); } -void PsychicWebSocketHandler::closeCallback(PsychicClient *client) -{ +void PsychicWebSocketHandler::closeCallback(PsychicClient *client) { PsychicWebSocketClient *buddy = getClient(client); if (buddy == NULL) { - TRACE(); return; } @@ -149,7 +140,7 @@ bool PsychicWebSocketHandler::isWebSocket() { return true; } esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request) { - // lookup our client + //lookup our client PsychicClient *client = checkForNewClient(request->client()); // beginning of the ws URI handler and our onConnect hook @@ -161,10 +152,10 @@ esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request) return ESP_OK; } - // prep our request + //prep our request PsychicWebSocketRequest wsRequest(request); - // init our memory for storing the packet + //init our memory for storing the packet httpd_ws_frame_t ws_pkt; memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); ws_pkt.type = HTTPD_WS_TYPE_TEXT; @@ -172,33 +163,29 @@ esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request) /* Set max_len = 0 to get the frame len */ esp_err_t ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, 0); - if (ret != ESP_OK) - { + if (ret != ESP_OK) { ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed to get frame len with %s", esp_err_to_name(ret)); return ret; } - // okay, now try to load the packet - ESP_LOGV(PH_TAG, "frame len is %d", ws_pkt.len); - if (ws_pkt.len) - { + //okay, now try to load the packet + //ESP_LOGD(PH_TAG, "frame len is %d", ws_pkt.len); + if (ws_pkt.len) { /* ws_pkt.len + 1 is for NULL termination as we are expecting a string */ - buf = (uint8_t *)calloc(1, ws_pkt.len + 1); - if (buf == NULL) - { + buf = (uint8_t*) calloc(1, ws_pkt.len + 1); + if (buf == NULL) { ESP_LOGE(PH_TAG, "Failed to calloc memory for buf"); return ESP_ERR_NO_MEM; } ws_pkt.payload = buf; /* Set max_len = ws_pkt.len to get the frame payload */ ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, ws_pkt.len); - if (ret != ESP_OK) - { + if (ret != ESP_OK) { ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed with %s", esp_err_to_name(ret)); free(buf); return ret; } - ESP_LOGV(PH_TAG, "Got packet with message: %s", ws_pkt.payload); + //ESP_LOGD(PH_TAG, "Got packet with message: %s", ws_pkt.payload); } // Text messages are our payload. @@ -208,49 +195,47 @@ esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request) ret = this->_onFrame(&wsRequest, &ws_pkt); } - // logging housekeeping + //logging housekeeping if (ret != ESP_OK) ESP_LOGE(PH_TAG, "httpd_ws_send_frame failed with %s", esp_err_to_name(ret)); - // ESP_LOGI(PH_TAG, "ws_handler: httpd_handle_t=%p, sockfd=%d, client_info:%d", request->server(), - // httpd_req_to_sockfd(request->request()), httpd_ws_get_fd_info(request->server(), httpd_req_to_sockfd(request->request()))); + // ESP_LOGD(PH_TAG, "ws_handler: httpd_handle_t=%p, sockfd=%d, client_info:%d", + // request->server(), + // httpd_req_to_sockfd(request->request()), + // httpd_ws_get_fd_info(request->server()->server, httpd_req_to_sockfd(request->request()))); - // dont forget to release our buffer memory + //dont forget to release our buffer memory free(buf); return ret; } -PsychicWebSocketHandler *PsychicWebSocketHandler::onOpen(PsychicWebSocketClientCallback fn) -{ +PsychicWebSocketHandler * PsychicWebSocketHandler::onOpen(PsychicWebSocketClientCallback fn) { _onOpen = fn; return this; } -PsychicWebSocketHandler *PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) -{ +PsychicWebSocketHandler * PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) { _onFrame = fn; return this; } -PsychicWebSocketHandler *PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) -{ +PsychicWebSocketHandler * PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) { _onClose = fn; return this; } -void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t *ws_pkt) +void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t * ws_pkt) { for (PsychicClient *client : _clients) { - ESP_LOGD(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket()); + //ESP_LOGD(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket()); if (client->_friend == NULL) { - TRACE(); return; } - if (((PsychicWebSocketClient *)client->_friend)->sendMessage(ws_pkt) != ESP_OK) + if (((PsychicWebSocketClient*)client->_friend)->sendMessage(ws_pkt) != ESP_OK) break; } } @@ -260,7 +245,7 @@ void PsychicWebSocketHandler::sendAll(httpd_ws_type_t op, const void *data, size httpd_ws_frame_t ws_pkt; memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); - ws_pkt.payload = (uint8_t *)data; + ws_pkt.payload = (uint8_t*)data; ws_pkt.len = len; ws_pkt.type = op; diff --git a/esp32/lib/PsychicHttp/src/TemplatePrinter.cpp b/esp32/lib/PsychicHttp/src/TemplatePrinter.cpp new file mode 100644 index 0000000..5c05904 --- /dev/null +++ b/esp32/lib/PsychicHttp/src/TemplatePrinter.cpp @@ -0,0 +1,90 @@ + /************************************************************ + + TemplatePrinter Class + + A basic templating engine for a stream of text. + This wraps the Arduino Print interface and writes to any + Print interface. + + Written by Christopher Andrews (https://github.com/Chris--A) + + ************************************************************/ + +#include "TemplatePrinter.h" + +void TemplatePrinter::resetParam(bool flush){ + if(flush && _inParam){ + _stream.write(_delimiter); + + if(_paramPos) + _stream.print(_paramBuffer); + } + + memset(_paramBuffer, 0, sizeof(_paramBuffer)); + _paramPos = 0; + _inParam = false; +} + + +void TemplatePrinter::flush(){ + resetParam(true); + _stream.flush(); +} + +size_t TemplatePrinter::write(uint8_t data){ + + if(data == _delimiter){ + + // End of parameter, send to callback + if(_inParam){ + + // On false, return the parameter place holder as is: not a parameter + // Bug fix: ignore parameters that are zero length. + if(!_paramPos || !_cb(_stream, _paramBuffer)){ + resetParam(true); + _stream.write(data); + }else{ + resetParam(false); + } + + // Start collecting parameter + }else{ + _inParam = true; + } + }else{ + + // Are we collecting + if(_inParam){ + + // Is param still valid + if(isalnum(data) || data == '_'){ + + // Total param len must be 63, 1 for null. + if(_paramPos < sizeof(_paramBuffer) - 1){ + _paramBuffer[_paramPos++] = data; + + // Not a valid param + }else{ + resetParam(true); + } + }else{ + resetParam(true); + _stream.write(data); + } + + // Just output + }else{ + _stream.write(data); + } + } + return 1; +} + +size_t TemplatePrinter::copyFrom(Stream &stream){ + size_t count = 0; + + while(stream.available()) + count += this->write(stream.read()); + + return count; +} diff --git a/esp32/lib/PsychicHttp/src/TemplatePrinter.h b/esp32/lib/PsychicHttp/src/TemplatePrinter.h new file mode 100644 index 0000000..6adf14a --- /dev/null +++ b/esp32/lib/PsychicHttp/src/TemplatePrinter.h @@ -0,0 +1,51 @@ +#ifndef TemplatePrinter_h + #define TemplatePrinter_h + + #include "PsychicCore.h" + #include + + /************************************************************ + + TemplatePrinter Class + + A basic templating engine for a stream of text. + This wraps the Arduino Print interface and writes to any + Print interface. + + Written by Christopher Andrews (https://github.com/Chris--A) + + ************************************************************/ + + class TemplatePrinter; + + typedef std::function TemplateCallback; + typedef std::function TemplateSourceCallback; + + class TemplatePrinter : public Print{ + private: + bool _inParam; + char _paramBuffer[64]; + uint8_t _paramPos; + Print &_stream; + TemplateCallback _cb; + char _delimiter; + + void resetParam(bool flush); + + public: + using Print::write; + + static void start(Print &stream, TemplateCallback cb, TemplateSourceCallback entry){ + TemplatePrinter printer(stream, cb); + entry(printer); + } + + TemplatePrinter(Print &stream, TemplateCallback cb, const char delimeter = '%') : _stream(stream), _cb(cb), _delimiter(delimeter) { resetParam(false); } + ~TemplatePrinter(){ flush(); } + + void flush() override; + size_t write(uint8_t data) override; + size_t copyFrom(Stream &stream); + }; + +#endif diff --git a/esp32/lib/PsychicHttp/src/async_worker.cpp b/esp32/lib/PsychicHttp/src/async_worker.cpp index b5b7c73..d7310cb 100644 --- a/esp32/lib/PsychicHttp/src/async_worker.cpp +++ b/esp32/lib/PsychicHttp/src/async_worker.cpp @@ -166,7 +166,7 @@ esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out) if (async == NULL) { return ESP_ERR_NO_MEM; } - memcpy(async, r, sizeof(httpd_req_t)); + memcpy((void *)async, (void *)r, sizeof(httpd_req_t)); // alloc async aux async->aux = (httpd_req_aux *)malloc(sizeof(struct httpd_req_aux));