/* * (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_CONNECTION_HPP #define HTTP_SERVER_CONNECTION_HPP #include #include #include #include "rti/apputils/http/common/HttpUtils.hpp" #include "rti/apputils/http/common/Response.hpp" #include "rti/apputils/http/log/Log.hpp" #include "log.peer.h" #include "Request.hpp" namespace rti { namespace apputils { namespace http { namespace server { /** * @brief This class implements the interfaces defined by rest::Connection to * deal with CivetWeb's native connections. */ class Connection { public: /** * @brief Constructor of the Connection class. It saves a reference to * CivetWeb's native connection. It also constructs a Request from * the connection passed by parameter. * * @param conn CivetWeb's connection that is wrapped in this class. */ explicit Connection(mg_connection *conn) : conn_(conn), request_(conn_) { } /** * Destructor of the Connection class. */ ~Connection() { } /** * @brief This method returns the HTTP request. * * @return The HTTP Request. */ const Request& request() const { return request_; } /** * @brief This method returns the CivetWeb's native connection. * * @return The CivetWeb's native connection. */ mg_connection *connection() const { return conn_; } /** * @brief This method sends an "Error 501: Not Implemented" response. */ void send_not_implemented(std::string const& message = std::string()) { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(StatusCode::not_implemented); response.http_reason_phrase = status_code_to_string(StatusCode::not_implemented); response.http_content = message; this->send_http_response(response); } /** * @brief This method sends an "OK 204: No Content" response. */ void send_http_ok_no_content() { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(StatusCode::no_content); response.http_reason_phrase = status_code_to_string(StatusCode::no_content); this->send_http_response(response); } /** * @brief This method sends an "Error 400: Bad Request" response. */ void send_http_bad_request(std::string const& message = std::string()) { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(StatusCode::bad_request); response.http_reason_phrase = status_code_to_string(StatusCode::bad_request); response.http_content = message; this->send_http_response(response); } /** * @brief This method sends an "Error 404: Not Found" response. */ void send_http_not_found(std::string const& message = std::string()) { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(StatusCode::not_found); response.http_reason_phrase = status_code_to_string(StatusCode::not_found); response.http_content = message; this->send_http_response(response); } /** * @brief This method sends an "Error 405: Method Not Allowed" response. */ void send_http_method_not_allowed( std::string const& message = std::string()) { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(StatusCode::not_allowed); response.http_reason_phrase = status_code_to_string(StatusCode::not_allowed); response.http_content = message; this->send_http_response(response); } /** * @brief This method sends an "OK 202: Accepted" response. */ void send_http_accepted(std::string const& message = std::string()) { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(StatusCode::accepted); response.http_reason_phrase = status_code_to_string(StatusCode::accepted); response.http_content = message; this->send_http_response(response); } /** * @brief This method sends an "OK 200: OK" response. */ void send_http_ok(std::string const& message = std::string()) { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(StatusCode::ok); response.http_reason_phrase = status_code_to_string(StatusCode::ok); response.http_content = message; this->send_http_response(response); } /** * @brief This operation constructs an HTTP response given its definition in * Response format. Once the Response is constructed, is sent via CivetWeb's * native APIs. */ void send_http_response(const Response& rpns) { // Constructs headers and body of the HTTP response. auto mg_result = mg_printf( conn_, "HTTP/%s %u %s\r\n" "Content-Type: %s\r\n" "Cache-Control: %s\r\n" "Content-Length: %lu\r\n" "Access-Control-Allow-Origin: *\r\n" "Date: %s\r\n\r\n" "%s", HTTP_VERSION, rpns.http_status_code, rpns.http_reason_phrase.c_str(), rpns.http_content_type.c_str(), HTTP_CACHE_CONTROL_NO_CACHE, rpns.http_content.length(), HttpSettings::get_current_date().c_str(), rpns.http_content.c_str()); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( mg_result >= 0, msg_server_error_printf); APPUTILSLogMessage_log( RTI_LOG_BIT_LOCAL, &APPUTILS_LOG_HTTP_SENT_RESPONSE_us, rpns.http_status_code, rpns.http_reason_phrase.c_str()); } /** * @brief Prepares and send the body of an HTTP response given an exception. * * @param [in] e Exception to be formatted and sent. */ void send_error_exception_response(const HttpException& e) { auto response = Response(); response.http_content_type = HTTP_JSON_CONTENT_TYPE; response.http_status_code = static_cast(e.error_code()); response.http_reason_phrase = status_code_to_string(e.error_code()); nlohmann::json http_content_json; http_content_json["code"] = status_code_to_string(e.error_code()); http_content_json["message"] = e.what(); response.http_content = http_content_json.dump(); this->send_http_response(response); } /** * @brief Read the content of an HTTP request. * * @param chunk_size size of the chunk to read. * @return std::string with the read content. */ std::string read_content(std::size_t chunk_size = 256) { const auto request_info = mg_get_request_info(conn_); RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO( request_info != nullptr, msg_server_error_get_request_info); if (request_info->content_length >= 0) { return read_content_with_content_length( conn_, static_cast(request_info->content_length)); } return read_content_without_content_length(conn_, chunk_size); } /** * @brief Check that content type is the expected one. * * @param expected_content_type * @return true if the content type is the expected one. otherwise * false. */ bool check_content_type(const std::string& expected_content_type) { auto ret = true; // Check if Content-Type is application/json if (this->request().get_header( rti::apputils::http::HTTP_CONTENT_TYPE_HEADER) != expected_content_type) { ret = false; } return ret; } private: mg_connection *conn_; Request request_; }; }}}} // namespace rti::apputils::http::server #endif // HTTP_SERVER_CONNECTION_HPP