|
|
@@ -1767,6 +1767,14 @@ private:
|
|
|
static std::unique_ptr<detail::MatcherBase>
|
|
|
make_matcher(const std::string &pattern);
|
|
|
|
|
|
+ template <typename H>
|
|
|
+ Server &add_handler(
|
|
|
+ std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, H>> &handlers,
|
|
|
+ const std::string &pattern, H handler) {
|
|
|
+ handlers.emplace_back(make_matcher(pattern), std::move(handler));
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
|
|
|
Server &set_error_handler_core(Handler handler, std::false_type);
|
|
|
|
|
|
@@ -2794,6 +2802,9 @@ private:
|
|
|
Response &res, bool &success, Error &error);
|
|
|
bool initialize_ssl(Socket &socket, Error &error);
|
|
|
|
|
|
+ void init_ctx();
|
|
|
+ void reset_ctx_on_error();
|
|
|
+
|
|
|
bool load_certs();
|
|
|
|
|
|
tls::ctx_t ctx_ = nullptr;
|
|
|
@@ -6695,6 +6706,35 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
|
|
|
return best;
|
|
|
}
|
|
|
|
|
|
+inline std::unique_ptr<compressor> make_compressor(EncodingType type) {
|
|
|
+#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
|
|
+ if (type == EncodingType::Gzip) {
|
|
|
+ return detail::make_unique<gzip_compressor>();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
|
|
+ if (type == EncodingType::Brotli) {
|
|
|
+ return detail::make_unique<brotli_compressor>();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
|
|
+ if (type == EncodingType::Zstd) {
|
|
|
+ return detail::make_unique<zstd_compressor>();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ (void)type;
|
|
|
+ return nullptr;
|
|
|
+}
|
|
|
+
|
|
|
+inline const char *encoding_name(EncodingType type) {
|
|
|
+ switch (type) {
|
|
|
+ case EncodingType::Gzip: return "gzip";
|
|
|
+ case EncodingType::Brotli: return "br";
|
|
|
+ case EncodingType::Zstd: return "zstd";
|
|
|
+ default: return "";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
inline bool nocompressor::compress(const char *data, size_t data_length,
|
|
|
bool /*last*/, Callback callback) {
|
|
|
if (!data_length) { return true; }
|
|
|
@@ -7027,6 +7067,29 @@ inline const char *get_header_value(const Headers &headers,
|
|
|
return def;
|
|
|
}
|
|
|
|
|
|
+inline size_t get_header_value_count(const Headers &headers,
|
|
|
+ const std::string &key) {
|
|
|
+ auto r = headers.equal_range(key);
|
|
|
+ return static_cast<size_t>(std::distance(r.first, r.second));
|
|
|
+}
|
|
|
+
|
|
|
+template <typename Map>
|
|
|
+inline typename Map::mapped_type
|
|
|
+get_multimap_value(const Map &m, const std::string &key, size_t id) {
|
|
|
+ auto rng = m.equal_range(key);
|
|
|
+ auto it = rng.first;
|
|
|
+ std::advance(it, static_cast<ssize_t>(id));
|
|
|
+ if (it != rng.second) { return it->second; }
|
|
|
+ return typename Map::mapped_type();
|
|
|
+}
|
|
|
+
|
|
|
+inline void set_header(Headers &headers, const std::string &key,
|
|
|
+ const std::string &val) {
|
|
|
+ if (fields::is_field_name(key) && fields::is_field_value(val)) {
|
|
|
+ headers.emplace(key, val);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
inline bool read_headers(Stream &strm, Headers &headers) {
|
|
|
const auto bufsiz = 2048;
|
|
|
char buf[bufsiz];
|
|
|
@@ -9721,16 +9784,12 @@ inline std::string Request::get_header_value(const std::string &key,
|
|
|
}
|
|
|
|
|
|
inline size_t Request::get_header_value_count(const std::string &key) const {
|
|
|
- auto r = headers.equal_range(key);
|
|
|
- return static_cast<size_t>(std::distance(r.first, r.second));
|
|
|
+ return detail::get_header_value_count(headers, key);
|
|
|
}
|
|
|
|
|
|
inline void Request::set_header(const std::string &key,
|
|
|
const std::string &val) {
|
|
|
- if (detail::fields::is_field_name(key) &&
|
|
|
- detail::fields::is_field_value(val)) {
|
|
|
- headers.emplace(key, val);
|
|
|
- }
|
|
|
+ detail::set_header(headers, key, val);
|
|
|
}
|
|
|
|
|
|
inline bool Request::has_trailer(const std::string &key) const {
|
|
|
@@ -9739,11 +9798,7 @@ inline bool Request::has_trailer(const std::string &key) const {
|
|
|
|
|
|
inline std::string Request::get_trailer_value(const std::string &key,
|
|
|
size_t id) const {
|
|
|
- auto rng = trailers.equal_range(key);
|
|
|
- auto it = rng.first;
|
|
|
- std::advance(it, static_cast<ssize_t>(id));
|
|
|
- if (it != rng.second) { return it->second; }
|
|
|
- return std::string();
|
|
|
+ return detail::get_multimap_value(trailers, key, id);
|
|
|
}
|
|
|
|
|
|
inline size_t Request::get_trailer_value_count(const std::string &key) const {
|
|
|
@@ -9757,11 +9812,7 @@ inline bool Request::has_param(const std::string &key) const {
|
|
|
|
|
|
inline std::string Request::get_param_value(const std::string &key,
|
|
|
size_t id) const {
|
|
|
- auto rng = params.equal_range(key);
|
|
|
- auto it = rng.first;
|
|
|
- std::advance(it, static_cast<ssize_t>(id));
|
|
|
- if (it != rng.second) { return it->second; }
|
|
|
- return std::string();
|
|
|
+ return detail::get_multimap_value(params, key, id);
|
|
|
}
|
|
|
|
|
|
inline std::vector<std::string>
|
|
|
@@ -9816,11 +9867,7 @@ inline size_t MultipartFormData::get_field_count(const std::string &key) const {
|
|
|
|
|
|
inline FormData MultipartFormData::get_file(const std::string &key,
|
|
|
size_t id) const {
|
|
|
- auto rng = files.equal_range(key);
|
|
|
- auto it = rng.first;
|
|
|
- std::advance(it, static_cast<ssize_t>(id));
|
|
|
- if (it != rng.second) { return it->second; }
|
|
|
- return FormData();
|
|
|
+ return detail::get_multimap_value(files, key, id);
|
|
|
}
|
|
|
|
|
|
inline std::vector<FormData>
|
|
|
@@ -9859,16 +9906,12 @@ inline std::string Response::get_header_value(const std::string &key,
|
|
|
}
|
|
|
|
|
|
inline size_t Response::get_header_value_count(const std::string &key) const {
|
|
|
- auto r = headers.equal_range(key);
|
|
|
- return static_cast<size_t>(std::distance(r.first, r.second));
|
|
|
+ return detail::get_header_value_count(headers, key);
|
|
|
}
|
|
|
|
|
|
inline void Response::set_header(const std::string &key,
|
|
|
const std::string &val) {
|
|
|
- if (detail::fields::is_field_name(key) &&
|
|
|
- detail::fields::is_field_value(val)) {
|
|
|
- headers.emplace(key, val);
|
|
|
- }
|
|
|
+ detail::set_header(headers, key, val);
|
|
|
}
|
|
|
inline bool Response::has_trailer(const std::string &key) const {
|
|
|
return trailers.find(key) != trailers.end();
|
|
|
@@ -9876,11 +9919,7 @@ inline bool Response::has_trailer(const std::string &key) const {
|
|
|
|
|
|
inline std::string Response::get_trailer_value(const std::string &key,
|
|
|
size_t id) const {
|
|
|
- auto rng = trailers.equal_range(key);
|
|
|
- auto it = rng.first;
|
|
|
- std::advance(it, static_cast<ssize_t>(id));
|
|
|
- if (it != rng.second) { return it->second; }
|
|
|
- return std::string();
|
|
|
+ return detail::get_multimap_value(trailers, key, id);
|
|
|
}
|
|
|
|
|
|
inline size_t Response::get_trailer_value_count(const std::string &key) const {
|
|
|
@@ -10721,61 +10760,51 @@ Server::make_matcher(const std::string &pattern) {
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Get(const std::string &pattern, Handler handler) {
|
|
|
- get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(get_handlers_, pattern, std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Post(const std::string &pattern, Handler handler) {
|
|
|
- post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(post_handlers_, pattern, std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Post(const std::string &pattern,
|
|
|
HandlerWithContentReader handler) {
|
|
|
- post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
|
|
|
- std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(post_handlers_for_content_reader_, pattern,
|
|
|
+ std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Put(const std::string &pattern, Handler handler) {
|
|
|
- put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(put_handlers_, pattern, std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Put(const std::string &pattern,
|
|
|
HandlerWithContentReader handler) {
|
|
|
- put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
|
|
|
- std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(put_handlers_for_content_reader_, pattern,
|
|
|
+ std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Patch(const std::string &pattern, Handler handler) {
|
|
|
- patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(patch_handlers_, pattern, std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Patch(const std::string &pattern,
|
|
|
HandlerWithContentReader handler) {
|
|
|
- patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
|
|
|
- std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(patch_handlers_for_content_reader_, pattern,
|
|
|
+ std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Delete(const std::string &pattern, Handler handler) {
|
|
|
- delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(delete_handlers_, pattern, std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Delete(const std::string &pattern,
|
|
|
HandlerWithContentReader handler) {
|
|
|
- delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
|
|
|
- std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(delete_handlers_for_content_reader_, pattern,
|
|
|
+ std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::Options(const std::string &pattern, Handler handler) {
|
|
|
- options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
|
|
|
- return *this;
|
|
|
+ return add_handler(options_handlers_, pattern, std::move(handler));
|
|
|
}
|
|
|
|
|
|
inline Server &Server::WebSocket(const std::string &pattern,
|
|
|
@@ -11209,23 +11238,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
|
|
|
if (res.is_chunked_content_provider_) {
|
|
|
auto type = detail::encoding_type(req, res);
|
|
|
|
|
|
- std::unique_ptr<detail::compressor> compressor;
|
|
|
- if (type == detail::EncodingType::Gzip) {
|
|
|
-#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
|
|
- compressor = detail::make_unique<detail::gzip_compressor>();
|
|
|
-#endif
|
|
|
- } else if (type == detail::EncodingType::Brotli) {
|
|
|
-#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
|
|
- compressor = detail::make_unique<detail::brotli_compressor>();
|
|
|
-#endif
|
|
|
- } else if (type == detail::EncodingType::Zstd) {
|
|
|
-#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
|
|
- compressor = detail::make_unique<detail::zstd_compressor>();
|
|
|
-#endif
|
|
|
- } else {
|
|
|
+ auto compressor = detail::make_compressor(type);
|
|
|
+ if (!compressor) {
|
|
|
compressor = detail::make_unique<detail::nocompressor>();
|
|
|
}
|
|
|
- assert(compressor != nullptr);
|
|
|
|
|
|
return detail::write_content_chunked(strm, res.content_provider_,
|
|
|
is_shutting_down, *compressor);
|
|
|
@@ -11847,14 +11863,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
|
|
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");
|
|
|
- res.set_header("Vary", "Accept-Encoding");
|
|
|
- } else if (type == detail::EncodingType::Brotli) {
|
|
|
- res.set_header("Content-Encoding", "br");
|
|
|
- res.set_header("Vary", "Accept-Encoding");
|
|
|
- } else if (type == detail::EncodingType::Zstd) {
|
|
|
- res.set_header("Content-Encoding", "zstd");
|
|
|
+ if (type != detail::EncodingType::None) {
|
|
|
+ res.set_header("Content-Encoding", detail::encoding_name(type));
|
|
|
res.set_header("Vary", "Accept-Encoding");
|
|
|
}
|
|
|
}
|
|
|
@@ -11885,27 +11895,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
|
|
if (type != detail::EncodingType::None) {
|
|
|
output_pre_compression_log(req, res);
|
|
|
|
|
|
- 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 = "br";
|
|
|
-#endif
|
|
|
- } else if (type == detail::EncodingType::Zstd) {
|
|
|
-#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
|
|
- compressor = detail::make_unique<detail::zstd_compressor>();
|
|
|
- content_encoding = "zstd";
|
|
|
-#endif
|
|
|
- }
|
|
|
-
|
|
|
- if (compressor) {
|
|
|
+ if (auto compressor = detail::make_compressor(type)) {
|
|
|
std::string compressed;
|
|
|
if (compressor->compress(res.body.data(), res.body.size(), true,
|
|
|
[&](const char *data, size_t data_len) {
|
|
|
@@ -11913,7 +11903,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
|
|
return true;
|
|
|
})) {
|
|
|
res.body.swap(compressed);
|
|
|
- res.set_header("Content-Encoding", content_encoding);
|
|
|
+ res.set_header("Content-Encoding", detail::encoding_name(type));
|
|
|
res.set_header("Vary", "Accept-Encoding");
|
|
|
}
|
|
|
}
|
|
|
@@ -14857,10 +14847,10 @@ inline Client::Client(const std::string &scheme_host_port,
|
|
|
cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
|
|
|
client_cert_path, client_key_path);
|
|
|
}
|
|
|
-} // namespace detail
|
|
|
+}
|
|
|
|
|
|
inline Client::Client(const std::string &host, int port)
|
|
|
- : cli_(detail::make_unique<ClientImpl>(host, port)) {}
|
|
|
+ : Client(host, port, std::string(), std::string()) {}
|
|
|
|
|
|
inline Client::Client(const std::string &host, int port,
|
|
|
const std::string &client_cert_path,
|
|
|
@@ -15823,24 +15813,31 @@ inline SSLClient::SSLClient(const std::string &host)
|
|
|
inline SSLClient::SSLClient(const std::string &host, int port)
|
|
|
: SSLClient(host, port, std::string(), std::string()) {}
|
|
|
|
|
|
+inline void SSLClient::init_ctx() {
|
|
|
+ ctx_ = tls::create_client_context();
|
|
|
+ if (ctx_) { tls::set_min_version(ctx_, tls::Version::TLS1_2); }
|
|
|
+}
|
|
|
+
|
|
|
+inline void SSLClient::reset_ctx_on_error() {
|
|
|
+ last_backend_error_ = tls::get_error();
|
|
|
+ tls::free_context(ctx_);
|
|
|
+ ctx_ = nullptr;
|
|
|
+}
|
|
|
+
|
|
|
inline SSLClient::SSLClient(const std::string &host, int port,
|
|
|
const std::string &client_cert_path,
|
|
|
const std::string &client_key_path,
|
|
|
const std::string &private_key_password)
|
|
|
: ClientImpl(host, port, client_cert_path, client_key_path) {
|
|
|
- ctx_ = tls::create_client_context();
|
|
|
+ init_ctx();
|
|
|
if (!ctx_) { return; }
|
|
|
|
|
|
- tls::set_min_version(ctx_, tls::Version::TLS1_2);
|
|
|
-
|
|
|
if (!client_cert_path.empty() && !client_key_path.empty()) {
|
|
|
const char *password =
|
|
|
private_key_password.empty() ? nullptr : private_key_password.c_str();
|
|
|
if (!tls::set_client_cert_file(ctx_, client_cert_path.c_str(),
|
|
|
client_key_path.c_str(), password)) {
|
|
|
- last_backend_error_ = tls::get_error();
|
|
|
- tls::free_context(ctx_);
|
|
|
- ctx_ = nullptr;
|
|
|
+ reset_ctx_on_error();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -15848,17 +15845,13 @@ inline SSLClient::SSLClient(const std::string &host, int port,
|
|
|
inline SSLClient::SSLClient(const std::string &host, int port,
|
|
|
const PemMemory &pem)
|
|
|
: ClientImpl(host, port) {
|
|
|
- ctx_ = tls::create_client_context();
|
|
|
+ init_ctx();
|
|
|
if (!ctx_) { return; }
|
|
|
|
|
|
- tls::set_min_version(ctx_, tls::Version::TLS1_2);
|
|
|
-
|
|
|
if (pem.cert_pem && pem.key_pem) {
|
|
|
if (!tls::set_client_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
|
|
|
pem.private_key_password)) {
|
|
|
- last_backend_error_ = tls::get_error();
|
|
|
- tls::free_context(ctx_);
|
|
|
- ctx_ = nullptr;
|
|
|
+ reset_ctx_on_error();
|
|
|
}
|
|
|
}
|
|
|
}
|