168 lines
4.9 KiB
C++
168 lines
4.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_UTILS_H
|
|
#define HTTP_UTILS_H
|
|
|
|
#include <ctime>
|
|
#include <cstring>
|
|
#include <unordered_map>
|
|
|
|
#include <civetweb.h>
|
|
|
|
#include <reda/reda_string.h>
|
|
#include <dds_c/dds_c_printformat.h>
|
|
|
|
#include "HttpException.hpp"
|
|
#include "StatusCode.hpp"
|
|
|
|
namespace rti { namespace apputils { namespace http {
|
|
|
|
using StringMap = std::unordered_map<std::string, std::string>;
|
|
|
|
// Unfortunately Civetweb doesn't expose these constants, so we needed to
|
|
// copy the values from civetweb.c (static struct mg_option config_options[])
|
|
constexpr auto HTTP_CLIENT_TIMEOUT_MS = int { 1000 };
|
|
constexpr auto HTTP_SERVER_DEFAULT_PORT = int { 8080 };
|
|
constexpr auto HTTP_SERVER_DEFAULT_PORT_STR = "8080";
|
|
constexpr auto HTTP_SERVER_DEFAULT_HOST = "127.0.0.1";
|
|
constexpr auto HTTP_VERSION = "1.1";
|
|
constexpr auto HTTP_CACHE_CONTROL_NO_CACHE = "no-cache";
|
|
constexpr auto HTTP_BODY_INITIAL_LENGTH = std::size_t { 1000 };
|
|
constexpr auto HTTP_PLAIN_TEXT_CONTENT_TYPE = "text/plain";
|
|
constexpr auto HTTP_JSON_CONTENT_TYPE = "application/json";
|
|
constexpr auto HTTP_XML_CONTENT_TYPE = "application/xml";
|
|
constexpr auto HTTP_CONTENT_TYPE_HEADER = "Content-Type";
|
|
constexpr auto HTTP_DATE_STR_LENGTH = std::size_t { 64 };
|
|
|
|
/**
|
|
* Maximum URL length we handle is 2048. This value is based on the
|
|
* comments on this article http://stackoverflow.com/questions/417142
|
|
*/
|
|
constexpr auto MAX_URI_LENGTH = int { 2048 };
|
|
|
|
/**
|
|
* @brief This structure contains utility methods to generate HttpSettings that
|
|
* need to be set as part of the HTTP response.
|
|
*/
|
|
struct HttpSettings {
|
|
/**
|
|
* @brief This method converts time_t to a string. According to RFC2616,
|
|
* Sec 14.18, this must be included in all responses other than
|
|
* 100, 101, 5xx.
|
|
*
|
|
* @return A string containing the date in the format specified by RFC2616.
|
|
*/
|
|
static std::string get_current_date()
|
|
{
|
|
const auto curtime = std::time(nullptr);
|
|
if (curtime == (time_t) -1) {
|
|
return "Thu, 01 Jan 1970 00:00:00 GMT";
|
|
}
|
|
|
|
struct tm curr_tm;
|
|
#ifdef RTI_WIN32
|
|
if (gmtime_s(&curr_tm, &curtime) != 0) {
|
|
return "Thu, 01 Jan 1970 00:00:00 GMT";
|
|
}
|
|
#else
|
|
if (gmtime_r(&curtime, &curr_tm) != &curr_tm) {
|
|
return "Thu, 01 Jan 1970 00:00:00 GMT";
|
|
}
|
|
#endif
|
|
|
|
auto result = std::string(HTTP_DATE_STR_LENGTH, '\0');
|
|
const auto bytes_written = std::strftime(
|
|
&result[0],
|
|
result.size(),
|
|
"%a, %d %b %Y %H:%M:%S GMT",
|
|
&curr_tm);
|
|
|
|
RTI_SERVICE_CHECK_CONDITION_W_EXTRA_INFO(
|
|
bytes_written > 0,
|
|
"Error in std::strftime");
|
|
|
|
result.resize(bytes_written);
|
|
return result;
|
|
}
|
|
};
|
|
|
|
inline std::string read_content_with_content_length(
|
|
mg_connection *connection,
|
|
std::size_t content_length)
|
|
{
|
|
auto content = std::string(content_length, 0);
|
|
|
|
auto result = 0;
|
|
while (content_length > 0 && result >= 0) {
|
|
result = mg_read(connection,
|
|
&content[content.size() - content_length],
|
|
content_length);
|
|
|
|
content_length -= result;
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
inline std::string read_content_without_content_length(
|
|
mg_connection *connection,
|
|
std::size_t chunk_size = 256)
|
|
{
|
|
auto content = std::string();
|
|
auto bytes_read = std::size_t { 0 };
|
|
auto result = 0;
|
|
|
|
do {
|
|
bytes_read += result;
|
|
content.resize(bytes_read + chunk_size);
|
|
result = mg_read(connection, &content[bytes_read], chunk_size);
|
|
} while (result > 0);
|
|
|
|
content.resize(bytes_read);
|
|
return content;
|
|
}
|
|
|
|
inline std::string base64_encode(const std::string& in)
|
|
{
|
|
std::string out;
|
|
out.reserve(in.size() * 2);
|
|
|
|
const char *b64 =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
for (std::size_t i = 0; i < in.size(); i += 3) {
|
|
// Static cast to unsigned char to avoid cert_str34_c_violation
|
|
int a = static_cast<unsigned char>(in[i]);
|
|
int b = i + 1 >= in.size() ? 0 : static_cast<unsigned char>(in[i + 1]);
|
|
int c = i + 2 >= in.size() ? 0 : static_cast<unsigned char>(in[i + 2]);
|
|
|
|
out.push_back(b64[a >> 2]);
|
|
out.push_back(b64[((a & 3) << 4) | (b >> 4)]);
|
|
if (i + 1 < in.size()) {
|
|
out.push_back(b64[(b & 15) << 2 | (c >> 6)]);
|
|
}
|
|
if (i + 2 < in.size()) {
|
|
out.push_back(b64[c & 63]);
|
|
}
|
|
}
|
|
|
|
while (out.size() % 4 != 0) {
|
|
out.push_back('=');
|
|
}
|
|
|
|
out.shrink_to_fit();
|
|
return out;
|
|
}
|
|
|
|
}}} // namespace rti::apputils::http
|
|
|
|
#endif // HTTP_UTILS_H
|