/* * (c) Copyright, Real-Time Innovations, 2022. * All rights reserved. * * No duplications, whole or partial, manual or electronic, may be made * without express written permission. Any such copies, or * revisions thereof, must display this notice unaltered. * This code contains trade secrets of Real-Time Innovations, Inc. */ #ifndef HTTP_SERVER_REQUEST_HPP #define HTTP_SERVER_REQUEST_HPP #include #include #include #include #include "rti/apputils/StringBuffer.hpp" #include "rti/apputils/http/common/Method.hpp" #include "rti/apputils/http/common/HttpException.hpp" namespace rti { namespace apputils { namespace http { namespace server { /** * @brief This class implements the Request to handle Requests, that is * requests provided by the CivetWeb server. */ class Request { public: /** * @brief Constructor of the Request. Retrieves information from the * mg_request_info provided by CivetWeb's connection. * * @param conn Civetweb's connection. */ explicit Request(mg_connection *conn) : conn_(conn) { } /** * @brief Destructor of the Request class. */ ~Request() { } /** * @brief Returns the URL of CivetWeb's request. * * @return URL of the HTTP request. */ const char *uri() const { const auto req = mg_get_request_info(conn_); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( req != nullptr, msg_server_error_get_request_info); return req->local_uri; } /** * @brief Gets a URI parameter given its name. * * @param value The value of the parameter. * @param name of the parameter to be retrieved. * @return Indicates whether the parameter was found. */ bool uri_param(std::string& value, const std::string& name) const { const auto req = mg_get_request_info(conn_); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( req != nullptr, msg_server_error_get_request_info); if (req->query_string == nullptr) { return false; } return CivetServer::getParam(req->query_string, name.c_str(), value); } /** * @brief Constructs a map with the query parameters in a given URI. * * @param params Map with decoded names and values of all the query * parameters in the URI. * @return Boolean value indicating whether query parameters were retrieved * or not. */ bool uri_params(StringMap& params) const { const auto req = mg_get_request_info(conn_); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( req != nullptr, msg_server_error_get_request_info); if (req->query_string == nullptr) { return false; } const auto query_string = req->query_string; auto query_string_length = strlen(query_string); auto i = std::size_t { 0 }; while (i < query_string_length) { // Get Name auto i0 = i; while ((i < query_string_length) && query_string[i] != '=' && query_string[i] != '&') { i++; } auto name = std::string(); CivetServer::urlDecode(&query_string[i0], i - i0, name, true); if (i < query_string_length && query_string[i] == '=') { i++; } // Get Value i0 = i; while ((i < query_string_length) && query_string[i] != '&') { i++; } auto value = std::string(); CivetServer::urlDecode(&query_string[i0], i - i0, value, true); if (i < query_string_length && query_string[i] == '&') { i++; } // Add key-value pair to the map params[name] = value; } return true; } /** * @brief Gets a URI header given its name. * * @param Name of the header to be retrieved. * @return Gets the value of an HTTP header given its name. */ const char *get_header(const char *name) const { const char *value = mg_get_header(conn_, name); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( value != nullptr, msg_server_error_get_header); return value; } /** * @brief This method saves the body of the HTTP request in the body buffer. * * @param [out] Body of the HTTP request. * @param [in,out] Length of the body of the HTTP request. */ void get_body(char *& body, size_t& body_length) const { auto mg_result = mg_read(conn_, body, body_length); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( mg_result >= 0, msg_server_error_read); } /** * @brief Saves the body of the HTTP request in a string buffer. * * @param [out] Body of the HTTP request in StringBuffer. */ void get_body(StringBuffer& body) const { const auto body_length = get_body_length(); body.ensure_capacity(body_length + 1); auto mg_result = mg_read(conn_, body.c_str_ptr(), body_length); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( mg_result >= 0, msg_server_error_read); } /** * @brief Returns the length of the body of the HTTP request. * * @return Body length. */ size_t get_body_length() const { const auto req = mg_get_request_info(conn_); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( req != nullptr, msg_server_error_get_request_info); auto content_length = req->content_length; // Check that the content_length is not negative (except for GET, // DELETE, and PUT operations) where it is permitted. if ((content_length < 0) && (strcmp(req->request_method, "GET") != 0) && (strcmp(req->request_method, "DELETE") != 0) && (strcmp(req->request_method, "PUT") != 0)) { RTI_THROW_HTTP_EXCEPTION( StatusCode::bad_request, &RTI_LOG_ANY_FAILURE_s, "Content-length of the HTTP request is invalid."); } return content_length < 0 ? 0 : static_cast(content_length); } /** * @brief Returns the method of the HTTP request. * * @return Method of the HTTP request. */ Method get_http_method() const { const struct mg_request_info *req = mg_get_request_info(conn_); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( req != nullptr, msg_server_error_get_request_info); return method_from_string( req->request_method == nullptr ? "" : req->request_method); } private: struct mg_connection *conn_; }; }}}} // namespace rti::apputils::http::server #endif // HTTP_SERVER_REQUEST_HPP