From 8fe7e98aaac06057161e09c3fe3bb995fd10083e Mon Sep 17 00:00:00 2001 From: Albert S Date: Fri, 3 May 2019 23:33:57 +0200 Subject: [PATCH] sync httplib.h with its current master, but replace std::regex with boost:regex again --- gateway/httplib.h | 196 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 153 insertions(+), 43 deletions(-) diff --git a/gateway/httplib.h b/gateway/httplib.h index 82b48ad..7fe82e7 100644 --- a/gateway/httplib.h +++ b/gateway/httplib.h @@ -1,7 +1,7 @@ // // httplib.h -// Copyright (c) 2019 Albert S. All rights reserved. -// Copyright (c) 2017 Yuji Hirose. All rights reserved. +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. // MIT License // @@ -29,32 +29,38 @@ #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) #endif // S_ISDIR +#ifndef NOMINMAX #define NOMINMAX +#endif // NOMINMAX #include #include #include +#pragma comment(lib, "ws2_32.lib") + #ifndef strcasecmp #define strcasecmp _stricmp #endif // strcasecmp typedef SOCKET socket_t; #else -#include -#include -#include -#include -#include #include +#include +#include +#include +#include #include -#include #include +#include +#include typedef int socket_t; #define INVALID_SOCKET (-1) #endif //_WIN32 +#include +#include #include #include #include @@ -62,12 +68,11 @@ typedef int socket_t; #include #include #include -#include #include -#include -#include +#include #ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include #include #endif @@ -80,6 +85,9 @@ typedef int socket_t; */ #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH std::numeric_limits::max() namespace httplib { @@ -237,6 +245,7 @@ class Server Server &Post(const char *pattern, Handler handler); Server &Put(const char *pattern, Handler handler); + Server &Patch(const char *pattern, Handler handler); Server &Delete(const char *pattern, Handler handler); Server &Options(const char *pattern, Handler handler); @@ -246,6 +255,7 @@ class Server void set_logger(Logger logger); void set_keep_alive_max_count(size_t count); + void set_payload_max_length(uint64_t length); int bind_to_any_port(const char *host, int socket_flags = 0); bool listen_after_bind(); @@ -259,6 +269,7 @@ class Server bool process_request(Stream &strm, bool last_connection, bool &connection_close); size_t keep_alive_max_count_; + size_t payload_max_length_; private: typedef std::vector> Handlers; @@ -282,6 +293,7 @@ class Server Handlers get_handlers_; Handlers post_handlers_; Handlers put_handlers_; + Handlers patch_handlers_; Handlers delete_handlers_; Handlers options_handlers_; Handler error_handler_; @@ -318,6 +330,10 @@ class Client std::shared_ptr Put(const char *path, const Headers &headers, const std::string &body, const char *content_type); + std::shared_ptr Patch(const char *path, const std::string &body, const char *content_type); + std::shared_ptr Patch(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + std::shared_ptr Delete(const char *path); std::shared_ptr Delete(const char *path, const Headers &headers); @@ -443,6 +459,18 @@ class stream_line_reader } } + size_t size() const + { + if(glowable_buffer_.empty()) + { + return fixed_buffer_used_size_; + } + else + { + return glowable_buffer_.size(); + } + } + bool getline() { fixed_buffer_used_size_ = 0; @@ -858,6 +886,10 @@ inline const char *status_message(int status) return "Forbidden"; case 404: return "Not Found"; + case 413: + return "Payload Too Large"; + case 414: + return "Request-URI Too Long"; case 415: return "Unsupported Media Type"; default: @@ -882,12 +914,12 @@ inline const char *get_header_value(const Headers &headers, const char *key, siz return def; } -inline int get_header_value_int(const Headers &headers, const char *key, int def = 0) +inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, int def = 0) { auto it = headers.find(key); if(it != headers.end()) { - return std::stoi(it->second); + return std::strtoull(it->second.data(), nullptr, 10); } return def; } @@ -1021,11 +1053,13 @@ inline bool read_content_chunked(Stream &strm, std::string &out) return true; } -template bool read_content(Stream &strm, T &x, Progress progress = Progress()) +template +bool read_content(Stream &strm, T &x, uint64_t payload_max_length, bool &exceed_payload_max_length, + Progress progress = Progress()) { if(has_header(x.headers, "Content-Length")) { - auto len = get_header_value_int(x.headers, "Content-Length", 0); + auto len = get_header_value_uint64(x.headers, "Content-Length", 0); if(len == 0) { const auto &encoding = get_header_value(x.headers, "Transfer-Encoding", 0, ""); @@ -1034,6 +1068,15 @@ template bool read_content(Stream &strm, T &x, Progress progress = return read_content_chunked(strm, x.body); } } + + if((len > payload_max_length) || + // For 32-bit platform + (sizeof(size_t) < sizeof(uint64_t) && len > std::numeric_limits::max())) + { + exceed_payload_max_length = true; + return false; + } + return read_content_with_length(strm, x.body, len, progress); } else @@ -1071,6 +1114,12 @@ inline std::string encode_url(const std::string &s) case '+': result += "%2B"; break; + case '\r': + result += "%0D"; + break; + case '\n': + result += "%0A"; + break; case '\'': result += "%27"; break; @@ -1470,9 +1519,9 @@ inline void decompress(std::string &content) strm.zfree = Z_NULL; strm.opaque = Z_NULL; - // 15 is the value of wbits, which should be at the maximum possible value to ensure - // that any gzip stream can be decoded. The offset of 16 specifies that the stream - // to decompress will be formatted with a gzip wrapper. + // 15 is the value of wbits, which should be at the maximum possible value to + // ensure that any gzip stream can be decoded. The offset of 16 specifies that + // the stream to decompress will be formatted with a gzip wrapper. auto ret = inflateInit2(&strm, 16 + 15); if(ret != Z_OK) { @@ -1728,7 +1777,9 @@ inline const std::string &BufferStream::get_buffer() const } // HTTP server implementation -inline Server::Server() : keep_alive_max_count_(5), is_running_(false), svr_sock_(INVALID_SOCKET), running_threads_(0) +inline Server::Server() + : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), + is_running_(false), svr_sock_(INVALID_SOCKET), running_threads_(0) { #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); @@ -1757,6 +1808,12 @@ inline Server &Server::Put(const char *pattern, Handler handler) return *this; } +inline Server &Server::Patch(const char *pattern, Handler handler) +{ + patch_handlers_.push_back(std::make_pair(boost::regex(pattern), handler)); + return *this; +} + inline Server &Server::Delete(const char *pattern, Handler handler) { delete_handlers_.push_back(std::make_pair(boost::regex(pattern), handler)); @@ -1794,6 +1851,11 @@ inline void Server::set_keep_alive_max_count(size_t count) keep_alive_max_count_ = count; } +inline void Server::set_payload_max_length(uint64_t length) +{ + payload_max_length_ = length; +} + inline int Server::bind_to_any_port(const char *host, int socket_flags) { return bind_internal(host, 0, socket_flags); @@ -1830,7 +1892,8 @@ inline void Server::stop() inline bool Server::parse_request_line(const char *s, Request &req) { - static boost::regex re("(GET|HEAD|POST|PUT|DELETE|OPTIONS) (([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); + static boost::regex re("(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS) " + "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); boost::cmatch m; if(boost::regex_match(s, m, re)) @@ -2119,6 +2182,10 @@ inline bool Server::routing(Request &req, Response &res) { return dispatch_request(req, res, put_handlers_); } + else if(req.method == "PATCH") + { + return dispatch_request(req, res, patch_handlers_); + } else if(req.method == "DELETE") { return dispatch_request(req, res, delete_handlers_); @@ -2164,6 +2231,14 @@ inline bool Server::process_request(Stream &strm, bool last_connection, bool &co res.version = "HTTP/1.1"; + // Check if the request URI doesn't exceed the limit + if(reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) + { + res.status = 414; + write_response(strm, last_connection, req, res); + return true; + } + // Request line and headers if(!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { @@ -2180,13 +2255,14 @@ inline bool Server::process_request(Stream &strm, bool last_connection, bool &co req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); // Body - if(req.method == "POST" || req.method == "PUT") + if(req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { - if(!detail::read_content(strm, req)) + bool exceed_payload_max_length = false; + if(!detail::read_content(strm, req, payload_max_length_, exceed_payload_max_length)) { - res.status = 400; + res.status = exceed_payload_max_length ? 413 : 400; write_response(strm, last_connection, req, res); - return true; + return !exceed_payload_max_length; } const auto &content_type = req.get_header_value("Content-Type"); @@ -2333,26 +2409,29 @@ inline void Client::write_request(Stream &strm, Request &req) bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); // Headers - if(is_ssl()) + if(!req.has_header("Host")) { - if(port_ == 443) + if(is_ssl()) { - req.set_header("Host", host_.c_str()); + if(port_ == 443) + { + req.set_header("Host", host_.c_str()); + } + else + { + req.set_header("Host", host_and_port_.c_str()); + } } else { - req.set_header("Host", host_and_port_.c_str()); - } - } - else - { - if(port_ == 80) - { - req.set_header("Host", host_.c_str()); - } - else - { - req.set_header("Host", host_and_port_.c_str()); + if(port_ == 80) + { + req.set_header("Host", host_.c_str()); + } + else + { + req.set_header("Host", host_and_port_.c_str()); + } } } @@ -2373,7 +2452,7 @@ inline void Client::write_request(Stream &strm, Request &req) if(req.body.empty()) { - if(req.method == "POST" || req.method == "PUT") + if(req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { req.set_header("Content-Length", "0"); } @@ -2385,8 +2464,11 @@ inline void Client::write_request(Stream &strm, Request &req) req.set_header("Content-Type", "text/plain"); } - auto length = std::to_string(req.body.size()); - req.set_header("Content-Length", length.c_str()); + if(!req.has_header("Content-Length")) + { + auto length = std::to_string(req.body.size()); + req.set_header("Content-Length", length.c_str()); + } } detail::write_headers(bstrm, req); @@ -2421,7 +2503,9 @@ inline bool Client::process_request(Stream &strm, Request &req, Response &res, b // Body if(req.method != "HEAD") { - if(!detail::read_content(strm, res, req.progress)) + bool exceed_payload_max_length = false; + if(!detail::read_content(strm, res, std::numeric_limits::max(), exceed_payload_max_length, + req.progress)) { return false; } @@ -2550,6 +2634,27 @@ inline std::shared_ptr Client::Put(const char *path, const Headers &he return send(req, *res) ? res : nullptr; } +inline std::shared_ptr Client::Patch(const char *path, const std::string &body, const char *content_type) +{ + return Patch(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Patch(const char *path, const Headers &headers, const std::string &body, + const char *content_type) +{ + Request req; + req.method = "PATCH"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + inline std::shared_ptr Client::Delete(const char *path) { return Delete(path, Headers()); @@ -2663,6 +2768,11 @@ class SSLInit SSL_load_error_strings(); SSL_library_init(); } + + ~SSLInit() + { + ERR_free_strings(); + } }; static SSLInit sslinit_;