|
|
|
@@ -41,7 +41,7 @@
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
|
|
|
|
|
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max())
|
|
|
|
|
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_RECV_BUFSIZ
|
|
|
|
@@ -50,7 +50,7 @@
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
|
|
|
|
|
#define CPPHTTPLIB_THREAD_POOL_COUNT \
|
|
|
|
|
(std::max(1u, std::thread::hardware_concurrency() - 1))
|
|
|
|
|
((std::max)(1u, std::thread::hardware_concurrency() - 1))
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -156,6 +156,7 @@ using socket_t = int;
|
|
|
|
|
#include <openssl/x509v3.h>
|
|
|
|
|
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
|
|
// #if OPENSSL_VERSION_NUMBER < 0x1010100fL
|
|
|
|
@@ -313,7 +314,7 @@ struct Response {
|
|
|
|
|
|
|
|
|
|
void set_redirect(const char *url);
|
|
|
|
|
void set_content(const char *s, size_t n, const char *content_type);
|
|
|
|
|
void set_content(const std::string &s, const char *content_type);
|
|
|
|
|
void set_content(std::string s, const char *content_type);
|
|
|
|
|
|
|
|
|
|
void set_content_provider(
|
|
|
|
|
size_t length,
|
|
|
|
@@ -349,14 +350,14 @@ public:
|
|
|
|
|
virtual bool is_readable() const = 0;
|
|
|
|
|
virtual bool is_writable() const = 0;
|
|
|
|
|
|
|
|
|
|
virtual int read(char *ptr, size_t size) = 0;
|
|
|
|
|
virtual int write(const char *ptr, size_t size) = 0;
|
|
|
|
|
virtual ssize_t read(char *ptr, size_t size) = 0;
|
|
|
|
|
virtual ssize_t write(const char *ptr, size_t size) = 0;
|
|
|
|
|
virtual std::string get_remote_addr() const = 0;
|
|
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
int write_format(const char *fmt, const Args &... args);
|
|
|
|
|
int write(const char *ptr);
|
|
|
|
|
int write(const std::string &s);
|
|
|
|
|
ssize_t write_format(const char *fmt, const Args &... args);
|
|
|
|
|
ssize_t write(const char *ptr);
|
|
|
|
|
ssize_t write(const std::string &s);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TaskQueue {
|
|
|
|
@@ -511,7 +512,7 @@ private:
|
|
|
|
|
int bind_internal(const char *host, int port, int socket_flags);
|
|
|
|
|
bool listen_internal();
|
|
|
|
|
|
|
|
|
|
bool routing(Request &req, Response &res, Stream &strm, bool last_connection);
|
|
|
|
|
bool routing(Request &req, Response &res, Stream &strm);
|
|
|
|
|
bool handle_file_request(Request &req, Response &res, bool head = false);
|
|
|
|
|
bool dispatch_request(Request &req, Response &res, Handlers &handlers);
|
|
|
|
|
bool dispatch_request_for_content_reader(Request &req, Response &res,
|
|
|
|
@@ -524,14 +525,14 @@ private:
|
|
|
|
|
bool write_content_with_provider(Stream &strm, const Request &req,
|
|
|
|
|
Response &res, const std::string &boundary,
|
|
|
|
|
const std::string &content_type);
|
|
|
|
|
bool read_content(Stream &strm, bool last_connection, Request &req,
|
|
|
|
|
Response &res);
|
|
|
|
|
bool read_content_with_content_receiver(
|
|
|
|
|
Stream &strm, bool last_connection, Request &req, Response &res,
|
|
|
|
|
ContentReceiver receiver, MultipartContentHeader multipart_header,
|
|
|
|
|
ContentReceiver multipart_receiver);
|
|
|
|
|
bool read_content_core(Stream &strm, bool last_connection, Request &req,
|
|
|
|
|
Response &res, ContentReceiver receiver,
|
|
|
|
|
bool read_content(Stream &strm, Request &req, Response &res);
|
|
|
|
|
bool
|
|
|
|
|
read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
|
|
|
|
|
ContentReceiver receiver,
|
|
|
|
|
MultipartContentHeader multipart_header,
|
|
|
|
|
ContentReceiver multipart_receiver);
|
|
|
|
|
bool read_content_core(Stream &strm, Request &req, Response &res,
|
|
|
|
|
ContentReceiver receiver,
|
|
|
|
|
MultipartContentHeader mulitpart_header,
|
|
|
|
|
ContentReceiver multipart_receiver);
|
|
|
|
|
|
|
|
|
@@ -845,12 +846,12 @@ public:
|
|
|
|
|
const char *client_ca_cert_file_path = nullptr,
|
|
|
|
|
const char *client_ca_cert_dir_path = nullptr);
|
|
|
|
|
|
|
|
|
|
virtual ~SSLServer();
|
|
|
|
|
~SSLServer() override;
|
|
|
|
|
|
|
|
|
|
virtual bool is_valid() const;
|
|
|
|
|
bool is_valid() const override;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
virtual bool process_and_close_socket(socket_t sock);
|
|
|
|
|
bool process_and_close_socket(socket_t sock) override;
|
|
|
|
|
|
|
|
|
|
SSL_CTX *ctx_;
|
|
|
|
|
std::mutex ctx_mutex_;
|
|
|
|
@@ -858,13 +859,13 @@ private:
|
|
|
|
|
|
|
|
|
|
class SSLClient : public Client {
|
|
|
|
|
public:
|
|
|
|
|
SSLClient(const std::string &host, int port = 443,
|
|
|
|
|
const std::string &client_cert_path = std::string(),
|
|
|
|
|
const std::string &client_key_path = std::string());
|
|
|
|
|
explicit SSLClient(const std::string &host, int port = 443,
|
|
|
|
|
const std::string &client_cert_path = std::string(),
|
|
|
|
|
const std::string &client_key_path = std::string());
|
|
|
|
|
|
|
|
|
|
virtual ~SSLClient();
|
|
|
|
|
~SSLClient() override;
|
|
|
|
|
|
|
|
|
|
virtual bool is_valid() const;
|
|
|
|
|
bool is_valid() const override;
|
|
|
|
|
|
|
|
|
|
void set_ca_cert_path(const char *ca_ceert_file_path,
|
|
|
|
|
const char *ca_cert_dir_path = nullptr);
|
|
|
|
@@ -876,12 +877,12 @@ public:
|
|
|
|
|
SSL_CTX *ssl_context() const noexcept;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
virtual bool process_and_close_socket(
|
|
|
|
|
bool process_and_close_socket(
|
|
|
|
|
socket_t sock, size_t request_count,
|
|
|
|
|
std::function<bool(Stream &strm, bool last_connection,
|
|
|
|
|
bool &connection_close)>
|
|
|
|
|
callback);
|
|
|
|
|
virtual bool is_ssl() const;
|
|
|
|
|
callback) override;
|
|
|
|
|
bool is_ssl() const override;
|
|
|
|
|
|
|
|
|
|
bool verify_host(X509 *server_cert) const;
|
|
|
|
|
bool verify_host_with_subject_alt_name(X509 *server_cert) const;
|
|
|
|
@@ -953,26 +954,26 @@ inline size_t to_utf8(int code, char *buff) {
|
|
|
|
|
buff[0] = (code & 0x7F);
|
|
|
|
|
return 1;
|
|
|
|
|
} else if (code < 0x0800) {
|
|
|
|
|
buff[0] = (0xC0 | ((code >> 6) & 0x1F));
|
|
|
|
|
buff[1] = (0x80 | (code & 0x3F));
|
|
|
|
|
buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
|
|
|
|
|
buff[1] = static_cast<char>(0x80 | (code & 0x3F));
|
|
|
|
|
return 2;
|
|
|
|
|
} else if (code < 0xD800) {
|
|
|
|
|
buff[0] = (0xE0 | ((code >> 12) & 0xF));
|
|
|
|
|
buff[1] = (0x80 | ((code >> 6) & 0x3F));
|
|
|
|
|
buff[2] = (0x80 | (code & 0x3F));
|
|
|
|
|
buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
|
|
|
|
|
buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
|
|
|
|
|
buff[2] = static_cast<char>(0x80 | (code & 0x3F));
|
|
|
|
|
return 3;
|
|
|
|
|
} else if (code < 0xE000) { // D800 - DFFF is invalid...
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (code < 0x10000) {
|
|
|
|
|
buff[0] = (0xE0 | ((code >> 12) & 0xF));
|
|
|
|
|
buff[1] = (0x80 | ((code >> 6) & 0x3F));
|
|
|
|
|
buff[2] = (0x80 | (code & 0x3F));
|
|
|
|
|
buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
|
|
|
|
|
buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
|
|
|
|
|
buff[2] = static_cast<char>(0x80 | (code & 0x3F));
|
|
|
|
|
return 3;
|
|
|
|
|
} else if (code < 0x110000) {
|
|
|
|
|
buff[0] = (0xF0 | ((code >> 18) & 0x7));
|
|
|
|
|
buff[1] = (0x80 | ((code >> 12) & 0x3F));
|
|
|
|
|
buff[2] = (0x80 | ((code >> 6) & 0x3F));
|
|
|
|
|
buff[3] = (0x80 | (code & 0x3F));
|
|
|
|
|
buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
|
|
|
|
|
buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
|
|
|
|
|
buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
|
|
|
|
|
buff[3] = static_cast<char>(0x80 | (code & 0x3F));
|
|
|
|
|
return 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -992,8 +993,8 @@ inline std::string base64_encode(const std::string &in) {
|
|
|
|
|
int val = 0;
|
|
|
|
|
int valb = -6;
|
|
|
|
|
|
|
|
|
|
for (uint8_t c : in) {
|
|
|
|
|
val = (val << 8) + c;
|
|
|
|
|
for (auto c : in) {
|
|
|
|
|
val = (val << 8) + static_cast<uint8_t>(c);
|
|
|
|
|
valb += 8;
|
|
|
|
|
while (valb >= 0) {
|
|
|
|
|
out.push_back(lookup[(val >> valb) & 0x3F]);
|
|
|
|
@@ -1188,7 +1189,7 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
|
|
|
|
|
|
|
|
|
|
timeval tv;
|
|
|
|
|
tv.tv_sec = static_cast<long>(sec);
|
|
|
|
|
tv.tv_usec = static_cast<long>(usec);
|
|
|
|
|
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
|
|
|
|
|
|
|
|
|
|
return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
|
|
|
|
|
#endif
|
|
|
|
@@ -1210,7 +1211,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
|
|
|
|
|
|
|
|
|
|
timeval tv;
|
|
|
|
|
tv.tv_sec = static_cast<long>(sec);
|
|
|
|
|
tv.tv_usec = static_cast<long>(usec);
|
|
|
|
|
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
|
|
|
|
|
|
|
|
|
|
return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
|
|
|
|
|
#endif
|
|
|
|
@@ -1243,7 +1244,7 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
|
|
|
|
|
|
|
|
|
|
timeval tv;
|
|
|
|
|
tv.tv_sec = static_cast<long>(sec);
|
|
|
|
|
tv.tv_usec = static_cast<long>(usec);
|
|
|
|
|
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
|
|
|
|
|
|
|
|
|
|
if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
|
|
|
|
|
(FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
|
|
|
|
@@ -1265,8 +1266,8 @@ public:
|
|
|
|
|
|
|
|
|
|
bool is_readable() const override;
|
|
|
|
|
bool is_writable() const override;
|
|
|
|
|
int read(char *ptr, size_t size) override;
|
|
|
|
|
int write(const char *ptr, size_t size) override;
|
|
|
|
|
ssize_t read(char *ptr, size_t size) override;
|
|
|
|
|
ssize_t write(const char *ptr, size_t size) override;
|
|
|
|
|
std::string get_remote_addr() const override;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
@@ -1280,12 +1281,12 @@ class SSLSocketStream : public Stream {
|
|
|
|
|
public:
|
|
|
|
|
SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
|
|
|
|
|
time_t read_timeout_usec);
|
|
|
|
|
virtual ~SSLSocketStream();
|
|
|
|
|
~SSLSocketStream() override;
|
|
|
|
|
|
|
|
|
|
bool is_readable() const override;
|
|
|
|
|
bool is_writable() const override;
|
|
|
|
|
int read(char *ptr, size_t size) override;
|
|
|
|
|
int write(const char *ptr, size_t size) override;
|
|
|
|
|
ssize_t read(char *ptr, size_t size) override;
|
|
|
|
|
ssize_t write(const char *ptr, size_t size) override;
|
|
|
|
|
std::string get_remote_addr() const override;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
@@ -1303,15 +1304,15 @@ public:
|
|
|
|
|
|
|
|
|
|
bool is_readable() const override;
|
|
|
|
|
bool is_writable() const override;
|
|
|
|
|
int read(char *ptr, size_t size) override;
|
|
|
|
|
int write(const char *ptr, size_t size) override;
|
|
|
|
|
ssize_t read(char *ptr, size_t size) override;
|
|
|
|
|
ssize_t write(const char *ptr, size_t size) override;
|
|
|
|
|
std::string get_remote_addr() const override;
|
|
|
|
|
|
|
|
|
|
const std::string &get_buffer() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::string buffer;
|
|
|
|
|
int position = 0;
|
|
|
|
|
size_t position = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
@@ -1479,7 +1480,7 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
|
|
|
|
|
auto ret = false;
|
|
|
|
|
for (auto rp = result; rp; rp = rp->ai_next) {
|
|
|
|
|
const auto &ai = *rp;
|
|
|
|
|
if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
|
|
|
|
|
if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
|
|
|
|
|
ret = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@@ -1523,7 +1524,8 @@ inline socket_t create_client_socket(const char *host, int port,
|
|
|
|
|
|
|
|
|
|
set_nonblocking(sock, true);
|
|
|
|
|
|
|
|
|
|
auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
|
|
|
|
|
auto ret =
|
|
|
|
|
::connect(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
if (is_connection_error() ||
|
|
|
|
|
!wait_until_socket_is_ready(sock, timeout_sec, 0)) {
|
|
|
|
@@ -1597,24 +1599,67 @@ find_content_type(const std::string &path,
|
|
|
|
|
inline const char *status_message(int status) {
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 100: return "Continue";
|
|
|
|
|
case 101: return "Switching Protocol";
|
|
|
|
|
case 102: return "Processing";
|
|
|
|
|
case 103: return "Early Hints";
|
|
|
|
|
case 200: return "OK";
|
|
|
|
|
case 201: return "Created";
|
|
|
|
|
case 202: return "Accepted";
|
|
|
|
|
case 203: return "Non-Authoritative Information";
|
|
|
|
|
case 204: return "No Content";
|
|
|
|
|
case 205: return "Reset Content";
|
|
|
|
|
case 206: return "Partial Content";
|
|
|
|
|
case 207: return "Multi-Status";
|
|
|
|
|
case 208: return "Already Reported";
|
|
|
|
|
case 226: return "IM Used";
|
|
|
|
|
case 300: return "Multiple Choice";
|
|
|
|
|
case 301: return "Moved Permanently";
|
|
|
|
|
case 302: return "Found";
|
|
|
|
|
case 303: return "See Other";
|
|
|
|
|
case 304: return "Not Modified";
|
|
|
|
|
case 305: return "Use Proxy";
|
|
|
|
|
case 306: return "unused";
|
|
|
|
|
case 307: return "Temporary Redirect";
|
|
|
|
|
case 308: return "Permanent Redirect";
|
|
|
|
|
case 400: return "Bad Request";
|
|
|
|
|
case 401: return "Unauthorized";
|
|
|
|
|
case 402: return "Payment Required";
|
|
|
|
|
case 403: return "Forbidden";
|
|
|
|
|
case 404: return "Not Found";
|
|
|
|
|
case 405: return "Method Not Allowed";
|
|
|
|
|
case 406: return "Not Acceptable";
|
|
|
|
|
case 407: return "Proxy Authentication Required";
|
|
|
|
|
case 408: return "Request Timeout";
|
|
|
|
|
case 409: return "Conflict";
|
|
|
|
|
case 410: return "Gone";
|
|
|
|
|
case 411: return "Length Required";
|
|
|
|
|
case 412: return "Precondition Failed";
|
|
|
|
|
case 413: return "Payload Too Large";
|
|
|
|
|
case 414: return "Request-URI Too Long";
|
|
|
|
|
case 414: return "URI Too Long";
|
|
|
|
|
case 415: return "Unsupported Media Type";
|
|
|
|
|
case 416: return "Range Not Satisfiable";
|
|
|
|
|
case 417: return "Expectation Failed";
|
|
|
|
|
case 418: return "I'm a teapot";
|
|
|
|
|
case 421: return "Misdirected Request";
|
|
|
|
|
case 422: return "Unprocessable Entity";
|
|
|
|
|
case 423: return "Locked";
|
|
|
|
|
case 424: return "Failed Dependency";
|
|
|
|
|
case 425: return "Too Early";
|
|
|
|
|
case 426: return "Upgrade Required";
|
|
|
|
|
case 428: return "Precondition Required";
|
|
|
|
|
case 429: return "Too Many Requests";
|
|
|
|
|
case 431: return "Request Header Fields Too Large";
|
|
|
|
|
case 451: return "Unavailable For Legal Reasons";
|
|
|
|
|
case 501: return "Not Implemented";
|
|
|
|
|
case 502: return "Bad Gateway";
|
|
|
|
|
case 503: return "Service Unavailable";
|
|
|
|
|
case 504: return "Gateway Timeout";
|
|
|
|
|
case 505: return "HTTP Version Not Supported";
|
|
|
|
|
case 506: return "Variant Also Negotiates";
|
|
|
|
|
case 507: return "Insufficient Storage";
|
|
|
|
|
case 508: return "Loop Detected";
|
|
|
|
|
case 510: return "Not Extended";
|
|
|
|
|
case 511: return "Network Authentication Required";
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
case 500: return "Internal Server Error";
|
|
|
|
@@ -1640,7 +1685,7 @@ inline bool compress(std::string &content) {
|
|
|
|
|
Z_DEFAULT_STRATEGY);
|
|
|
|
|
if (ret != Z_OK) { return false; }
|
|
|
|
|
|
|
|
|
|
strm.avail_in = content.size();
|
|
|
|
|
strm.avail_in = static_cast<decltype(strm.avail_in)>(content.size());
|
|
|
|
|
strm.next_in =
|
|
|
|
|
const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));
|
|
|
|
|
|
|
|
|
@@ -1687,7 +1732,7 @@ public:
|
|
|
|
|
bool decompress(const char *data, size_t data_length, T callback) {
|
|
|
|
|
int ret = Z_OK;
|
|
|
|
|
|
|
|
|
|
strm.avail_in = data_length;
|
|
|
|
|
strm.avail_in = static_cast<decltype(strm.avail_in)>(data_length);
|
|
|
|
|
strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
|
|
|
|
|
|
|
|
|
|
std::array<char, 16384> buff{};
|
|
|
|
@@ -1724,13 +1769,13 @@ inline bool has_header(const Headers &headers, const char *key) {
|
|
|
|
|
inline const char *get_header_value(const Headers &headers, const char *key,
|
|
|
|
|
size_t id = 0, const char *def = nullptr) {
|
|
|
|
|
auto it = headers.find(key);
|
|
|
|
|
std::advance(it, id);
|
|
|
|
|
std::advance(it, static_cast<int>(id));
|
|
|
|
|
if (it != headers.end()) { return it->second.c_str(); }
|
|
|
|
|
return def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline uint64_t get_header_value_uint64(const Headers &headers, const char *key,
|
|
|
|
|
int def = 0) {
|
|
|
|
|
uint64_t def = 0) {
|
|
|
|
|
auto it = headers.find(key);
|
|
|
|
|
if (it != headers.end()) {
|
|
|
|
|
return std::strtoull(it->second.data(), nullptr, 10);
|
|
|
|
@@ -1784,12 +1829,12 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
|
|
|
|
|
uint64_t r = 0;
|
|
|
|
|
while (r < len) {
|
|
|
|
|
auto read_len = static_cast<size_t>(len - r);
|
|
|
|
|
auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
|
|
|
|
|
auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
|
|
|
|
|
if (n <= 0) { return false; }
|
|
|
|
|
|
|
|
|
|
if (!out(buf, n)) { return false; }
|
|
|
|
|
if (!out(buf, static_cast<size_t>(n))) { return false; }
|
|
|
|
|
|
|
|
|
|
r += n;
|
|
|
|
|
r += static_cast<uint64_t>(n);
|
|
|
|
|
|
|
|
|
|
if (progress) {
|
|
|
|
|
if (!progress(r, len)) { return false; }
|
|
|
|
@@ -1804,9 +1849,9 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
|
|
|
|
|
uint64_t r = 0;
|
|
|
|
|
while (r < len) {
|
|
|
|
|
auto read_len = static_cast<size_t>(len - r);
|
|
|
|
|
auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
|
|
|
|
|
auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
|
|
|
|
|
if (n <= 0) { return; }
|
|
|
|
|
r += n;
|
|
|
|
|
r += static_cast<uint64_t>(n);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1819,7 +1864,7 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
|
|
|
|
|
} else if (n == 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (!out(buf, n)) { return false; }
|
|
|
|
|
if (!out(buf, static_cast<size_t>(n))) { return false; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
@@ -1833,7 +1878,7 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
|
|
|
|
|
|
|
|
|
|
if (!line_reader.getline()) { return false; }
|
|
|
|
|
|
|
|
|
|
auto chunk_len = std::stoi(line_reader.ptr(), 0, 16);
|
|
|
|
|
auto chunk_len = std::stoul(line_reader.ptr(), 0, 16);
|
|
|
|
|
|
|
|
|
|
while (chunk_len > 0) {
|
|
|
|
|
if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
|
|
|
|
@@ -1846,7 +1891,7 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
|
|
|
|
|
|
|
|
|
|
if (!line_reader.getline()) { return false; }
|
|
|
|
|
|
|
|
|
|
chunk_len = std::stoi(line_reader.ptr(), 0, 16);
|
|
|
|
|
chunk_len = std::stoul(line_reader.ptr(), 0, 16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chunk_len == 0) {
|
|
|
|
@@ -1918,9 +1963,11 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
|
|
|
|
|
auto write_len = 0;
|
|
|
|
|
inline ssize_t write_headers(Stream &strm, const T &info,
|
|
|
|
|
const Headers &headers) {
|
|
|
|
|
ssize_t write_len = 0;
|
|
|
|
|
for (const auto &x : info.headers) {
|
|
|
|
|
if (x.first == "EXCEPTION_WHAT") { continue; }
|
|
|
|
|
auto len =
|
|
|
|
|
strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
|
|
|
|
|
if (len < 0) { return len; }
|
|
|
|
@@ -2009,7 +2056,7 @@ inline bool redirect(T &cli, const Request &req, Response &res,
|
|
|
|
|
inline std::string encode_url(const std::string &s) {
|
|
|
|
|
std::string result;
|
|
|
|
|
|
|
|
|
|
for (auto i = 0; s[i]; i++) {
|
|
|
|
|
for (size_t i = 0; s[i]; i++) {
|
|
|
|
|
switch (s[i]) {
|
|
|
|
|
case ' ': result += "%20"; break;
|
|
|
|
|
case '+': result += "%2B"; break;
|
|
|
|
@@ -2024,9 +2071,9 @@ inline std::string encode_url(const std::string &s) {
|
|
|
|
|
if (c >= 0x80) {
|
|
|
|
|
result += '%';
|
|
|
|
|
char hex[4];
|
|
|
|
|
size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
|
|
|
|
|
auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
|
|
|
|
|
assert(len == 2);
|
|
|
|
|
result.append(hex, len);
|
|
|
|
|
result.append(hex, static_cast<size_t>(len));
|
|
|
|
|
} else {
|
|
|
|
|
result += s[i];
|
|
|
|
|
}
|
|
|
|
@@ -2037,7 +2084,8 @@ inline std::string encode_url(const std::string &s) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string decode_url(const std::string &s) {
|
|
|
|
|
inline std::string decode_url(const std::string &s,
|
|
|
|
|
bool convert_plus_to_space) {
|
|
|
|
|
std::string result;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < s.size(); i++) {
|
|
|
|
@@ -2063,7 +2111,7 @@ inline std::string decode_url(const std::string &s) {
|
|
|
|
|
result += s[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (s[i] == '+') {
|
|
|
|
|
} else if (convert_plus_to_space && s[i] == '+') {
|
|
|
|
|
result += ' ';
|
|
|
|
|
} else {
|
|
|
|
|
result += s[i];
|
|
|
|
@@ -2097,7 +2145,7 @@ inline void parse_query_text(const std::string &s, Params ¶ms) {
|
|
|
|
|
val.assign(b2, e2);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
params.emplace(key, decode_url(val));
|
|
|
|
|
params.emplace(decode_url(key, true), decode_url(val, true));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -2114,8 +2162,8 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
|
|
|
|
|
static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
|
|
|
|
|
std::smatch m;
|
|
|
|
|
if (std::regex_match(s, m, re_first_range)) {
|
|
|
|
|
auto pos = m.position(1);
|
|
|
|
|
auto len = m.length(1);
|
|
|
|
|
auto pos = static_cast<size_t>(m.position(1));
|
|
|
|
|
auto len = static_cast<size_t>(m.length(1));
|
|
|
|
|
bool all_valid_ranges = true;
|
|
|
|
|
split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
|
|
|
|
|
if (!all_valid_ranges) return;
|
|
|
|
@@ -2146,9 +2194,9 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
|
|
|
|
|
|
|
|
|
|
class MultipartFormDataParser {
|
|
|
|
|
public:
|
|
|
|
|
MultipartFormDataParser() {}
|
|
|
|
|
MultipartFormDataParser() = default;
|
|
|
|
|
|
|
|
|
|
void set_boundary(const std::string &boundary) { boundary_ = boundary; }
|
|
|
|
|
void set_boundary(std::string boundary) { boundary_ = std::move(boundary); }
|
|
|
|
|
|
|
|
|
|
bool is_valid() const { return is_valid_; }
|
|
|
|
|
|
|
|
|
@@ -2161,6 +2209,8 @@ public:
|
|
|
|
|
"^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
|
|
|
|
|
"\"(.*?)\")?\\s*$",
|
|
|
|
|
std::regex_constants::icase);
|
|
|
|
|
static const std::string dash_ = "--";
|
|
|
|
|
static const std::string crlf_ = "\r\n";
|
|
|
|
|
|
|
|
|
|
buf_.append(buf, n); // TODO: performance improvement
|
|
|
|
|
|
|
|
|
@@ -2300,8 +2350,6 @@ private:
|
|
|
|
|
file_.content_type.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string dash_ = "--";
|
|
|
|
|
const std::string crlf_ = "\r\n";
|
|
|
|
|
std::string boundary_;
|
|
|
|
|
|
|
|
|
|
std::string buf_;
|
|
|
|
@@ -2347,12 +2395,14 @@ get_range_offset_and_length(const Request &req, size_t content_length,
|
|
|
|
|
return std::make_pair(0, content_length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto slen = static_cast<ssize_t>(content_length);
|
|
|
|
|
|
|
|
|
|
if (r.first == -1) {
|
|
|
|
|
r.first = content_length - r.second;
|
|
|
|
|
r.second = content_length - 1;
|
|
|
|
|
r.first = slen - r.second;
|
|
|
|
|
r.second = slen - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r.second == -1) { r.second = content_length - 1; }
|
|
|
|
|
if (r.second == -1) { r.second = slen - 1; }
|
|
|
|
|
|
|
|
|
|
return std::make_pair(r.first, r.second - r.first + 1);
|
|
|
|
|
}
|
|
|
|
@@ -2456,7 +2506,9 @@ get_range_offset_and_length(const Request &req, const Response &res,
|
|
|
|
|
size_t index) {
|
|
|
|
|
auto r = req.ranges[index];
|
|
|
|
|
|
|
|
|
|
if (r.second == -1) { r.second = res.content_length - 1; }
|
|
|
|
|
if (r.second == -1) {
|
|
|
|
|
r.second = static_cast<ssize_t>(res.content_length) - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return std::make_pair(r.first, r.second - r.first + 1);
|
|
|
|
|
}
|
|
|
|
@@ -2611,9 +2663,13 @@ inline bool parse_www_authenticate(const httplib::Response &res,
|
|
|
|
|
auto beg = std::sregex_iterator(s.begin(), s.end(), re);
|
|
|
|
|
for (auto i = beg; i != std::sregex_iterator(); ++i) {
|
|
|
|
|
auto m = *i;
|
|
|
|
|
auto key = s.substr(m.position(1), m.length(1));
|
|
|
|
|
auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2))
|
|
|
|
|
: s.substr(m.position(3), m.length(3));
|
|
|
|
|
auto key = s.substr(static_cast<size_t>(m.position(1)),
|
|
|
|
|
static_cast<size_t>(m.length(1)));
|
|
|
|
|
auto val = m.length(2) > 0
|
|
|
|
|
? s.substr(static_cast<size_t>(m.position(2)),
|
|
|
|
|
static_cast<size_t>(m.length(2)))
|
|
|
|
|
: s.substr(static_cast<size_t>(m.position(3)),
|
|
|
|
|
static_cast<size_t>(m.length(3)));
|
|
|
|
|
auth[key] = val;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
@@ -2630,7 +2686,7 @@ inline std::string random_string(size_t length) {
|
|
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
|
"abcdefghijklmnopqrstuvwxyz";
|
|
|
|
|
const size_t max_index = (sizeof(charset) - 1);
|
|
|
|
|
return charset[rand() % max_index];
|
|
|
|
|
return charset[static_cast<size_t>(rand()) % max_index];
|
|
|
|
|
};
|
|
|
|
|
std::string str(length, 0);
|
|
|
|
|
std::generate_n(str.begin(), length, randchar);
|
|
|
|
@@ -2648,7 +2704,7 @@ inline std::string Request::get_header_value(const char *key, size_t id) const {
|
|
|
|
|
|
|
|
|
|
inline size_t Request::get_header_value_count(const char *key) const {
|
|
|
|
|
auto r = headers.equal_range(key);
|
|
|
|
|
return std::distance(r.first, r.second);
|
|
|
|
|
return static_cast<size_t>(std::distance(r.first, r.second));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Request::set_header(const char *key, const char *val) {
|
|
|
|
@@ -2665,14 +2721,14 @@ inline bool Request::has_param(const char *key) const {
|
|
|
|
|
|
|
|
|
|
inline std::string Request::get_param_value(const char *key, size_t id) const {
|
|
|
|
|
auto it = params.find(key);
|
|
|
|
|
std::advance(it, id);
|
|
|
|
|
std::advance(it, static_cast<ssize_t>(id));
|
|
|
|
|
if (it != params.end()) { return it->second; }
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline size_t Request::get_param_value_count(const char *key) const {
|
|
|
|
|
auto r = params.equal_range(key);
|
|
|
|
|
return std::distance(r.first, r.second);
|
|
|
|
|
return static_cast<size_t>(std::distance(r.first, r.second));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Request::is_multipart_form_data() const {
|
|
|
|
@@ -2702,7 +2758,7 @@ inline std::string Response::get_header_value(const char *key,
|
|
|
|
|
|
|
|
|
|
inline size_t Response::get_header_value_count(const char *key) const {
|
|
|
|
|
auto r = headers.equal_range(key);
|
|
|
|
|
return std::distance(r.first, r.second);
|
|
|
|
|
return static_cast<size_t>(std::distance(r.first, r.second));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Response::set_header(const char *key, const char *val) {
|
|
|
|
@@ -2724,9 +2780,8 @@ inline void Response::set_content(const char *s, size_t n,
|
|
|
|
|
set_header("Content-Type", content_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Response::set_content(const std::string &s,
|
|
|
|
|
const char *content_type) {
|
|
|
|
|
body = s;
|
|
|
|
|
inline void Response::set_content(std::string s, const char *content_type) {
|
|
|
|
|
body = std::move(s);
|
|
|
|
|
set_header("Content-Type", content_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -2753,33 +2808,39 @@ inline void Response::set_chunked_content_provider(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rstream implementation
|
|
|
|
|
inline int Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); }
|
|
|
|
|
inline ssize_t Stream::write(const char *ptr) {
|
|
|
|
|
return write(ptr, strlen(ptr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline int Stream::write(const std::string &s) {
|
|
|
|
|
inline ssize_t Stream::write(const std::string &s) {
|
|
|
|
|
return write(s.data(), s.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
inline int Stream::write_format(const char *fmt, const Args &... args) {
|
|
|
|
|
inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
|
|
|
|
|
std::array<char, 2048> buf;
|
|
|
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
|
|
|
auto n = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
|
|
|
|
|
auto sn = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
|
|
|
|
|
#else
|
|
|
|
|
auto n = snprintf(buf.data(), buf.size() - 1, fmt, args...);
|
|
|
|
|
auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
|
|
|
|
|
#endif
|
|
|
|
|
if (n <= 0) { return n; }
|
|
|
|
|
if (sn <= 0) { return sn; }
|
|
|
|
|
|
|
|
|
|
if (n >= static_cast<int>(buf.size()) - 1) {
|
|
|
|
|
auto n = static_cast<size_t>(sn);
|
|
|
|
|
|
|
|
|
|
if (n >= buf.size() - 1) {
|
|
|
|
|
std::vector<char> glowable_buf(buf.size());
|
|
|
|
|
|
|
|
|
|
while (n >= static_cast<int>(glowable_buf.size() - 1)) {
|
|
|
|
|
while (n >= glowable_buf.size() - 1) {
|
|
|
|
|
glowable_buf.resize(glowable_buf.size() * 2);
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
|
|
|
n = _snprintf_s(&glowable_buf[0], glowable_buf.size(),
|
|
|
|
|
glowable_buf.size() - 1, fmt, args...);
|
|
|
|
|
n = static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
|
|
|
|
|
glowable_buf.size() - 1, fmt,
|
|
|
|
|
args...));
|
|
|
|
|
#else
|
|
|
|
|
n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
|
|
|
|
|
n = static_cast<size_t>(
|
|
|
|
|
snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
return write(&glowable_buf[0], n);
|
|
|
|
@@ -2806,13 +2867,13 @@ inline bool SocketStream::is_writable() const {
|
|
|
|
|
return detail::select_write(sock_, 0, 0) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline int SocketStream::read(char *ptr, size_t size) {
|
|
|
|
|
if (is_readable()) { return recv(sock_, ptr, static_cast<int>(size), 0); }
|
|
|
|
|
inline ssize_t SocketStream::read(char *ptr, size_t size) {
|
|
|
|
|
if (is_readable()) { return recv(sock_, ptr, size, 0); }
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline int SocketStream::write(const char *ptr, size_t size) {
|
|
|
|
|
if (is_writable()) { return send(sock_, ptr, static_cast<int>(size), 0); }
|
|
|
|
|
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
|
|
|
|
|
if (is_writable()) { return send(sock_, ptr, size, 0); }
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -2825,19 +2886,19 @@ inline bool BufferStream::is_readable() const { return true; }
|
|
|
|
|
|
|
|
|
|
inline bool BufferStream::is_writable() const { return true; }
|
|
|
|
|
|
|
|
|
|
inline int BufferStream::read(char *ptr, size_t size) {
|
|
|
|
|
inline ssize_t BufferStream::read(char *ptr, size_t size) {
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
|
|
|
int len_read = static_cast<int>(buffer._Copy_s(ptr, size, size, position));
|
|
|
|
|
auto len_read = buffer._Copy_s(ptr, size, size, position);
|
|
|
|
|
#else
|
|
|
|
|
int len_read = static_cast<int>(buffer.copy(ptr, size, position));
|
|
|
|
|
auto len_read = buffer.copy(ptr, size, position);
|
|
|
|
|
#endif
|
|
|
|
|
position += len_read;
|
|
|
|
|
return len_read;
|
|
|
|
|
position += static_cast<size_t>(len_read);
|
|
|
|
|
return static_cast<ssize_t>(len_read);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline int BufferStream::write(const char *ptr, size_t size) {
|
|
|
|
|
inline ssize_t BufferStream::write(const char *ptr, size_t size) {
|
|
|
|
|
buffer.append(ptr, size);
|
|
|
|
|
return static_cast<int>(size);
|
|
|
|
|
return static_cast<ssize_t>(size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string BufferStream::get_remote_addr() const { return ""; }
|
|
|
|
@@ -3005,7 +3066,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
|
|
|
|
|
req.version = std::string(m[5]);
|
|
|
|
|
req.method = std::string(m[1]);
|
|
|
|
|
req.target = std::string(m[2]);
|
|
|
|
|
req.path = detail::decode_url(m[3]);
|
|
|
|
|
req.path = detail::decode_url(m[3], false);
|
|
|
|
|
|
|
|
|
|
// Parse query text
|
|
|
|
|
auto len = std::distance(m[4].first, m[4].second);
|
|
|
|
@@ -3183,47 +3244,45 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::read_content(Stream &strm, bool last_connection,
|
|
|
|
|
Request &req, Response &res) {
|
|
|
|
|
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
|
|
|
|
|
MultipartFormDataMap::iterator cur;
|
|
|
|
|
auto ret = read_content_core(
|
|
|
|
|
strm, last_connection, req, res,
|
|
|
|
|
// Regular
|
|
|
|
|
[&](const char *buf, size_t n) {
|
|
|
|
|
if (req.body.size() + n > req.body.max_size()) { return false; }
|
|
|
|
|
req.body.append(buf, n);
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
// Multipart
|
|
|
|
|
[&](const MultipartFormData &file) {
|
|
|
|
|
cur = req.files.emplace(file.name, file);
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
[&](const char *buf, size_t n) {
|
|
|
|
|
auto &content = cur->second.content;
|
|
|
|
|
if (content.size() + n > content.max_size()) { return false; }
|
|
|
|
|
content.append(buf, n);
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const auto &content_type = req.get_header_value("Content-Type");
|
|
|
|
|
if (!content_type.find("application/x-www-form-urlencoded")) {
|
|
|
|
|
detail::parse_query_text(req.body, req.params);
|
|
|
|
|
if (read_content_core(
|
|
|
|
|
strm, req, res,
|
|
|
|
|
// Regular
|
|
|
|
|
[&](const char *buf, size_t n) {
|
|
|
|
|
if (req.body.size() + n > req.body.max_size()) { return false; }
|
|
|
|
|
req.body.append(buf, n);
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
// Multipart
|
|
|
|
|
[&](const MultipartFormData &file) {
|
|
|
|
|
cur = req.files.emplace(file.name, file);
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
[&](const char *buf, size_t n) {
|
|
|
|
|
auto &content = cur->second.content;
|
|
|
|
|
if (content.size() + n > content.max_size()) { return false; }
|
|
|
|
|
content.append(buf, n);
|
|
|
|
|
return true;
|
|
|
|
|
})) {
|
|
|
|
|
const auto &content_type = req.get_header_value("Content-Type");
|
|
|
|
|
if (!content_type.find("application/x-www-form-urlencoded")) {
|
|
|
|
|
detail::parse_query_text(req.body, req.params);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::read_content_with_content_receiver(
|
|
|
|
|
Stream &strm, bool last_connection, Request &req, Response &res,
|
|
|
|
|
ContentReceiver receiver, MultipartContentHeader multipart_header,
|
|
|
|
|
Stream &strm, Request &req, Response &res, ContentReceiver receiver,
|
|
|
|
|
MultipartContentHeader multipart_header,
|
|
|
|
|
ContentReceiver multipart_receiver) {
|
|
|
|
|
return read_content_core(strm, last_connection, req, res, receiver,
|
|
|
|
|
multipart_header, multipart_receiver);
|
|
|
|
|
return read_content_core(strm, req, res, receiver, multipart_header,
|
|
|
|
|
multipart_receiver);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::read_content_core(Stream &strm, bool last_connection,
|
|
|
|
|
Request &req, Response &res,
|
|
|
|
|
inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
|
|
|
|
|
ContentReceiver receiver,
|
|
|
|
|
MultipartContentHeader mulitpart_header,
|
|
|
|
|
ContentReceiver multipart_receiver) {
|
|
|
|
@@ -3235,10 +3294,10 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
|
|
|
|
|
std::string boundary;
|
|
|
|
|
if (!detail::parse_multipart_boundary(content_type, boundary)) {
|
|
|
|
|
res.status = 400;
|
|
|
|
|
return write_response(strm, last_connection, req, res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
multipart_form_data_parser.set_boundary(boundary);
|
|
|
|
|
multipart_form_data_parser.set_boundary(std::move(boundary));
|
|
|
|
|
out = [&](const char *buf, size_t n) {
|
|
|
|
|
return multipart_form_data_parser.parse(buf, n, multipart_receiver,
|
|
|
|
|
mulitpart_header);
|
|
|
|
@@ -3249,13 +3308,13 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
|
|
|
|
|
|
|
|
|
|
if (!detail::read_content(strm, req, payload_max_length_, res.status,
|
|
|
|
|
Progress(), out)) {
|
|
|
|
|
return write_response(strm, last_connection, req, res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (req.is_multipart_form_data()) {
|
|
|
|
|
if (!multipart_form_data_parser.is_valid()) {
|
|
|
|
|
res.status = 400;
|
|
|
|
|
return write_response(strm, last_connection, req, res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3297,7 +3356,7 @@ inline socket_t Server::create_server_socket(const char *host, int port,
|
|
|
|
|
return detail::create_socket(
|
|
|
|
|
host, port,
|
|
|
|
|
[](socket_t sock, struct addrinfo &ai) -> bool {
|
|
|
|
|
if (::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
|
|
|
|
|
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (::listen(sock, 5)) { // Listen through 5 channels
|
|
|
|
@@ -3371,7 +3430,11 @@ inline bool Server::listen_internal() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if __cplusplus > 201703L
|
|
|
|
|
task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
|
|
|
|
|
#else
|
|
|
|
|
task_queue->enqueue([=]() { process_and_close_socket(sock); });
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task_queue->shutdown();
|
|
|
|
@@ -3381,8 +3444,7 @@ inline bool Server::listen_internal() {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::routing(Request &req, Response &res, Stream &strm,
|
|
|
|
|
bool last_connection) {
|
|
|
|
|
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
|
|
|
|
// File handler
|
|
|
|
|
bool is_head_request = req.method == "HEAD";
|
|
|
|
|
if ((req.method == "GET" || is_head_request) &&
|
|
|
|
@@ -3395,12 +3457,12 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
|
|
|
|
|
{
|
|
|
|
|
ContentReader reader(
|
|
|
|
|
[&](ContentReceiver receiver) {
|
|
|
|
|
return read_content_with_content_receiver(
|
|
|
|
|
strm, last_connection, req, res, receiver, nullptr, nullptr);
|
|
|
|
|
return read_content_with_content_receiver(strm, req, res, receiver,
|
|
|
|
|
nullptr, nullptr);
|
|
|
|
|
},
|
|
|
|
|
[&](MultipartContentHeader header, ContentReceiver receiver) {
|
|
|
|
|
return read_content_with_content_receiver(
|
|
|
|
|
strm, last_connection, req, res, nullptr, header, receiver);
|
|
|
|
|
return read_content_with_content_receiver(strm, req, res, nullptr,
|
|
|
|
|
header, receiver);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (req.method == "POST") {
|
|
|
|
@@ -3422,7 +3484,7 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read content into `req.body`
|
|
|
|
|
if (!read_content(strm, last_connection, req, res)) { return false; }
|
|
|
|
|
if (!read_content(strm, req, res)) { return false; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Regular handler
|
|
|
|
@@ -3446,14 +3508,23 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
|
|
|
|
|
|
|
|
|
|
inline bool Server::dispatch_request(Request &req, Response &res,
|
|
|
|
|
Handlers &handlers) {
|
|
|
|
|
for (const auto &x : handlers) {
|
|
|
|
|
const auto &pattern = x.first;
|
|
|
|
|
const auto &handler = x.second;
|
|
|
|
|
|
|
|
|
|
if (std::regex_match(req.path, req.matches, pattern)) {
|
|
|
|
|
handler(req, res);
|
|
|
|
|
return true;
|
|
|
|
|
try {
|
|
|
|
|
for (const auto &x : handlers) {
|
|
|
|
|
const auto &pattern = x.first;
|
|
|
|
|
const auto &handler = x.second;
|
|
|
|
|
|
|
|
|
|
if (std::regex_match(req.path, req.matches, pattern)) {
|
|
|
|
|
handler(req, res);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (const std::exception &ex) {
|
|
|
|
|
res.status = 500;
|
|
|
|
|
res.set_header("EXCEPTION_WHAT", ex.what());
|
|
|
|
|
} catch (...) {
|
|
|
|
|
res.status = 500;
|
|
|
|
|
res.set_header("EXCEPTION_WHAT", "UNKNOWN");
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@@ -3540,7 +3611,7 @@ Server::process_request(Stream &strm, bool last_connection,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rounting
|
|
|
|
|
if (routing(req, res, strm, last_connection)) {
|
|
|
|
|
if (routing(req, res, strm)) {
|
|
|
|
|
if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
|
|
|
|
|
} else {
|
|
|
|
|
if (res.status == -1) { res.status = 404; }
|
|
|
|
@@ -3875,7 +3946,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
|
|
|
|
|
DataSink data_sink;
|
|
|
|
|
data_sink.write = [&](const char *d, size_t l) {
|
|
|
|
|
auto written_length = strm.write(d, l);
|
|
|
|
|
offset += written_length;
|
|
|
|
|
offset += static_cast<size_t>(written_length);
|
|
|
|
|
};
|
|
|
|
|
data_sink.is_writable = [&](void) { return strm.is_writable(); };
|
|
|
|
|
|
|
|
|
@@ -3974,7 +4045,7 @@ inline bool Client::process_request(Stream &strm, const Request &req,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int dummy_status;
|
|
|
|
|
if (!detail::read_content(strm, res, std::numeric_limits<size_t>::max(),
|
|
|
|
|
if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
|
|
|
|
|
dummy_status, req.progress, out)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@@ -3991,7 +4062,7 @@ inline bool Client::process_and_close_socket(
|
|
|
|
|
std::function<bool(Stream &strm, bool last_connection,
|
|
|
|
|
bool &connection_close)>
|
|
|
|
|
callback) {
|
|
|
|
|
request_count = std::min(request_count, keep_alive_max_count_);
|
|
|
|
|
request_count = (std::min)(request_count, keep_alive_max_count_);
|
|
|
|
|
return detail::process_and_close_socket(true, sock, request_count,
|
|
|
|
|
read_timeout_sec_, read_timeout_usec_,
|
|
|
|
|
callback);
|
|
|
|
@@ -4476,7 +4547,7 @@ inline bool SSLSocketStream::is_writable() const {
|
|
|
|
|
return detail::select_write(sock_, 0, 0) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline int SSLSocketStream::read(char *ptr, size_t size) {
|
|
|
|
|
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
|
|
|
|
|
if (SSL_pending(ssl_) > 0 ||
|
|
|
|
|
select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
|
|
|
|
|
return SSL_read(ssl_, ptr, static_cast<int>(size));
|
|
|
|
@@ -4484,7 +4555,7 @@ inline int SSLSocketStream::read(char *ptr, size_t size) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline int SSLSocketStream::write(const char *ptr, size_t size) {
|
|
|
|
|
inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
|
|
|
|
|
if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); }
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
@@ -4744,7 +4815,9 @@ inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
|
|
|
|
|
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
|
|
|
|
|
name, sizeof(name));
|
|
|
|
|
|
|
|
|
|
if (name_len != -1) { return check_host_name(name, name_len); }
|
|
|
|
|
if (name_len != -1) {
|
|
|
|
|
return check_host_name(name, static_cast<size_t>(name_len));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|