|
|
|
@@ -173,6 +173,7 @@ using socket_t = int;
|
|
|
|
|
#define INVALID_SOCKET (-1)
|
|
|
|
|
#endif //_WIN32
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <atomic>
|
|
|
|
|
#include <cassert>
|
|
|
|
@@ -325,6 +326,10 @@ using ContentProvider =
|
|
|
|
|
using ContentProviderWithoutLength =
|
|
|
|
|
std::function<bool(size_t offset, DataSink &sink)>;
|
|
|
|
|
|
|
|
|
|
using ContentReceiverWithProgress =
|
|
|
|
|
std::function<bool(const char *data, size_t data_length, uint64_t offset,
|
|
|
|
|
uint64_t total_length)>;
|
|
|
|
|
|
|
|
|
|
using ContentReceiver =
|
|
|
|
|
std::function<bool(const char *data, size_t data_length)>;
|
|
|
|
|
|
|
|
|
@@ -377,7 +382,7 @@ struct Request {
|
|
|
|
|
// for client
|
|
|
|
|
size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
|
|
|
|
|
ResponseHandler response_handler;
|
|
|
|
|
ContentReceiver content_receiver;
|
|
|
|
|
ContentReceiverWithProgress content_receiver;
|
|
|
|
|
size_t content_length = 0;
|
|
|
|
|
ContentProvider content_provider;
|
|
|
|
|
Progress progress;
|
|
|
|
@@ -671,8 +676,14 @@ private:
|
|
|
|
|
const HandlersForContentReader &handlers);
|
|
|
|
|
|
|
|
|
|
bool parse_request_line(const char *s, Request &req);
|
|
|
|
|
void apply_ranges(const Request &req, Response &res,
|
|
|
|
|
std::string &content_type, std::string &boundary);
|
|
|
|
|
bool write_response(Stream &strm, bool close_connection, const Request &req,
|
|
|
|
|
Response &res);
|
|
|
|
|
bool write_response_with_content(Stream &strm, bool close_connection,
|
|
|
|
|
const Request &req, Response &res,
|
|
|
|
|
std::string &content_type,
|
|
|
|
|
std::string &boundary);
|
|
|
|
|
bool write_content_with_provider(Stream &strm, const Request &req,
|
|
|
|
|
Response &res, const std::string &boundary,
|
|
|
|
|
const std::string &content_type);
|
|
|
|
@@ -688,8 +699,8 @@ private:
|
|
|
|
|
ContentReceiver multipart_receiver);
|
|
|
|
|
|
|
|
|
|
virtual bool process_and_close_socket(socket_t sock);
|
|
|
|
|
|
|
|
|
|
struct MountPointEntry {
|
|
|
|
|
|
|
|
|
|
struct MountPointEntry {
|
|
|
|
|
std::string mount_point;
|
|
|
|
|
std::string base_dir;
|
|
|
|
|
Headers headers;
|
|
|
|
@@ -740,8 +751,11 @@ public:
|
|
|
|
|
bool operator==(std::nullptr_t) const { return res_ == nullptr; }
|
|
|
|
|
bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
|
|
|
|
|
const Response &value() const { return *res_; }
|
|
|
|
|
Response &value() { return *res_; }
|
|
|
|
|
const Response &operator*() const { return *res_; }
|
|
|
|
|
Response &operator*() { return *res_; }
|
|
|
|
|
const Response *operator->() const { return res_.get(); }
|
|
|
|
|
Response *operator->() { return res_.get(); }
|
|
|
|
|
Error error() const { return err_; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
@@ -803,8 +817,7 @@ public:
|
|
|
|
|
Result Post(const char *path, const Headers &headers,
|
|
|
|
|
const MultipartFormDataItems &items);
|
|
|
|
|
Result Post(const char *path, const Headers &headers,
|
|
|
|
|
const MultipartFormDataItems &items,
|
|
|
|
|
const std::string& boundary);
|
|
|
|
|
const MultipartFormDataItems &items, const std::string &boundary);
|
|
|
|
|
|
|
|
|
|
Result Put(const char *path);
|
|
|
|
|
Result Put(const char *path, const std::string &body,
|
|
|
|
@@ -1041,8 +1054,7 @@ public:
|
|
|
|
|
Result Post(const char *path, const Headers &headers,
|
|
|
|
|
const MultipartFormDataItems &items);
|
|
|
|
|
Result Post(const char *path, const Headers &headers,
|
|
|
|
|
const MultipartFormDataItems &items,
|
|
|
|
|
const std::string& boundary);
|
|
|
|
|
const MultipartFormDataItems &items, const std::string &boundary);
|
|
|
|
|
Result Put(const char *path);
|
|
|
|
|
Result Put(const char *path, const std::string &body,
|
|
|
|
|
const char *content_type);
|
|
|
|
@@ -1134,7 +1146,7 @@ private:
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
bool is_ssl_ = false;
|
|
|
|
|
#endif
|
|
|
|
|
}; // namespace httplib
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
class SSLServer : public Server {
|
|
|
|
@@ -2509,7 +2521,8 @@ inline bool read_headers(Stream &strm, Headers &headers) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool read_content_with_length(Stream &strm, uint64_t len,
|
|
|
|
|
Progress progress, ContentReceiver out) {
|
|
|
|
|
Progress progress,
|
|
|
|
|
ContentReceiverWithProgress out) {
|
|
|
|
|
char buf[CPPHTTPLIB_RECV_BUFSIZ];
|
|
|
|
|
|
|
|
|
|
uint64_t r = 0;
|
|
|
|
@@ -2518,8 +2531,7 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
|
|
|
|
|
auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
|
|
|
|
|
if (n <= 0) { return false; }
|
|
|
|
|
|
|
|
|
|
if (!out(buf, static_cast<size_t>(n))) { return false; }
|
|
|
|
|
|
|
|
|
|
if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
|
|
|
|
|
r += static_cast<uint64_t>(n);
|
|
|
|
|
|
|
|
|
|
if (progress) {
|
|
|
|
@@ -2541,8 +2553,10 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
|
|
|
|
|
inline bool read_content_without_length(Stream &strm,
|
|
|
|
|
ContentReceiverWithProgress out) {
|
|
|
|
|
char buf[CPPHTTPLIB_RECV_BUFSIZ];
|
|
|
|
|
uint64_t r = 0;
|
|
|
|
|
for (;;) {
|
|
|
|
|
auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
|
|
|
|
|
if (n < 0) {
|
|
|
|
@@ -2550,13 +2564,16 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
|
|
|
|
|
} else if (n == 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (!out(buf, static_cast<size_t>(n))) { return false; }
|
|
|
|
|
|
|
|
|
|
if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
|
|
|
|
|
r += static_cast<uint64_t>(n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
|
|
|
|
|
inline bool read_content_chunked(Stream &strm,
|
|
|
|
|
ContentReceiverWithProgress out) {
|
|
|
|
|
const auto bufsiz = 16;
|
|
|
|
|
char buf[bufsiz];
|
|
|
|
|
|
|
|
|
@@ -2601,7 +2618,8 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T, typename U>
|
|
|
|
|
bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
|
|
|
|
|
bool prepare_content_receiver(T &x, int &status,
|
|
|
|
|
ContentReceiverWithProgress receiver,
|
|
|
|
|
bool decompress, U callback) {
|
|
|
|
|
if (decompress) {
|
|
|
|
|
std::string encoding = x.get_header_value("Content-Encoding");
|
|
|
|
@@ -2626,10 +2644,12 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
|
|
|
|
|
|
|
|
|
|
if (decompressor) {
|
|
|
|
|
if (decompressor->is_valid()) {
|
|
|
|
|
ContentReceiver out = [&](const char *buf, size_t n) {
|
|
|
|
|
return decompressor->decompress(
|
|
|
|
|
buf, n,
|
|
|
|
|
[&](const char *buf, size_t n) { return receiver(buf, n); });
|
|
|
|
|
ContentReceiverWithProgress out = [&](const char *buf, size_t n,
|
|
|
|
|
uint64_t off, uint64_t len) {
|
|
|
|
|
return decompressor->decompress(buf, n,
|
|
|
|
|
[&](const char *buf, size_t n) {
|
|
|
|
|
return receiver(buf, n, off, len);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
return callback(std::move(out));
|
|
|
|
|
} else {
|
|
|
|
@@ -2639,18 +2659,20 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ContentReceiver out = [&](const char *buf, size_t n) {
|
|
|
|
|
return receiver(buf, n);
|
|
|
|
|
ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
|
|
|
|
|
uint64_t len) {
|
|
|
|
|
return receiver(buf, n, off, len);
|
|
|
|
|
};
|
|
|
|
|
return callback(std::move(out));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
|
|
|
|
|
Progress progress, ContentReceiver receiver,
|
|
|
|
|
Progress progress, ContentReceiverWithProgress receiver,
|
|
|
|
|
bool decompress) {
|
|
|
|
|
return prepare_content_receiver(
|
|
|
|
|
x, status, std::move(receiver), decompress, [&](const ContentReceiver &out) {
|
|
|
|
|
x, status, std::move(receiver), decompress,
|
|
|
|
|
[&](const ContentReceiverWithProgress &out) {
|
|
|
|
|
auto ret = true;
|
|
|
|
|
auto exceed_payload_max_length = false;
|
|
|
|
|
|
|
|
|
@@ -2906,7 +2928,7 @@ inline bool parse_multipart_boundary(const std::string &content_type,
|
|
|
|
|
return !boundary.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
|
|
|
|
|
inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
|
|
|
|
|
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)) {
|
|
|
|
@@ -2938,7 +2960,7 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
|
|
|
|
|
return all_valid_ranges;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} catch (...) { return false; }
|
|
|
|
|
|
|
|
|
|
class MultipartFormDataParser {
|
|
|
|
|
public:
|
|
|
|
@@ -2949,7 +2971,8 @@ public:
|
|
|
|
|
bool is_valid() const { return is_valid_; }
|
|
|
|
|
|
|
|
|
|
template <typename T, typename U>
|
|
|
|
|
bool parse(const char *buf, size_t n, const T &content_callback, const U &header_callback) {
|
|
|
|
|
bool parse(const char *buf, size_t n, const T &content_callback,
|
|
|
|
|
const U &header_callback) {
|
|
|
|
|
|
|
|
|
|
static const std::regex re_content_disposition(
|
|
|
|
|
"^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
|
|
|
|
@@ -3150,13 +3173,12 @@ get_range_offset_and_length(const Request &req, size_t content_length,
|
|
|
|
|
auto slen = static_cast<ssize_t>(content_length);
|
|
|
|
|
|
|
|
|
|
if (r.first == -1) {
|
|
|
|
|
r.first = slen - r.second;
|
|
|
|
|
r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
|
|
|
|
|
r.second = slen - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r.second == -1) { r.second = slen - 1; }
|
|
|
|
|
|
|
|
|
|
return std::make_pair(r.first, r.second - r.first + 1);
|
|
|
|
|
return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string make_content_range_header_field(size_t offset, size_t length,
|
|
|
|
@@ -3205,21 +3227,21 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string make_multipart_ranges_data(const Request &req, Response &res,
|
|
|
|
|
const std::string &boundary,
|
|
|
|
|
const std::string &content_type) {
|
|
|
|
|
std::string data;
|
|
|
|
|
|
|
|
|
|
process_multipart_ranges_data(
|
|
|
|
|
inline bool make_multipart_ranges_data(const Request &req, Response &res,
|
|
|
|
|
const std::string &boundary,
|
|
|
|
|
const std::string &content_type,
|
|
|
|
|
std::string &data) {
|
|
|
|
|
return process_multipart_ranges_data(
|
|
|
|
|
req, res, boundary, content_type,
|
|
|
|
|
[&](const std::string &token) { data += token; },
|
|
|
|
|
[&](const char *token) { data += token; },
|
|
|
|
|
[&](size_t offset, size_t length) {
|
|
|
|
|
data += res.body.substr(offset, length);
|
|
|
|
|
return true;
|
|
|
|
|
if (offset < res.body.size()) {
|
|
|
|
|
data += res.body.substr(offset, length);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline size_t
|
|
|
|
@@ -3797,14 +3819,14 @@ inline Server::Server()
|
|
|
|
|
inline Server::~Server() {}
|
|
|
|
|
|
|
|
|
|
inline Server &Server::Get(const char *pattern, Handler handler) {
|
|
|
|
|
get_handlers_.push_back(std::make_pair(std::regex(pattern),
|
|
|
|
|
std::move(handler)));
|
|
|
|
|
get_handlers_.push_back(
|
|
|
|
|
std::make_pair(std::regex(pattern), std::move(handler)));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Server &Server::Post(const char *pattern, Handler handler) {
|
|
|
|
|
post_handlers_.push_back(std::make_pair(std::regex(pattern),
|
|
|
|
|
std::move(handler)));
|
|
|
|
|
post_handlers_.push_back(
|
|
|
|
|
std::make_pair(std::regex(pattern), std::move(handler)));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3816,8 +3838,8 @@ inline Server &Server::Post(const char *pattern,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Server &Server::Put(const char *pattern, Handler handler) {
|
|
|
|
|
put_handlers_.push_back(std::make_pair(std::regex(pattern),
|
|
|
|
|
std::move(handler)));
|
|
|
|
|
put_handlers_.push_back(
|
|
|
|
|
std::make_pair(std::regex(pattern), std::move(handler)));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3829,8 +3851,8 @@ inline Server &Server::Put(const char *pattern,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Server &Server::Patch(const char *pattern, Handler handler) {
|
|
|
|
|
patch_handlers_.push_back(std::make_pair(std::regex(pattern),
|
|
|
|
|
std::move(handler)));
|
|
|
|
|
patch_handlers_.push_back(
|
|
|
|
|
std::make_pair(std::regex(pattern), std::move(handler)));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3842,8 +3864,8 @@ inline Server &Server::Patch(const char *pattern,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Server &Server::Delete(const char *pattern, Handler handler) {
|
|
|
|
|
delete_handlers_.push_back(std::make_pair(std::regex(pattern),
|
|
|
|
|
std::move(handler)));
|
|
|
|
|
delete_handlers_.push_back(
|
|
|
|
|
std::make_pair(std::regex(pattern), std::move(handler)));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3855,8 +3877,8 @@ inline Server &Server::Delete(const char *pattern,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Server &Server::Options(const char *pattern, Handler handler) {
|
|
|
|
|
options_handlers_.push_back(std::make_pair(std::regex(pattern),
|
|
|
|
|
std::move(handler)));
|
|
|
|
|
options_handlers_.push_back(
|
|
|
|
|
std::make_pair(std::regex(pattern), std::move(handler)));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3988,18 +4010,19 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
|
|
|
|
|
|
|
|
|
|
inline bool Server::write_response(Stream &strm, bool close_connection,
|
|
|
|
|
const Request &req, Response &res) {
|
|
|
|
|
std::string content_type;
|
|
|
|
|
std::string boundary;
|
|
|
|
|
return write_response_with_content(strm, close_connection, req, res,
|
|
|
|
|
content_type, boundary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::write_response_with_content(
|
|
|
|
|
Stream &strm, bool close_connection, const Request &req, Response &res,
|
|
|
|
|
std::string &content_type, std::string &boundary) {
|
|
|
|
|
assert(res.status != -1);
|
|
|
|
|
|
|
|
|
|
if (400 <= res.status && error_handler_) { error_handler_(req, res); }
|
|
|
|
|
|
|
|
|
|
detail::BufferStream bstrm;
|
|
|
|
|
|
|
|
|
|
// Response line
|
|
|
|
|
if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
|
|
|
|
|
detail::status_message(res.status))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Headers
|
|
|
|
|
if (close_connection || req.get_header_value("Connection") == "close") {
|
|
|
|
|
res.set_header("Connection", "close");
|
|
|
|
@@ -4015,109 +4038,21 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
|
|
|
|
|
res.set_header("Content-Type", "text/plain");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res.has_header("Content-Length") && res.body.empty() &&
|
|
|
|
|
!res.content_length_ && !res.content_provider_) {
|
|
|
|
|
res.set_header("Content-Length", "0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
|
|
|
|
|
res.set_header("Accept-Ranges", "bytes");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string content_type;
|
|
|
|
|
std::string boundary;
|
|
|
|
|
detail::BufferStream bstrm;
|
|
|
|
|
|
|
|
|
|
if (req.ranges.size() > 1) {
|
|
|
|
|
boundary = detail::make_multipart_data_boundary();
|
|
|
|
|
|
|
|
|
|
auto it = res.headers.find("Content-Type");
|
|
|
|
|
if (it != res.headers.end()) {
|
|
|
|
|
content_type = it->second;
|
|
|
|
|
res.headers.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.headers.emplace("Content-Type",
|
|
|
|
|
"multipart/byteranges; boundary=" + boundary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto type = detail::encoding_type(req, res);
|
|
|
|
|
|
|
|
|
|
if (res.body.empty()) {
|
|
|
|
|
if (res.content_length_ > 0) {
|
|
|
|
|
size_t length = 0;
|
|
|
|
|
if (req.ranges.empty()) {
|
|
|
|
|
length = res.content_length_;
|
|
|
|
|
} else if (req.ranges.size() == 1) {
|
|
|
|
|
auto offsets =
|
|
|
|
|
detail::get_range_offset_and_length(req, res.content_length_, 0);
|
|
|
|
|
auto offset = offsets.first;
|
|
|
|
|
length = offsets.second;
|
|
|
|
|
auto content_range = detail::make_content_range_header_field(
|
|
|
|
|
offset, length, res.content_length_);
|
|
|
|
|
res.set_header("Content-Range", content_range);
|
|
|
|
|
} else {
|
|
|
|
|
length = detail::get_multipart_ranges_data_length(req, res, boundary,
|
|
|
|
|
content_type);
|
|
|
|
|
}
|
|
|
|
|
res.set_header("Content-Length", std::to_string(length));
|
|
|
|
|
} else {
|
|
|
|
|
if (res.content_provider_) {
|
|
|
|
|
if (res.is_chunked_content_provider) {
|
|
|
|
|
res.set_header("Transfer-Encoding", "chunked");
|
|
|
|
|
if (type == detail::EncodingType::Gzip) {
|
|
|
|
|
res.set_header("Content-Encoding", "gzip");
|
|
|
|
|
} else if (type == detail::EncodingType::Brotli) {
|
|
|
|
|
res.set_header("Content-Encoding", "br");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
res.set_header("Content-Length", "0");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (req.ranges.empty()) {
|
|
|
|
|
;
|
|
|
|
|
} else if (req.ranges.size() == 1) {
|
|
|
|
|
auto offsets =
|
|
|
|
|
detail::get_range_offset_and_length(req, res.body.size(), 0);
|
|
|
|
|
auto offset = offsets.first;
|
|
|
|
|
auto length = offsets.second;
|
|
|
|
|
auto content_range = detail::make_content_range_header_field(
|
|
|
|
|
offset, length, res.body.size());
|
|
|
|
|
res.set_header("Content-Range", content_range);
|
|
|
|
|
res.body = res.body.substr(offset, length);
|
|
|
|
|
} else {
|
|
|
|
|
res.body =
|
|
|
|
|
detail::make_multipart_ranges_data(req, res, boundary, content_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type != detail::EncodingType::None) {
|
|
|
|
|
std::unique_ptr<detail::compressor> compressor;
|
|
|
|
|
|
|
|
|
|
if (type == detail::EncodingType::Gzip) {
|
|
|
|
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
|
|
|
|
compressor = detail::make_unique<detail::gzip_compressor>();
|
|
|
|
|
res.set_header("Content-Encoding", "gzip");
|
|
|
|
|
#endif
|
|
|
|
|
} else if (type == detail::EncodingType::Brotli) {
|
|
|
|
|
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
|
|
|
|
compressor = detail::make_unique<detail::brotli_compressor>();
|
|
|
|
|
res.set_header("Content-Encoding", "brotli");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (compressor) {
|
|
|
|
|
std::string compressed;
|
|
|
|
|
|
|
|
|
|
if (!compressor->compress(res.body.data(), res.body.size(), true,
|
|
|
|
|
[&](const char *data, size_t data_len) {
|
|
|
|
|
compressed.append(data, data_len);
|
|
|
|
|
return true;
|
|
|
|
|
})) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.body.swap(compressed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto length = std::to_string(res.body.size());
|
|
|
|
|
res.set_header("Content-Length", length);
|
|
|
|
|
// Response line
|
|
|
|
|
if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
|
|
|
|
|
detail::status_message(res.status))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!detail::write_headers(bstrm, res, Headers())) { return false; }
|
|
|
|
@@ -4240,7 +4175,7 @@ inline bool Server::read_content_with_content_receiver(
|
|
|
|
|
Stream &strm, Request &req, Response &res, ContentReceiver receiver,
|
|
|
|
|
MultipartContentHeader multipart_header,
|
|
|
|
|
ContentReceiver multipart_receiver) {
|
|
|
|
|
return read_content_core(strm, req, res, std::move(receiver),
|
|
|
|
|
return read_content_core(strm, req, res, std::move(receiver),
|
|
|
|
|
std::move(multipart_header),
|
|
|
|
|
std::move(multipart_receiver));
|
|
|
|
|
}
|
|
|
|
@@ -4250,7 +4185,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
|
|
|
|
|
MultipartContentHeader mulitpart_header,
|
|
|
|
|
ContentReceiver multipart_receiver) {
|
|
|
|
|
detail::MultipartFormDataParser multipart_form_data_parser;
|
|
|
|
|
ContentReceiver out;
|
|
|
|
|
ContentReceiverWithProgress out;
|
|
|
|
|
|
|
|
|
|
if (req.is_multipart_form_data()) {
|
|
|
|
|
const auto &content_type = req.get_header_value("Content-Type");
|
|
|
|
@@ -4261,7 +4196,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
multipart_form_data_parser.set_boundary(std::move(boundary));
|
|
|
|
|
out = [&](const char *buf, size_t n) {
|
|
|
|
|
out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
|
|
|
|
|
/* For debug
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
while (pos < n) {
|
|
|
|
@@ -4277,7 +4212,8 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
|
|
|
|
|
mulitpart_header);
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
out = std::move(receiver);
|
|
|
|
|
out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
|
|
|
|
|
uint64_t /*len*/) { return receiver(buf, n); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (req.method == "DELETE" && !req.has_header("Content-Length")) {
|
|
|
|
@@ -4314,7 +4250,7 @@ inline bool Server::handle_file_request(Request &req, Response &res,
|
|
|
|
|
auto type =
|
|
|
|
|
detail::find_content_type(path, file_extension_and_mimetype_map_);
|
|
|
|
|
if (type) { res.set_header("Content-Type", type); }
|
|
|
|
|
for (const auto& kv : entry.headers) {
|
|
|
|
|
for (const auto &kv : entry.headers) {
|
|
|
|
|
res.set_header(kv.first.c_str(), kv.second);
|
|
|
|
|
}
|
|
|
|
|
res.status = 200;
|
|
|
|
@@ -4435,9 +4371,8 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
|
|
|
|
{
|
|
|
|
|
ContentReader reader(
|
|
|
|
|
[&](ContentReceiver receiver) {
|
|
|
|
|
return read_content_with_content_receiver(strm, req, res,
|
|
|
|
|
std::move(receiver),
|
|
|
|
|
nullptr, nullptr);
|
|
|
|
|
return read_content_with_content_receiver(
|
|
|
|
|
strm, req, res, std::move(receiver), nullptr, nullptr);
|
|
|
|
|
},
|
|
|
|
|
[&](MultipartContentHeader header, ContentReceiver receiver) {
|
|
|
|
|
return read_content_with_content_receiver(strm, req, res, nullptr,
|
|
|
|
@@ -4497,7 +4432,6 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
|
|
|
|
|
|
|
|
|
inline bool Server::dispatch_request(Request &req, Response &res,
|
|
|
|
|
const Handlers &handlers) {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
for (const auto &x : handlers) {
|
|
|
|
|
const auto &pattern = x.first;
|
|
|
|
@@ -4518,6 +4452,116 @@ inline bool Server::dispatch_request(Request &req, Response &res,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Server::apply_ranges(const Request &req, Response &res,
|
|
|
|
|
std::string &content_type,
|
|
|
|
|
std::string &boundary) {
|
|
|
|
|
if (req.ranges.size() > 1) {
|
|
|
|
|
boundary = detail::make_multipart_data_boundary();
|
|
|
|
|
|
|
|
|
|
auto it = res.headers.find("Content-Type");
|
|
|
|
|
if (it != res.headers.end()) {
|
|
|
|
|
content_type = it->second;
|
|
|
|
|
res.headers.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.headers.emplace("Content-Type",
|
|
|
|
|
"multipart/byteranges; boundary=" + boundary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto type = detail::encoding_type(req, res);
|
|
|
|
|
|
|
|
|
|
if (res.body.empty()) {
|
|
|
|
|
if (res.content_length_ > 0) {
|
|
|
|
|
size_t length = 0;
|
|
|
|
|
if (req.ranges.empty()) {
|
|
|
|
|
length = res.content_length_;
|
|
|
|
|
} else if (req.ranges.size() == 1) {
|
|
|
|
|
auto offsets =
|
|
|
|
|
detail::get_range_offset_and_length(req, res.content_length_, 0);
|
|
|
|
|
auto offset = offsets.first;
|
|
|
|
|
length = offsets.second;
|
|
|
|
|
auto content_range = detail::make_content_range_header_field(
|
|
|
|
|
offset, length, res.content_length_);
|
|
|
|
|
res.set_header("Content-Range", content_range);
|
|
|
|
|
} else {
|
|
|
|
|
length = detail::get_multipart_ranges_data_length(req, res, boundary,
|
|
|
|
|
content_type);
|
|
|
|
|
}
|
|
|
|
|
res.set_header("Content-Length", std::to_string(length));
|
|
|
|
|
} else {
|
|
|
|
|
if (res.content_provider_) {
|
|
|
|
|
if (res.is_chunked_content_provider) {
|
|
|
|
|
res.set_header("Transfer-Encoding", "chunked");
|
|
|
|
|
if (type == detail::EncodingType::Gzip) {
|
|
|
|
|
res.set_header("Content-Encoding", "gzip");
|
|
|
|
|
} else if (type == detail::EncodingType::Brotli) {
|
|
|
|
|
res.set_header("Content-Encoding", "br");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (req.ranges.empty()) {
|
|
|
|
|
;
|
|
|
|
|
} else if (req.ranges.size() == 1) {
|
|
|
|
|
auto offsets =
|
|
|
|
|
detail::get_range_offset_and_length(req, res.body.size(), 0);
|
|
|
|
|
auto offset = offsets.first;
|
|
|
|
|
auto length = offsets.second;
|
|
|
|
|
auto content_range = detail::make_content_range_header_field(
|
|
|
|
|
offset, length, res.body.size());
|
|
|
|
|
res.set_header("Content-Range", content_range);
|
|
|
|
|
if (offset < res.body.size()) {
|
|
|
|
|
res.body = res.body.substr(offset, length);
|
|
|
|
|
} else {
|
|
|
|
|
res.body.clear();
|
|
|
|
|
res.status = 416;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
std::string data;
|
|
|
|
|
if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
|
|
|
|
|
data)) {
|
|
|
|
|
res.body.swap(data);
|
|
|
|
|
} else {
|
|
|
|
|
res.body.clear();
|
|
|
|
|
res.status = 416;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type != detail::EncodingType::None) {
|
|
|
|
|
std::unique_ptr<detail::compressor> compressor;
|
|
|
|
|
std::string content_encoding;
|
|
|
|
|
|
|
|
|
|
if (type == detail::EncodingType::Gzip) {
|
|
|
|
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
|
|
|
|
compressor = detail::make_unique<detail::gzip_compressor>();
|
|
|
|
|
content_encoding = "gzip";
|
|
|
|
|
#endif
|
|
|
|
|
} else if (type == detail::EncodingType::Brotli) {
|
|
|
|
|
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
|
|
|
|
compressor = detail::make_unique<detail::brotli_compressor>();
|
|
|
|
|
content_encoding = "brotli";
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (compressor) {
|
|
|
|
|
std::string compressed;
|
|
|
|
|
if (compressor->compress(res.body.data(), res.body.size(), true,
|
|
|
|
|
[&](const char *data, size_t data_len) {
|
|
|
|
|
compressed.append(data, data_len);
|
|
|
|
|
return true;
|
|
|
|
|
})) {
|
|
|
|
|
res.body.swap(compressed);
|
|
|
|
|
res.set_header("Content-Encoding", content_encoding);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto length = std::to_string(res.body.size());
|
|
|
|
|
res.set_header("Content-Length", length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::dispatch_request_for_content_reader(
|
|
|
|
|
Request &req, Response &res, ContentReader content_reader,
|
|
|
|
|
const HandlersForContentReader &handlers) {
|
|
|
|
@@ -4580,7 +4624,8 @@ Server::process_request(Stream &strm, bool close_connection,
|
|
|
|
|
if (req.has_header("Range")) {
|
|
|
|
|
const auto &range_header_value = req.get_header_value("Range");
|
|
|
|
|
if (!detail::parse_range_header(range_header_value, req.ranges)) {
|
|
|
|
|
// TODO: error
|
|
|
|
|
res.status = 416;
|
|
|
|
|
return write_response(strm, close_connection, req, res);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -4608,7 +4653,12 @@ Server::process_request(Stream &strm, bool close_connection,
|
|
|
|
|
if (res.status == -1) { res.status = 404; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return write_response(strm, close_connection, req, res);
|
|
|
|
|
std::string content_type;
|
|
|
|
|
std::string boundary;
|
|
|
|
|
apply_ranges(req, res, content_type, boundary);
|
|
|
|
|
|
|
|
|
|
return write_response_with_content(strm, close_connection, req, res,
|
|
|
|
|
content_type, boundary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::is_valid() const { return true; }
|
|
|
|
@@ -4721,7 +4771,7 @@ inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
|
|
|
|
|
const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n");
|
|
|
|
|
|
|
|
|
|
std::cmatch m;
|
|
|
|
|
if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
|
|
|
|
|
if (!std::regex_match(line_reader.ptr(), m, re)) { return true; }
|
|
|
|
|
res.version = std::string(m[1]);
|
|
|
|
|
res.status = std::stoi(std::string(m[2]));
|
|
|
|
|
res.reason = std::string(m[3]);
|
|
|
|
@@ -5119,16 +5169,21 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
|
|
|
|
|
if (req.method != "HEAD" && req.method != "CONNECT") {
|
|
|
|
|
auto out =
|
|
|
|
|
req.content_receiver
|
|
|
|
|
? static_cast<ContentReceiver>([&](const char *buf, size_t n) {
|
|
|
|
|
auto ret = req.content_receiver(buf, n);
|
|
|
|
|
if (!ret) { error_ = Error::Canceled; }
|
|
|
|
|
return ret;
|
|
|
|
|
})
|
|
|
|
|
: static_cast<ContentReceiver>([&](const char *buf, size_t n) {
|
|
|
|
|
if (res.body.size() + n > res.body.max_size()) { return false; }
|
|
|
|
|
res.body.append(buf, n);
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
? static_cast<ContentReceiverWithProgress>(
|
|
|
|
|
[&](const char *buf, size_t n, uint64_t off, uint64_t len) {
|
|
|
|
|
auto ret = req.content_receiver(buf, n, off, len);
|
|
|
|
|
if (!ret) { error_ = Error::Canceled; }
|
|
|
|
|
return ret;
|
|
|
|
|
})
|
|
|
|
|
: static_cast<ContentReceiverWithProgress>(
|
|
|
|
|
[&](const char *buf, size_t n, uint64_t /*off*/,
|
|
|
|
|
uint64_t /*len*/) {
|
|
|
|
|
if (res.body.size() + n > res.body.max_size()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
res.body.append(buf, n);
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
auto progress = [&](uint64_t current, uint64_t total) {
|
|
|
|
|
if (!req.progress) { return true; }
|
|
|
|
@@ -5160,10 +5215,9 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
|
|
|
|
|
inline bool
|
|
|
|
|
ClientImpl::process_socket(Socket &socket,
|
|
|
|
|
std::function<bool(Stream &strm)> callback) {
|
|
|
|
|
return detail::process_client_socket(socket.sock, read_timeout_sec_,
|
|
|
|
|
read_timeout_usec_, write_timeout_sec_,
|
|
|
|
|
write_timeout_usec_,
|
|
|
|
|
std::move(callback));
|
|
|
|
|
return detail::process_client_socket(
|
|
|
|
|
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
|
|
|
|
|
write_timeout_usec_, std::move(callback));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool ClientImpl::is_ssl() const { return false; }
|
|
|
|
@@ -5250,7 +5304,11 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
|
|
|
|
|
req.headers = default_headers_;
|
|
|
|
|
req.headers.insert(headers.begin(), headers.end());
|
|
|
|
|
req.response_handler = std::move(response_handler);
|
|
|
|
|
req.content_receiver = std::move(content_receiver);
|
|
|
|
|
req.content_receiver =
|
|
|
|
|
[content_receiver](const char *data, size_t data_length,
|
|
|
|
|
uint64_t /*offset*/, uint64_t /*total_length*/) {
|
|
|
|
|
return content_receiver(data, data_length);
|
|
|
|
|
};
|
|
|
|
|
req.progress = std::move(progress);
|
|
|
|
|
|
|
|
|
|
auto res = detail::make_unique<Response>();
|
|
|
|
@@ -5306,10 +5364,9 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
|
|
|
|
size_t content_length,
|
|
|
|
|
ContentProvider content_provider,
|
|
|
|
|
const char *content_type) {
|
|
|
|
|
auto ret = send_with_content_provider("POST", path, headers, std::string(),
|
|
|
|
|
content_length,
|
|
|
|
|
std::move(content_provider),
|
|
|
|
|
content_type);
|
|
|
|
|
auto ret = send_with_content_provider(
|
|
|
|
|
"POST", path, headers, std::string(), content_length,
|
|
|
|
|
std::move(content_provider), content_type);
|
|
|
|
|
return Result{std::move(ret), get_last_error()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -5330,7 +5387,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
|
|
|
|
}
|
|
|
|
|
inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
|
|
|
|
const MultipartFormDataItems &items,
|
|
|
|
|
const std::string& boundary) {
|
|
|
|
|
const std::string &boundary) {
|
|
|
|
|
for (size_t i = 0; i < boundary.size(); i++) {
|
|
|
|
|
char c = boundary[i];
|
|
|
|
|
if (!std::isalnum(c) && c != '-' && c != '_') {
|
|
|
|
@@ -5389,10 +5446,9 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
|
|
|
|
|
size_t content_length,
|
|
|
|
|
ContentProvider content_provider,
|
|
|
|
|
const char *content_type) {
|
|
|
|
|
auto ret = send_with_content_provider("PUT", path, headers, std::string(),
|
|
|
|
|
content_length,
|
|
|
|
|
std::move(content_provider),
|
|
|
|
|
content_type);
|
|
|
|
|
auto ret = send_with_content_provider(
|
|
|
|
|
"PUT", path, headers, std::string(), content_length,
|
|
|
|
|
std::move(content_provider), content_type);
|
|
|
|
|
return Result{std::move(ret), get_last_error()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -5430,10 +5486,9 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
|
|
|
|
|
size_t content_length,
|
|
|
|
|
ContentProvider content_provider,
|
|
|
|
|
const char *content_type) {
|
|
|
|
|
auto ret = send_with_content_provider("PATCH", path, headers, std::string(),
|
|
|
|
|
content_length,
|
|
|
|
|
std::move(content_provider),
|
|
|
|
|
content_type);
|
|
|
|
|
auto ret = send_with_content_provider(
|
|
|
|
|
"PATCH", path, headers, std::string(), content_length,
|
|
|
|
|
std::move(content_provider), content_type);
|
|
|
|
|
return Result{std::move(ret), get_last_error()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -5605,7 +5660,9 @@ inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ssl) {
|
|
|
|
|
set_nonblocking(sock, true);
|
|
|
|
|
auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
|
|
|
|
|
BIO_set_nbio(bio, 1);
|
|
|
|
|
SSL_set_bio(ssl, bio, bio);
|
|
|
|
|
|
|
|
|
|
if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
|
|
|
|
@@ -5614,8 +5671,11 @@ inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
|
|
|
|
|
std::lock_guard<std::mutex> guard(ctx_mutex);
|
|
|
|
|
SSL_free(ssl);
|
|
|
|
|
}
|
|
|
|
|
set_nonblocking(sock, false);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
BIO_set_nbio(bio, 0);
|
|
|
|
|
set_nonblocking(sock, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ssl;
|
|
|
|
@@ -5631,6 +5691,28 @@ inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
|
|
|
|
|
SSL_free(ssl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename U>
|
|
|
|
|
bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
|
|
|
|
|
U ssl_connect_or_accept,
|
|
|
|
|
time_t timeout_sec,
|
|
|
|
|
time_t timeout_usec) {
|
|
|
|
|
int res = 0;
|
|
|
|
|
while ((res = ssl_connect_or_accept(ssl)) != 1) {
|
|
|
|
|
auto err = SSL_get_error(ssl, res);
|
|
|
|
|
switch (err) {
|
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
|
if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
|
|
|
|
|
break;
|
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
|
if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
|
|
|
|
|
break;
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
inline bool
|
|
|
|
|
process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count,
|
|
|
|
@@ -5718,22 +5800,7 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
|
|
|
|
|
read_timeout_usec_(read_timeout_usec),
|
|
|
|
|
write_timeout_sec_(write_timeout_sec),
|
|
|
|
|
write_timeout_usec_(write_timeout_usec) {
|
|
|
|
|
{
|
|
|
|
|
timeval tv;
|
|
|
|
|
tv.tv_sec = static_cast<long>(read_timeout_sec);
|
|
|
|
|
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
|
|
|
|
|
|
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&tv),
|
|
|
|
|
sizeof(tv));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
timeval tv;
|
|
|
|
|
tv.tv_sec = static_cast<long>(write_timeout_sec);
|
|
|
|
|
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
|
|
|
|
|
|
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&tv),
|
|
|
|
|
sizeof(tv));
|
|
|
|
|
}
|
|
|
|
|
SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline SSLSocketStream::~SSLSocketStream() {}
|
|
|
|
@@ -5748,8 +5815,25 @@ inline bool SSLSocketStream::is_writable() const {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
|
|
|
|
|
if (SSL_pending(ssl_) > 0 || is_readable()) {
|
|
|
|
|
if (SSL_pending(ssl_) > 0) {
|
|
|
|
|
return SSL_read(ssl_, ptr, static_cast<int>(size));
|
|
|
|
|
} else if (is_readable()) {
|
|
|
|
|
auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
auto err = SSL_get_error(ssl_, ret);
|
|
|
|
|
while (err == SSL_ERROR_WANT_READ) {
|
|
|
|
|
if (SSL_pending(ssl_) > 0) {
|
|
|
|
|
return SSL_read(ssl_, ptr, static_cast<int>(size));
|
|
|
|
|
} else if (is_readable()) {
|
|
|
|
|
ret = SSL_read(ssl_, ptr, static_cast<int>(size));
|
|
|
|
|
if (ret >= 0) { return ret; }
|
|
|
|
|
err = SSL_get_error(ssl_, ret);
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
@@ -5841,8 +5925,13 @@ inline SSLServer::~SSLServer() {
|
|
|
|
|
inline bool SSLServer::is_valid() const { return ctx_; }
|
|
|
|
|
|
|
|
|
|
inline bool SSLServer::process_and_close_socket(socket_t sock) {
|
|
|
|
|
auto ssl = detail::ssl_new(sock, ctx_, ctx_mutex_, SSL_accept,
|
|
|
|
|
[](SSL * /*ssl*/) { return true; });
|
|
|
|
|
auto ssl = detail::ssl_new(
|
|
|
|
|
sock, ctx_, ctx_mutex_,
|
|
|
|
|
[&](SSL *ssl) {
|
|
|
|
|
return detail::ssl_connect_or_accept_nonblocking(
|
|
|
|
|
sock, ssl, SSL_accept, read_timeout_sec_, read_timeout_usec_);
|
|
|
|
|
},
|
|
|
|
|
[](SSL * /*ssl*/) { return true; });
|
|
|
|
|
|
|
|
|
|
if (ssl) {
|
|
|
|
|
auto ret = detail::process_server_socket_ssl(
|
|
|
|
@@ -6036,7 +6125,9 @@ inline bool SSLClient::initialize_ssl(Socket &socket) {
|
|
|
|
|
SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SSL_connect(ssl) != 1) {
|
|
|
|
|
if (!detail::ssl_connect_or_accept_nonblocking(
|
|
|
|
|
socket.sock, ssl, SSL_connect, connection_timeout_sec_,
|
|
|
|
|
connection_timeout_usec_)) {
|
|
|
|
|
error_ = Error::SSLConnection;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@@ -6161,17 +6252,15 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
|
|
|
|
|
auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
|
|
|
|
|
auto name_len = (size_t)ASN1_STRING_length(val->d.ia5);
|
|
|
|
|
|
|
|
|
|
if (strlen(name) == name_len) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
|
|
|
|
|
switch (type) {
|
|
|
|
|
case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
|
|
|
|
|
|
|
|
|
|
case GEN_IPADD:
|
|
|
|
|
if (!memcmp(&addr6, name, addr_len) ||
|
|
|
|
|
!memcmp(&addr, name, addr_len)) {
|
|
|
|
|
ip_mached = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case GEN_IPADD:
|
|
|
|
|
if (!memcmp(&addr6, name, addr_len) ||
|
|
|
|
|
!memcmp(&addr, name, addr_len)) {
|
|
|
|
|
ip_mached = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -6246,6 +6335,8 @@ inline Client::Client(const char *scheme_host_port,
|
|
|
|
|
#else
|
|
|
|
|
if (!scheme.empty() && scheme != "http") {
|
|
|
|
|
#endif
|
|
|
|
|
std::string msg = "'" + scheme + "' scheme is not supported.";
|
|
|
|
|
throw std::invalid_argument(msg);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -6381,7 +6472,7 @@ inline Result Client::Post(const char *path, const Headers &headers,
|
|
|
|
|
}
|
|
|
|
|
inline Result Client::Post(const char *path, const Headers &headers,
|
|
|
|
|
const MultipartFormDataItems &items,
|
|
|
|
|
const std::string& boundary) {
|
|
|
|
|
const std::string &boundary) {
|
|
|
|
|
return cli_->Post(path, headers, items, boundary);
|
|
|
|
|
}
|
|
|
|
|
inline Result Client::Put(const char *path) { return cli_->Put(path); }
|
|
|
|
|