From 6e478460f54826ae4bfd450ee71b3ab8134574f5 Mon Sep 17 00:00:00 2001 From: Niklas Jensen Date: Sat, 24 Jan 2026 12:56:19 +0100 Subject: [PATCH] Redone protobuf receiving to be more dynamic and scalable --- esp32/include/communication/webserver.h | 7 +-- .../template/stateful_proto_endpoint.h | 17 +++---- esp32/src/communication/webserver.cpp | 51 ++++++++++++++++--- esp32/src/main.cpp | 6 ++- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/esp32/include/communication/webserver.h b/esp32/include/communication/webserver.h index 1fa9ac0..97909d8 100644 --- a/esp32/include/communication/webserver.h +++ b/esp32/include/communication/webserver.h @@ -8,10 +8,11 @@ #include #include #include +#include using HttpGetHandler = std::function; using HttpPostHandler = std::function; -using HttpRawHandler = std::function; +using HttpProtoHandler = std::function; using WsFrameHandler = std::function; using WsOpenHandler = std::function; using WsCloseHandler = std::function; @@ -21,7 +22,7 @@ struct HttpRoute { httpd_method_t method; HttpGetHandler getHandler; HttpPostHandler postHandler; - HttpRawHandler rawHandler; // For proto handlers that don't need JSON parsing + HttpProtoHandler protoHandler; // For proto handlers that don't need JSON parsing bool isWebsocket; }; @@ -36,7 +37,7 @@ class WebServer { void on(const char* uri, httpd_method_t method, HttpGetHandler handler); void on(const char* uri, httpd_method_t method, HttpPostHandler handler); - void onRaw(const char* uri, httpd_method_t method, HttpRawHandler handler); + void onProto(const char* uri, httpd_method_t method, HttpProtoHandler handler); void onWsFrame(WsFrameHandler handler); void onWsOpen(WsOpenHandler handler); diff --git a/esp32/include/template/stateful_proto_endpoint.h b/esp32/include/template/stateful_proto_endpoint.h index f90532f..20e697f 100644 --- a/esp32/include/template/stateful_proto_endpoint.h +++ b/esp32/include/template/stateful_proto_endpoint.h @@ -58,27 +58,22 @@ class StatefulProtoEndpoint { _responseAssigner(responseAssigner) {} /** - * Handles POST requests: decodes Request, updates state, returns Response + * Handles POST requests: extracts payload from pre-decoded Request, updates state, returns Response */ - esp_err_t handleStateUpdate(httpd_req_t* request) { - api_Request req = api_Request_init_zero; - if (!NativeServer::receiveProto(request, req, api_Request_fields)) { - return sendErrorResponse(request, 400, "Failed to decode request"); - } - + esp_err_t handleStateUpdate(httpd_req_t* httpReq, api_Request* protoReq) { ProtoT protoMsg = {}; - if (!_requestExtractor(req, protoMsg)) { - return sendErrorResponse(request, 400, "Invalid request type"); + if (!_requestExtractor(*protoReq, protoMsg)) { + return sendErrorResponse(httpReq, 400, "Invalid request type"); } StateUpdateResult outcome = _statefulService->update( [this, &protoMsg](T& settings) { return _stateUpdater(protoMsg, settings); }, PROTO_ENDPOINT_ORIGIN_ID); if (outcome == StateUpdateResult::ERROR) { - return sendErrorResponse(request, 400, "Invalid state"); + return sendErrorResponse(httpReq, 400, "Invalid state"); } - return sendStateResponse(request, 200); + return sendStateResponse(httpReq, 200); } /** diff --git a/esp32/src/communication/webserver.cpp b/esp32/src/communication/webserver.cpp index 2b3da15..b5bddb1 100644 --- a/esp32/src/communication/webserver.cpp +++ b/esp32/src/communication/webserver.cpp @@ -72,8 +72,45 @@ esp_err_t WebServer::httpHandler(httpd_req_t* req) { if (route.getHandler) { return route.getHandler(req); } - if (route.rawHandler) { - return route.rawHandler(req); + if (route.protoHandler) { + size_t contentLen = req->content_len; + if (contentLen == 0 || contentLen > 4096) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid content length"); + return ESP_FAIL; + } + + uint8_t* buffer = (uint8_t*)malloc(contentLen); + if (!buffer) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory allocation failed"); + return ESP_FAIL; + } + + int received = 0; + int remaining = contentLen; + while (remaining > 0) { + int ret = httpd_req_recv(req, (char*)buffer + received, remaining); + if (ret <= 0) { + free(buffer); + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_err(req, HTTPD_408_REQ_TIMEOUT, "Request timeout"); + } + return ESP_FAIL; + } + received += ret; + remaining -= ret; + } + + api_Request protoReq = api_Request_init_zero; + pb_istream_t stream = pb_istream_from_buffer(buffer, contentLen); + bool success = pb_decode(&stream, api_Request_fields, &protoReq); + free(buffer); + + if (!success) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Failed to decode protobuf"); + return ESP_FAIL; + } + + return route.protoHandler(req, &protoReq); } if (route.postHandler) { char* content = nullptr; @@ -190,7 +227,7 @@ void WebServer::on(const char* uri, httpd_method_t method, HttpGetHandler handle route.method = method; route.getHandler = handler; route.postHandler = nullptr; - route.rawHandler = nullptr; + route.protoHandler = nullptr; route.isWebsocket = false; routes_.push_back(route); @@ -205,7 +242,7 @@ void WebServer::on(const char* uri, httpd_method_t method, HttpPostHandler handl route.method = method; route.getHandler = nullptr; route.postHandler = handler; - route.rawHandler = nullptr; + route.protoHandler = nullptr; route.isWebsocket = false; routes_.push_back(route); @@ -214,13 +251,13 @@ void WebServer::on(const char* uri, httpd_method_t method, HttpPostHandler handl } } -void NativeServer::onRaw(const char* uri, httpd_method_t method, HttpRawHandler handler) { +void NativeServer::onProto(const char* uri, httpd_method_t method, HttpProtoHandler handler) { HttpRoute route; route.uri = uri; route.method = method; route.getHandler = nullptr; route.postHandler = nullptr; - route.rawHandler = handler; + route.protoHandler = handler; route.isWebsocket = false; routes_.push_back(route); @@ -246,7 +283,7 @@ void WebServer::registerWebsocket(const char* uri) { route.method = HTTP_GET; route.getHandler = nullptr; route.postHandler = nullptr; - route.rawHandler = nullptr; + route.protoHandler = nullptr; route.isWebsocket = true; routes_.push_back(route); diff --git a/esp32/src/main.cpp b/esp32/src/main.cpp index bb397dc..71efde8 100644 --- a/esp32/src/main.cpp +++ b/esp32/src/main.cpp @@ -77,8 +77,10 @@ void setupServer() { server.on("/api/ap/status", HTTP_GET, [&](httpd_req_t *request) { return apService.getStatusProto(request); }); server.on("/api/ap/settings", HTTP_GET, [&](httpd_req_t *request) { return apService.protoEndpoint.getState(request); }); - server.onRaw("/api/ap/settings", HTTP_POST, - [&](httpd_req_t *request) { return apService.protoEndpoint.handleStateUpdate(request); }); + server.onProto("/api/ap/settings", HTTP_POST, + [&](httpd_req_t *request, api_Request *protoReq) { + return apService.protoEndpoint.handleStateUpdate(request, protoReq); + }); server.on("/api/peripherals", HTTP_GET, [&](httpd_req_t *request) { return peripherals.endpoint.getState(request); });