sync httplib.h with its current master, but replace std::regex with boost:regex again

This commit is contained in:
Albert S. 2019-05-03 23:33:57 +02:00
parent b751e0ba92
commit 8fe7e98aaa

View File

@ -1,7 +1,7 @@
// //
// httplib.h // 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 // MIT License
// //
@ -29,32 +29,38 @@
#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
#endif // S_ISDIR #endif // S_ISDIR
#ifndef NOMINMAX
#define NOMINMAX #define NOMINMAX
#endif // NOMINMAX
#include <io.h> #include <io.h>
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#ifndef strcasecmp #ifndef strcasecmp
#define strcasecmp _stricmp #define strcasecmp _stricmp
#endif // strcasecmp #endif // strcasecmp
typedef SOCKET socket_t; typedef SOCKET socket_t;
#else #else
#include <pthread.h>
#include <unistd.h>
#include <netdb.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <cstring>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h> #include <signal.h>
#include <sys/socket.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
typedef int socket_t; typedef int socket_t;
#define INVALID_SOCKET (-1) #define INVALID_SOCKET (-1)
#endif //_WIN32 #endif //_WIN32
#include <assert.h>
#include <fcntl.h>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <map> #include <map>
@ -62,12 +68,11 @@ typedef int socket_t;
#include <mutex> #include <mutex>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <string> #include <string>
#include <thread>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <thread>
#include <assert.h>
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#include <openssl/err.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#endif #endif
@ -80,6 +85,9 @@ typedef int socket_t;
*/ */
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 #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<uint64_t>::max()
namespace httplib namespace httplib
{ {
@ -237,6 +245,7 @@ class Server
Server &Post(const char *pattern, Handler handler); Server &Post(const char *pattern, Handler handler);
Server &Put(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 &Delete(const char *pattern, Handler handler);
Server &Options(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_logger(Logger logger);
void set_keep_alive_max_count(size_t count); 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); int bind_to_any_port(const char *host, int socket_flags = 0);
bool listen_after_bind(); bool listen_after_bind();
@ -259,6 +269,7 @@ class Server
bool process_request(Stream &strm, bool last_connection, bool &connection_close); bool process_request(Stream &strm, bool last_connection, bool &connection_close);
size_t keep_alive_max_count_; size_t keep_alive_max_count_;
size_t payload_max_length_;
private: private:
typedef std::vector<std::pair<boost::regex, Handler>> Handlers; typedef std::vector<std::pair<boost::regex, Handler>> Handlers;
@ -282,6 +293,7 @@ class Server
Handlers get_handlers_; Handlers get_handlers_;
Handlers post_handlers_; Handlers post_handlers_;
Handlers put_handlers_; Handlers put_handlers_;
Handlers patch_handlers_;
Handlers delete_handlers_; Handlers delete_handlers_;
Handlers options_handlers_; Handlers options_handlers_;
Handler error_handler_; Handler error_handler_;
@ -318,6 +330,10 @@ class Client
std::shared_ptr<Response> Put(const char *path, const Headers &headers, const std::string &body, std::shared_ptr<Response> Put(const char *path, const Headers &headers, const std::string &body,
const char *content_type); const char *content_type);
std::shared_ptr<Response> Patch(const char *path, const std::string &body, const char *content_type);
std::shared_ptr<Response> Patch(const char *path, const Headers &headers, const std::string &body,
const char *content_type);
std::shared_ptr<Response> Delete(const char *path); std::shared_ptr<Response> Delete(const char *path);
std::shared_ptr<Response> Delete(const char *path, const Headers &headers); std::shared_ptr<Response> 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() bool getline()
{ {
fixed_buffer_used_size_ = 0; fixed_buffer_used_size_ = 0;
@ -858,6 +886,10 @@ inline const char *status_message(int status)
return "Forbidden"; return "Forbidden";
case 404: case 404:
return "Not Found"; return "Not Found";
case 413:
return "Payload Too Large";
case 414:
return "Request-URI Too Long";
case 415: case 415:
return "Unsupported Media Type"; return "Unsupported Media Type";
default: default:
@ -882,12 +914,12 @@ inline const char *get_header_value(const Headers &headers, const char *key, siz
return def; 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); auto it = headers.find(key);
if(it != headers.end()) if(it != headers.end())
{ {
return std::stoi(it->second); return std::strtoull(it->second.data(), nullptr, 10);
} }
return def; return def;
} }
@ -1021,11 +1053,13 @@ inline bool read_content_chunked(Stream &strm, std::string &out)
return true; return true;
} }
template <typename T> bool read_content(Stream &strm, T &x, Progress progress = Progress()) template <typename T>
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")) 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) if(len == 0)
{ {
const auto &encoding = get_header_value(x.headers, "Transfer-Encoding", 0, ""); const auto &encoding = get_header_value(x.headers, "Transfer-Encoding", 0, "");
@ -1034,6 +1068,15 @@ template <typename T> bool read_content(Stream &strm, T &x, Progress progress =
return read_content_chunked(strm, x.body); 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<size_t>::max()))
{
exceed_payload_max_length = true;
return false;
}
return read_content_with_length(strm, x.body, len, progress); return read_content_with_length(strm, x.body, len, progress);
} }
else else
@ -1071,6 +1114,12 @@ inline std::string encode_url(const std::string &s)
case '+': case '+':
result += "%2B"; result += "%2B";
break; break;
case '\r':
result += "%0D";
break;
case '\n':
result += "%0A";
break;
case '\'': case '\'':
result += "%27"; result += "%27";
break; break;
@ -1470,9 +1519,9 @@ inline void decompress(std::string &content)
strm.zfree = Z_NULL; strm.zfree = Z_NULL;
strm.opaque = Z_NULL; strm.opaque = Z_NULL;
// 15 is the value of wbits, which should be at the maximum possible value to ensure // 15 is the value of wbits, which should be at the maximum possible value to
// that any gzip stream can be decoded. The offset of 16 specifies that the stream // ensure that any gzip stream can be decoded. The offset of 16 specifies that
// to decompress will be formatted with a gzip wrapper. // the stream to decompress will be formatted with a gzip wrapper.
auto ret = inflateInit2(&strm, 16 + 15); auto ret = inflateInit2(&strm, 16 + 15);
if(ret != Z_OK) if(ret != Z_OK)
{ {
@ -1728,7 +1777,9 @@ inline const std::string &BufferStream::get_buffer() const
} }
// HTTP server implementation // 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 #ifndef _WIN32
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
@ -1757,6 +1808,12 @@ inline Server &Server::Put(const char *pattern, Handler handler)
return *this; 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) inline Server &Server::Delete(const char *pattern, Handler handler)
{ {
delete_handlers_.push_back(std::make_pair(boost::regex(pattern), 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; 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) inline int Server::bind_to_any_port(const char *host, int socket_flags)
{ {
return bind_internal(host, 0, 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) 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; boost::cmatch m;
if(boost::regex_match(s, m, re)) 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_); return dispatch_request(req, res, put_handlers_);
} }
else if(req.method == "PATCH")
{
return dispatch_request(req, res, patch_handlers_);
}
else if(req.method == "DELETE") else if(req.method == "DELETE")
{ {
return dispatch_request(req, res, delete_handlers_); 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"; 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 // Request line and headers
if(!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.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()); req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str());
// Body // 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); write_response(strm, last_connection, req, res);
return true; return !exceed_payload_max_length;
} }
const auto &content_type = req.get_header_value("Content-Type"); 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()); bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
// Headers // 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 else
{ {
req.set_header("Host", host_and_port_.c_str()); if(port_ == 80)
} {
} req.set_header("Host", host_.c_str());
else }
{ else
if(port_ == 80) {
{ req.set_header("Host", host_and_port_.c_str());
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.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"); 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"); req.set_header("Content-Type", "text/plain");
} }
auto length = std::to_string(req.body.size()); if(!req.has_header("Content-Length"))
req.set_header("Content-Length", length.c_str()); {
auto length = std::to_string(req.body.size());
req.set_header("Content-Length", length.c_str());
}
} }
detail::write_headers(bstrm, req); detail::write_headers(bstrm, req);
@ -2421,7 +2503,9 @@ inline bool Client::process_request(Stream &strm, Request &req, Response &res, b
// Body // Body
if(req.method != "HEAD") 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<uint64_t>::max(), exceed_payload_max_length,
req.progress))
{ {
return false; return false;
} }
@ -2550,6 +2634,27 @@ inline std::shared_ptr<Response> Client::Put(const char *path, const Headers &he
return send(req, *res) ? res : nullptr; return send(req, *res) ? res : nullptr;
} }
inline std::shared_ptr<Response> Client::Patch(const char *path, const std::string &body, const char *content_type)
{
return Patch(path, Headers(), body, content_type);
}
inline std::shared_ptr<Response> 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<Response>();
return send(req, *res) ? res : nullptr;
}
inline std::shared_ptr<Response> Client::Delete(const char *path) inline std::shared_ptr<Response> Client::Delete(const char *path)
{ {
return Delete(path, Headers()); return Delete(path, Headers());
@ -2663,6 +2768,11 @@ class SSLInit
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
} }
~SSLInit()
{
ERR_free_strings();
}
}; };
static SSLInit sslinit_; static SSLInit sslinit_;