diff --git a/src/asio_webserver/clientconnection.cpp b/src/asio_webserver/clientconnection.cpp index bc81ea2..3cdb8d2 100644 --- a/src/asio_webserver/clientconnection.cpp +++ b/src/asio_webserver/clientconnection.cpp @@ -7,6 +7,10 @@ // esp-idf includes #include +// 3rdparty lib includes +#include +#include + // local includes #include "webserver.h" #include "responsehandler.h" @@ -76,6 +80,45 @@ void ClientConnection::readyRead(std::error_code ec, std::size_t length) return; } + if (m_state == State::RequestBody) + { + if (!m_responseHandler) + { + ESP_LOGW(TAG, "invalid response handler (%s:%hi)", + m_remote_endpoint.address().to_string().c_str(), m_remote_endpoint.port()); + m_socket.close(); + return; + } + + if (!m_requestBodySize) + goto requestFinished; + else + { + if (length <= m_requestBodySize) + { + m_responseHandler->requestBodyReceived({m_receiveBuffer, length}); + m_requestBodySize -= length; + length = 0; + + if (!m_requestBodySize) + goto requestFinished; + } + else + { + m_responseHandler->requestBodyReceived({m_receiveBuffer, m_requestBodySize}); + // TODO how to erase from m_receiveBuffer ? + m_requestBodySize = 0; + goto requestFinished; + } + +requestFinished: +// ESP_LOGV(TAG, "state changed to Response"); + m_state = State::Response; + + m_responseHandler->sendResponse(); + } + } + // ESP_LOGV(TAG, "received: %zd \"%.*s\"", length, length, m_data); m_parsingBuffer.append(m_receiveBuffer, length); @@ -88,14 +131,14 @@ void ClientConnection::readyRead(std::error_code ec, std::size_t length) if (index == std::string::npos) break; - std::string_view line{m_parsingBuffer.data(), index}; + std::string line{m_parsingBuffer.data(), index}; // ESP_LOGD(TAG, "line: %zd \"%.*s\"", line.size(), line.size(), line.data()); + m_parsingBuffer.erase(std::begin(m_parsingBuffer), std::next(std::begin(m_parsingBuffer), line.size() + newLine.size())); + if (!readyReadLine(line)) shouldDoRead = false; - - m_parsingBuffer.erase(std::begin(m_parsingBuffer), std::next(std::begin(m_parsingBuffer), line.size() + newLine.size())); } if (shouldDoRead) @@ -195,6 +238,19 @@ bool ClientConnection::parseRequestHeader(std::string_view line) // ESP_LOGD(TAG, "header key=\"%.*s\" value=\"%.*s\"", key.size(), key.data(), value.size(), value.data()); + if (cpputils::stringEqualsIgnoreCase(key, "Content-Length")) + { + if (const auto parsed = cpputils::fromString(value); !parsed) + { + ESP_LOGW(TAG, "invalid Content-Length %.*s %.*s", value.size(), value.data(), + parsed.error().size(), parsed.error().data()); + m_socket.close(); + return false; + } + else + m_requestBodySize = *parsed; + } + if (!m_responseHandler) { ESP_LOGW(TAG, "invalid response handler (%s:%hi)", @@ -209,19 +265,52 @@ bool ClientConnection::parseRequestHeader(std::string_view line) } else { -// ESP_LOGV(TAG, "state changed to Response"); - m_state = State::Response; - if (!m_responseHandler) { - ESP_LOGW(TAG, "invalid response handler ESP_LOGI", + ESP_LOGW(TAG, "invalid response handler (%s:%hi)", m_remote_endpoint.address().to_string().c_str(), m_remote_endpoint.port()); m_socket.close(); return false; } - m_responseHandler->sendResponse(); + if (m_requestBodySize) + { +// ESP_LOGV(TAG, "state changed to RequestBody"); + m_state = State::RequestBody; - return false; + if (!m_parsingBuffer.empty()) + { + if (m_parsingBuffer.size() <= m_requestBodySize) + { + m_responseHandler->requestBodyReceived(m_parsingBuffer); + m_requestBodySize -= m_parsingBuffer.size(); + m_parsingBuffer.clear(); + + if (!m_requestBodySize) + goto requestFinished; + + return true; + } + else + { + m_responseHandler->requestBodyReceived({m_parsingBuffer.data(), m_requestBodySize}); + m_parsingBuffer.erase(std::begin(m_parsingBuffer), std::next(std::begin(m_parsingBuffer), m_requestBodySize)); + m_requestBodySize = 0; + goto requestFinished; + } + } + else + return true; + } + else + { +requestFinished: +// ESP_LOGV(TAG, "state changed to Response"); + m_state = State::Response; + + m_responseHandler->sendResponse(); + + return false; + } } } diff --git a/src/asio_webserver/responsehandler.h b/src/asio_webserver/responsehandler.h index 1ddd3f9..24b72b3 100644 --- a/src/asio_webserver/responsehandler.h +++ b/src/asio_webserver/responsehandler.h @@ -9,5 +9,6 @@ public: virtual ~ResponseHandler() = default; virtual void requestHeaderReceived(std::string_view key, std::string_view value) = 0; + virtual void requestBodyReceived(std::string_view body) = 0; virtual void sendResponse() = 0; }; diff --git a/test/webserver_example/chunkedresponsehandler.cpp b/test/webserver_example/chunkedresponsehandler.cpp index 689fc1f..429171a 100644 --- a/test/webserver_example/chunkedresponsehandler.cpp +++ b/test/webserver_example/chunkedresponsehandler.cpp @@ -29,6 +29,10 @@ void ChunkedResponseHandler::requestHeaderReceived(std::string_view key, std::st { } +void ChunkedResponseHandler::requestBodyReceived(std::string_view body) +{ +} + void ChunkedResponseHandler::sendResponse() { ESP_LOGI(TAG, "sending response (header) for (%s:%hi)", diff --git a/test/webserver_example/chunkedresponsehandler.h b/test/webserver_example/chunkedresponsehandler.h index 314859f..5b4c40f 100644 --- a/test/webserver_example/chunkedresponsehandler.h +++ b/test/webserver_example/chunkedresponsehandler.h @@ -11,13 +11,14 @@ // forward declarations class ClientConnection; -class ChunkedResponseHandler : public ResponseHandler +class ChunkedResponseHandler final : public ResponseHandler { public: ChunkedResponseHandler(ClientConnection &clientConnection); - ~ChunkedResponseHandler() override; + ~ChunkedResponseHandler() final; void requestHeaderReceived(std::string_view key, std::string_view value) final; + void requestBodyReceived(std::string_view body) final; void sendResponse() final; private: diff --git a/test/webserver_example/debugresponsehandler.cpp b/test/webserver_example/debugresponsehandler.cpp index 68f1d50..5705e86 100644 --- a/test/webserver_example/debugresponsehandler.cpp +++ b/test/webserver_example/debugresponsehandler.cpp @@ -30,6 +30,11 @@ void DebugResponseHandler::requestHeaderReceived(std::string_view key, std::stri m_requestHeaders.emplace_back(std::make_pair(std::string{key}, std::string{value})); } +void DebugResponseHandler::requestBodyReceived(std::string_view body) +{ + m_requestBody += body; +} + void DebugResponseHandler::sendResponse() { ESP_LOGI(TAG, "sending response for %.*s %.*s (%s:%hi)", m_method.size(), m_method.data(), m_path.size(), m_path.data(), @@ -56,10 +61,19 @@ void DebugResponseHandler::sendResponse() m_response += fmt::format( "{}{}", pair.first, pair.second); - m_response += fmt::format( "" + m_response += "" "" - "" - "" + ""; + + if (!m_requestBody.empty()) + { + m_response += fmt::format( "" + "Request body:" + "
{}
" + "", m_requestBody); + } + + m_response += "" "" "
" "
" @@ -90,7 +104,7 @@ void DebugResponseHandler::sendResponse() "
" "
" "" - ""); + ""; m_response = fmt::format("HTTP/1.1 200 Ok\r\n" "Content-Type: text/html\r\n" diff --git a/test/webserver_example/debugresponsehandler.h b/test/webserver_example/debugresponsehandler.h index d512605..85d9688 100644 --- a/test/webserver_example/debugresponsehandler.h +++ b/test/webserver_example/debugresponsehandler.h @@ -13,13 +13,14 @@ // forward declarations class ClientConnection; -class DebugResponseHandler : public ResponseHandler +class DebugResponseHandler final : public ResponseHandler { public: DebugResponseHandler(ClientConnection &clientConnection, std::string_view method, std::string_view path, std::string_view protocol); - ~DebugResponseHandler() override; + ~DebugResponseHandler() final; void requestHeaderReceived(std::string_view key, std::string_view value) final; + void requestBodyReceived(std::string_view body) final; void sendResponse() final; private: @@ -32,5 +33,7 @@ private: std::vector> m_requestHeaders; + std::string m_requestBody; + std::string m_response; }; diff --git a/test/webserver_example/errorresponsehandler.cpp b/test/webserver_example/errorresponsehandler.cpp index 61a23fe..8e36aa3 100644 --- a/test/webserver_example/errorresponsehandler.cpp +++ b/test/webserver_example/errorresponsehandler.cpp @@ -30,6 +30,10 @@ void ErrorResponseHandler::requestHeaderReceived(std::string_view key, std::stri { } +void ErrorResponseHandler::requestBodyReceived(std::string_view body) +{ +} + void ErrorResponseHandler::sendResponse() { ESP_LOGI(TAG, "sending response for %.*s (%s:%hi)", m_path.size(), m_path.data(), diff --git a/test/webserver_example/errorresponsehandler.h b/test/webserver_example/errorresponsehandler.h index eef1b50..7132cfc 100644 --- a/test/webserver_example/errorresponsehandler.h +++ b/test/webserver_example/errorresponsehandler.h @@ -11,13 +11,14 @@ // forward declarations class ClientConnection; -class ErrorResponseHandler : public ResponseHandler +class ErrorResponseHandler final : public ResponseHandler { public: ErrorResponseHandler(ClientConnection &clientConnection, std::string_view path); - ~ErrorResponseHandler() override; + ~ErrorResponseHandler() final; void requestHeaderReceived(std::string_view key, std::string_view value) final; + void requestBodyReceived(std::string_view body) final; void sendResponse() final; private: diff --git a/test/webserver_example/rootresponsehandler.cpp b/test/webserver_example/rootresponsehandler.cpp index 55d0d60..3bd5c8d 100644 --- a/test/webserver_example/rootresponsehandler.cpp +++ b/test/webserver_example/rootresponsehandler.cpp @@ -29,6 +29,10 @@ void RootResponseHandler::requestHeaderReceived(std::string_view key, std::strin { } +void RootResponseHandler::requestBodyReceived(std::string_view body) +{ +} + void RootResponseHandler::sendResponse() { ESP_LOGI(TAG, "sending response for (%s:%hi)", @@ -40,8 +44,10 @@ void RootResponseHandler::sendResponse() "" "" "

asio test webserver

" - "Debug" - "Chunked" + "" "" ""); diff --git a/test/webserver_example/rootresponsehandler.h b/test/webserver_example/rootresponsehandler.h index 249484b..313714a 100644 --- a/test/webserver_example/rootresponsehandler.h +++ b/test/webserver_example/rootresponsehandler.h @@ -11,13 +11,14 @@ // forward declarations class ClientConnection; -class RootResponseHandler : public ResponseHandler +class RootResponseHandler final : public ResponseHandler { public: RootResponseHandler(ClientConnection &clientConnection); ~RootResponseHandler() override; void requestHeaderReceived(std::string_view key, std::string_view value) final; + void requestBodyReceived(std::string_view body) final; void sendResponse() final; private: