293 lines
8.9 KiB
C++
293 lines
8.9 KiB
C++
/*
|
|
* (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 <CivetServer.h>
|
|
#include <civetweb.h>
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#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<unsigned short>(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<unsigned short>(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<unsigned short>(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<unsigned short>(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<unsigned short>(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<unsigned short>(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<unsigned short>(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<unsigned short>(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<std::size_t>(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
|