From 0c50cfa639a1c17046fbcd804f854904092dbaec Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 14 Dec 2023 19:17:28 -0300 Subject: [PATCH] some thinking around passing the form --- handler/CMakeLists.txt | 2 ++ handler/register.cpp | 23 +++++++++++++++++++++ handler/register.h | 16 +++++++++++++++ request/request.cpp | 42 ++++++++++++++++++++++++++++++++++++++ request/request.h | 5 +++++ server/server.cpp | 2 ++ utils/CMakeLists.txt | 2 ++ utils/formdecode.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++ utils/formdecode.h | 11 ++++++++++ 9 files changed, 149 insertions(+) create mode 100644 handler/register.cpp create mode 100644 handler/register.h create mode 100644 utils/formdecode.cpp create mode 100644 utils/formdecode.h diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index a3dd084..1f58f4f 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -2,12 +2,14 @@ set(HEADERS handler.h info.h env.h + register.h ) set(SOURCES handler.cpp info.cpp env.cpp + register.cpp ) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/handler/register.cpp b/handler/register.cpp new file mode 100644 index 0000000..4928627 --- /dev/null +++ b/handler/register.cpp @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Yury Gubich +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "register.h" + +Handler::Register::Register(): + Handler("register", Request::Method::post) +{} + +void Handler::Register::handle(Request& request) { + std::map form = request.getForm(); + + std::cout << "Received form:" << std::endl; + for (const auto& pair : form) + std::cout << '\t' << pair.first << ": " << pair.second << std::endl; + + Response res(request); + nlohmann::json body = nlohmann::json::object(); + body["result"] = "ok"; + + res.setBody(body); + res.send(); +} diff --git a/handler/register.h b/handler/register.h new file mode 100644 index 0000000..047dde3 --- /dev/null +++ b/handler/register.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Yury Gubich +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "handler.h" + +namespace Handler { + +class Register : public Handler::Handler { +public: + Register(); + virtual void handle(Request& request); + +}; +} diff --git a/request/request.cpp b/request/request.cpp index 48e084b..2859e00 100644 --- a/request/request.cpp +++ b/request/request.cpp @@ -10,6 +10,10 @@ constexpr static const char* GET("GET"); constexpr static const char* REQUEST_METHOD("REQUEST_METHOD"); constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME"); constexpr static const char* SERVER_NAME("SERVER_NAME"); +constexpr static const char* CONTENT_TYPE("CONTENT_TYPE"); +constexpr static const char* CONTENT_LENGTH("CONTENT_LENGTH"); + +constexpr static const char* urlEncoded("application/x-www-form-urlencoded"); // constexpr static const char* REQUEST_URI("REQUEST_URI"); // @@ -178,3 +182,41 @@ void Request::printEnvironment(nlohmann::json& out) { out[std::string(value.substr(0, pos))] = std::string(value.substr(pos + 1, value.size())); } } + +bool Request::isFormUrlEncoded() const { + if (state == State::initial) + throw std::runtime_error("An attempt to read request content type on not accepted request"); + + std::string_view contentType(FCGX_GetParam(CONTENT_TYPE, raw.envp)); + if (!contentType.empty() && contentType.find(urlEncoded) != std::string_view::npos) { + return true; + } + + return false; +} + +unsigned int Request::contentLength() const { + if (state == State::initial) + throw std::runtime_error("An attempt to read request content length on not accepted request"); + + return atoi(FCGX_GetParam(CONTENT_LENGTH, raw.envp)); +} + +std::map Request::getForm() const { + if (state == State::initial) + throw std::runtime_error("An attempt to read form on not accepted request"); + + std::map result; + std::string_view contentType(FCGX_GetParam(CONTENT_TYPE, raw.envp)); + if (contentType.empty()) + return result; + + unsigned int length = contentLength(); + if (contentType.find(urlEncoded) != std::string_view::npos) { + std::string postData(length, '\0'); + FCGX_GetStr(&postData[0], length, raw.in); + result = urlDecodeAndParse(postData); + } + + return result; +} diff --git a/request/request.h b/request/request.h index 9871106..e28e283 100644 --- a/request/request.h +++ b/request/request.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,7 @@ #include #include "stream/ostream.h" +#include "utils/formdecode.h" class Response; class Request { @@ -43,6 +45,9 @@ public: Method method() const; State currentState() const; + bool isFormUrlEncoded() const; + unsigned int contentLength() const; + std::map getForm() const; OStream getOutputStream(const Response* response); OStream getErrorStream(const Response* response); diff --git a/server/server.cpp b/server/server.cpp index 744c27e..dad6a3a 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -5,6 +5,7 @@ #include "handler/info.h" #include "handler/env.h" +#include "handler/register.h" constexpr uint8_t currentDbVesion = 1; @@ -28,6 +29,7 @@ Server::Server(): router.addRoute(std::make_unique()); router.addRoute(std::make_unique()); + router.addRoute(std::make_unique()); } Server::~Server() {} diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 93d7c25..4834150 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,9 +1,11 @@ set(HEADER helpers.h + formdecode.h ) set(SOURCES helpers.cpp + formdecode.cpp ) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/utils/formdecode.cpp b/utils/formdecode.cpp new file mode 100644 index 0000000..8dbd14d --- /dev/null +++ b/utils/formdecode.cpp @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2023 Yury Gubich +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "formdecode.h" + +#include + +std::map urlDecodeAndParse(const std::string_view& encoded) { + std::map result; + + std::istringstream iss(std::string(encoded.begin(), encoded.end())); + std::string pair; + while (std::getline(iss, pair, '&')) { + size_t equalsPos = pair.find('='); + if (equalsPos != std::string::npos) + result.emplace( + urlDecode(pair.substr(0, equalsPos)), + urlDecode(pair.substr(equalsPos + 1)) + ); + } + + return result; +} + +std::string urlDecode(const std::string_view& encoded) { + std::ostringstream decoded; + + for (size_t i = 0; i < encoded.length(); ++i) { + if (encoded[i] == '%') { + if (i + 2 < encoded.length()) { + std::string hexStr(encoded.substr(i + 1, 2)); + char hexValue = static_cast(std::stoul(hexStr, nullptr, 16)); + decoded.put(hexValue); + i += 2; + } else { + decoded.put(encoded[i]); + } + } else if (encoded[i] == '+') { + decoded.put(' '); + } else { + decoded.put(encoded[i]); + } + } + + return decoded.str(); +} diff --git a/utils/formdecode.h b/utils/formdecode.h new file mode 100644 index 0000000..5f4ec61 --- /dev/null +++ b/utils/formdecode.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Yury Gubich +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +std::map urlDecodeAndParse(const std::string_view& encoded); +std::string urlDecode(const std::string_view& encoded);