/* * (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 #include #include #include #include #include #include "HttpException.hpp" #include "StatusCode.hpp" namespace rti { namespace apputils { namespace http { using StringMap = std::unordered_map; // 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(in[i]); int b = i + 1 >= in.size() ? 0 : static_cast(in[i + 1]); int c = i + 2 >= in.size() ? 0 : static_cast(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