You've already forked cpp-httplib
@@ -8,4 +8,6 @@ FROM scratch
|
|||||||
COPY --from=builder /build/server /server
|
COPY --from=builder /build/server /server
|
||||||
COPY docker/html/index.html /html/index.html
|
COPY docker/html/index.html /html/index.html
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
CMD ["/server"]
|
|
||||||
|
ENTRYPOINT ["/server"]
|
||||||
|
CMD ["0.0.0.0", "80", "/", "/html"]
|
||||||
|
212
docker/main.cc
212
docker/main.cc
@@ -5,77 +5,203 @@
|
|||||||
// MIT License
|
// MIT License
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <signal.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
|
|
||||||
constexpr auto error_html = R"(<html>
|
using namespace httplib;
|
||||||
<head><title>{} {}</title></head>
|
|
||||||
<body>
|
|
||||||
<center><h1>404 Not Found</h1></center>
|
|
||||||
<hr><center>cpp-httplib/{}</center>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
)";
|
|
||||||
|
|
||||||
void sigint_handler(int s) { exit(1); }
|
auto SERVER_NAME =
|
||||||
|
std::format("cpp-httplib-nginxish-server/{}", CPPHTTPLIB_VERSION);
|
||||||
|
|
||||||
std::string time_local() {
|
Server svr;
|
||||||
auto p = std::chrono::system_clock::now();
|
|
||||||
auto t = std::chrono::system_clock::to_time_t(p);
|
void signal_handler(int signal) {
|
||||||
|
if (signal == SIGINT || signal == SIGTERM) {
|
||||||
|
std::cout << std::format("\nReceived signal, shutting down gracefully...")
|
||||||
|
<< std::endl;
|
||||||
|
svr.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_nginx_time_format() {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::put_time(std::localtime(&t), "%d/%b/%Y:%H:%M:%S %z");
|
ss << std::put_time(std::localtime(&time_t), "%d/%b/%Y:%H:%M:%S %z");
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string log(auto &req, auto &res) {
|
std::string get_nginx_error_time_format() {
|
||||||
auto remote_user = "-"; // TODO:
|
auto now = std::chrono::system_clock::now();
|
||||||
auto request = std::format("{} {} {}", req.method, req.path, req.version);
|
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||||||
auto body_bytes_sent = res.get_header_value("Content-Length");
|
|
||||||
auto http_referer = "-"; // TODO:
|
|
||||||
auto http_user_agent = req.get_header_value("User-Agent", "-");
|
|
||||||
|
|
||||||
// NOTE: From NGINX default access log format
|
std::stringstream ss;
|
||||||
// log_format combined '$remote_addr - $remote_user [$time_local] '
|
ss << std::put_time(std::localtime(&time_t), "%Y/%m/%d %H:%M:%S");
|
||||||
// '"$request" $status $body_bytes_sent '
|
return ss.str();
|
||||||
// '"$http_referer" "$http_user_agent"';
|
|
||||||
return std::format(R"({} - {} [{}] "{}" {} {} "{}" "{}")", req.remote_addr,
|
|
||||||
remote_user, time_local(), request, res.status,
|
|
||||||
body_bytes_sent, http_referer, http_user_agent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
std::string get_client_ip(const Request &req) {
|
||||||
signal(SIGINT, sigint_handler);
|
// Check for X-Forwarded-For header first (common in reverse proxy setups)
|
||||||
|
auto forwarded_for = req.get_header_value("X-Forwarded-For");
|
||||||
|
if (!forwarded_for.empty()) {
|
||||||
|
// Get the first IP if there are multiple
|
||||||
|
auto comma_pos = forwarded_for.find(',');
|
||||||
|
if (comma_pos != std::string::npos) {
|
||||||
|
return forwarded_for.substr(0, comma_pos);
|
||||||
|
}
|
||||||
|
return forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
auto base_dir = "./html";
|
// Check for X-Real-IP header
|
||||||
auto host = "0.0.0.0";
|
auto real_ip = req.get_header_value("X-Real-IP");
|
||||||
auto port = 80;
|
if (!real_ip.empty()) { return real_ip; }
|
||||||
|
|
||||||
httplib::Server svr;
|
// Fallback to remote address (though cpp-httplib doesn't provide this
|
||||||
|
// directly) For demonstration, we'll use a placeholder
|
||||||
|
return "127.0.0.1";
|
||||||
|
}
|
||||||
|
|
||||||
svr.set_error_handler([](auto & /*req*/, auto &res) {
|
// NGINX Combined log format:
|
||||||
auto body =
|
// $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent
|
||||||
std::format(error_html, res.status, httplib::status_message(res.status),
|
// "$http_referer" "$http_user_agent"
|
||||||
CPPHTTPLIB_VERSION);
|
void nginx_access_logger(const Request &req, const Response &res) {
|
||||||
|
std::string remote_addr = get_client_ip(req);
|
||||||
|
std::string remote_user =
|
||||||
|
"-"; // cpp-httplib doesn't have built-in auth user tracking
|
||||||
|
std::string time_local = get_nginx_time_format();
|
||||||
|
std::string request =
|
||||||
|
std::format("{} {} {}", req.method, req.path, req.version);
|
||||||
|
int status = res.status;
|
||||||
|
size_t body_bytes_sent = res.body.size();
|
||||||
|
std::string http_referer = req.get_header_value("Referer");
|
||||||
|
if (http_referer.empty()) http_referer = "-";
|
||||||
|
std::string http_user_agent = req.get_header_value("User-Agent");
|
||||||
|
if (http_user_agent.empty()) http_user_agent = "-";
|
||||||
|
|
||||||
res.set_content(body, "text/html");
|
std::cout << std::format("{} - {} [{}] \"{}\" {} {} \"{}\" \"{}\"",
|
||||||
});
|
remote_addr, remote_user, time_local, request,
|
||||||
|
status, body_bytes_sent, http_referer,
|
||||||
|
http_user_agent)
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
svr.set_logger(
|
// NGINX Error log format:
|
||||||
[](auto &req, auto &res) { std::cout << log(req, res) << std::endl; });
|
// YYYY/MM/DD HH:MM:SS [level] message, client: client_ip, request: "request",
|
||||||
|
// host: "host"
|
||||||
|
void nginx_error_logger(const Error &err, const Request *req) {
|
||||||
|
std::string time_local = get_nginx_error_time_format();
|
||||||
|
std::string level = "error";
|
||||||
|
|
||||||
svr.set_mount_point("/", base_dir);
|
if (req) {
|
||||||
|
std::string client_ip = get_client_ip(*req);
|
||||||
|
std::string request =
|
||||||
|
std::format("{} {} {}", req->method, req->path, req->version);
|
||||||
|
std::string host = req->get_header_value("Host");
|
||||||
|
if (host.empty()) host = "-";
|
||||||
|
|
||||||
std::cout << std::format("Serving HTTP on {0} port {1} ...", host, port)
|
std::cerr << std::format("{} [{}] {}, client: {}, request: "
|
||||||
|
"\"{}\", host: \"{}\"",
|
||||||
|
time_local, level, to_string(err), client_ip,
|
||||||
|
request, host)
|
||||||
|
<< std::endl;
|
||||||
|
} else {
|
||||||
|
// If no request context, just log the error
|
||||||
|
std::cerr << std::format("{} [{}] {}", time_local, level, to_string(err))
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_usage(const char *program_name) {
|
||||||
|
std::cout << std::format("Usage: {} <hostname> <port> <mount_point> "
|
||||||
|
"<document_root_directory>",
|
||||||
|
program_name)
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
auto ret = svr.listen(host, port);
|
std::cout << std::format("Example: {} localhost 8080 /var/www/html .",
|
||||||
|
program_name)
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc != 5) {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hostname = argv[1];
|
||||||
|
auto port = std::atoi(argv[2]);
|
||||||
|
std::string mount_point = argv[3];
|
||||||
|
std::string document_root = argv[4];
|
||||||
|
|
||||||
|
svr.set_logger(nginx_access_logger);
|
||||||
|
svr.set_error_logger(nginx_error_logger);
|
||||||
|
|
||||||
|
auto ret = svr.set_mount_point(mount_point, document_root);
|
||||||
|
if (!ret) {
|
||||||
|
std::cerr
|
||||||
|
<< std::format(
|
||||||
|
"Error: Cannot mount '{}' to '{}'. Directory may not exist.",
|
||||||
|
mount_point, document_root)
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("html", "text/html");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("htm", "text/html");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("css", "text/css");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("js", "text/javascript");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("json", "application/json");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("xml", "application/xml");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("png", "image/png");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("jpg", "image/jpeg");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("jpeg", "image/jpeg");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("gif", "image/gif");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("svg", "image/svg+xml");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("ico", "image/x-icon");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("pdf", "application/pdf");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("zip", "application/zip");
|
||||||
|
svr.set_file_extension_and_mimetype_mapping("txt", "text/plain");
|
||||||
|
|
||||||
|
svr.set_error_handler([](const Request & /*req*/, Response &res) {
|
||||||
|
if (res.status == 404) {
|
||||||
|
res.set_content(
|
||||||
|
std::format(
|
||||||
|
"<html><head><title>404 Not Found</title></head>"
|
||||||
|
"<body><h1>404 Not Found</h1>"
|
||||||
|
"<p>The requested resource was not found on this server.</p>"
|
||||||
|
"<hr><p>{}</p></body></html>",
|
||||||
|
SERVER_NAME),
|
||||||
|
"text/html");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.set_pre_routing_handler([](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_header("Server", SERVER_NAME);
|
||||||
|
return Server::HandlerResponse::Unhandled;
|
||||||
|
});
|
||||||
|
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
|
std::cout << std::format("Serving HTTP on {}:{}", hostname, port)
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << std::format("Mount point: {} -> {}", mount_point, document_root)
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << std::format("Press Ctrl+C to shutdown gracefully...")
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
ret = svr.listen(hostname, port);
|
||||||
|
|
||||||
|
std::cout << std::format("Server has been shut down.") << std::endl;
|
||||||
|
|
||||||
return ret ? 0 : 1;
|
return ret ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
228
httplib.h
228
httplib.h
@@ -949,6 +949,10 @@ private:
|
|||||||
|
|
||||||
using Logger = std::function<void(const Request &, const Response &)>;
|
using Logger = std::function<void(const Request &, const Response &)>;
|
||||||
|
|
||||||
|
// Forward declaration for Error type
|
||||||
|
enum class Error;
|
||||||
|
using ErrorLogger = std::function<void(const Error &, const Request *)>;
|
||||||
|
|
||||||
using SocketOptions = std::function<void(socket_t sock)>;
|
using SocketOptions = std::function<void(socket_t sock)>;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@@ -1109,6 +1113,7 @@ public:
|
|||||||
Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
|
Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
|
||||||
Server &set_logger(Logger logger);
|
Server &set_logger(Logger logger);
|
||||||
Server &set_pre_compression_logger(Logger logger);
|
Server &set_pre_compression_logger(Logger logger);
|
||||||
|
Server &set_error_logger(ErrorLogger error_logger);
|
||||||
|
|
||||||
Server &set_address_family(int family);
|
Server &set_address_family(int family);
|
||||||
Server &set_tcp_nodelay(bool on);
|
Server &set_tcp_nodelay(bool on);
|
||||||
@@ -1220,6 +1225,11 @@ private:
|
|||||||
|
|
||||||
virtual bool process_and_close_socket(socket_t sock);
|
virtual bool process_and_close_socket(socket_t sock);
|
||||||
|
|
||||||
|
void output_log(const Request &req, const Response &res) const;
|
||||||
|
void output_pre_compression_log(const Request &req,
|
||||||
|
const Response &res) const;
|
||||||
|
void output_error_log(const Error &err, const Request *req) const;
|
||||||
|
|
||||||
std::atomic<bool> is_running_{false};
|
std::atomic<bool> is_running_{false};
|
||||||
std::atomic<bool> is_decommissioned{false};
|
std::atomic<bool> is_decommissioned{false};
|
||||||
|
|
||||||
@@ -1251,8 +1261,10 @@ private:
|
|||||||
HandlerWithResponse pre_request_handler_;
|
HandlerWithResponse pre_request_handler_;
|
||||||
Expect100ContinueHandler expect_100_continue_handler_;
|
Expect100ContinueHandler expect_100_continue_handler_;
|
||||||
|
|
||||||
|
mutable std::mutex logger_mutex_;
|
||||||
Logger logger_;
|
Logger logger_;
|
||||||
Logger pre_compression_logger_;
|
Logger pre_compression_logger_;
|
||||||
|
ErrorLogger error_logger_;
|
||||||
|
|
||||||
int address_family_ = AF_UNSPEC;
|
int address_family_ = AF_UNSPEC;
|
||||||
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
|
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
|
||||||
@@ -1281,6 +1293,22 @@ enum class Error {
|
|||||||
Compression,
|
Compression,
|
||||||
ConnectionTimeout,
|
ConnectionTimeout,
|
||||||
ProxyConnection,
|
ProxyConnection,
|
||||||
|
ResourceExhaustion,
|
||||||
|
TooManyFormDataFiles,
|
||||||
|
ExceedMaxPayloadSize,
|
||||||
|
ExceedUriMaxLength,
|
||||||
|
ExceedMaxSocketDescriptorCount,
|
||||||
|
InvalidRequestLine,
|
||||||
|
InvalidHTTPMethod,
|
||||||
|
InvalidHTTPVersion,
|
||||||
|
InvalidHeaders,
|
||||||
|
MultipartParsing,
|
||||||
|
OpenFile,
|
||||||
|
Listen,
|
||||||
|
GetSockName,
|
||||||
|
UnsupportedAddressFamily,
|
||||||
|
HTTPParsing,
|
||||||
|
InvalidRangeHeader,
|
||||||
|
|
||||||
// For internal use only
|
// For internal use only
|
||||||
SSLPeerCouldBeClosed_,
|
SSLPeerCouldBeClosed_,
|
||||||
@@ -1525,6 +1553,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void set_logger(Logger logger);
|
void set_logger(Logger logger);
|
||||||
|
void set_error_logger(ErrorLogger error_logger);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct Socket {
|
struct Socket {
|
||||||
@@ -1557,6 +1586,9 @@ protected:
|
|||||||
|
|
||||||
void copy_settings(const ClientImpl &rhs);
|
void copy_settings(const ClientImpl &rhs);
|
||||||
|
|
||||||
|
void output_log(const Request &req, const Response &res) const;
|
||||||
|
void output_error_log(const Error &err, const Request *req) const;
|
||||||
|
|
||||||
// Socket endpoint information
|
// Socket endpoint information
|
||||||
const std::string host_;
|
const std::string host_;
|
||||||
const int port_;
|
const int port_;
|
||||||
@@ -1641,7 +1673,9 @@ protected:
|
|||||||
std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
|
std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
mutable std::mutex logger_mutex_;
|
||||||
Logger logger_;
|
Logger logger_;
|
||||||
|
ErrorLogger error_logger_;
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
int last_ssl_error_ = 0;
|
int last_ssl_error_ = 0;
|
||||||
@@ -1868,6 +1902,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void set_logger(Logger logger);
|
void set_logger(Logger logger);
|
||||||
|
void set_error_logger(ErrorLogger error_logger);
|
||||||
|
|
||||||
// SSL
|
// SSL
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
@@ -2199,6 +2234,7 @@ Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
|
|||||||
inline std::string to_string(const Error error) {
|
inline std::string to_string(const Error error) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case Error::Success: return "Success (no error)";
|
case Error::Success: return "Success (no error)";
|
||||||
|
case Error::Unknown: return "Unknown";
|
||||||
case Error::Connection: return "Could not establish connection";
|
case Error::Connection: return "Could not establish connection";
|
||||||
case Error::BindIPAddress: return "Failed to bind IP address";
|
case Error::BindIPAddress: return "Failed to bind IP address";
|
||||||
case Error::Read: return "Failed to read connection";
|
case Error::Read: return "Failed to read connection";
|
||||||
@@ -2215,7 +2251,23 @@ inline std::string to_string(const Error error) {
|
|||||||
case Error::Compression: return "Compression failed";
|
case Error::Compression: return "Compression failed";
|
||||||
case Error::ConnectionTimeout: return "Connection timed out";
|
case Error::ConnectionTimeout: return "Connection timed out";
|
||||||
case Error::ProxyConnection: return "Proxy connection failed";
|
case Error::ProxyConnection: return "Proxy connection failed";
|
||||||
case Error::Unknown: return "Unknown";
|
case Error::ResourceExhaustion: return "Resource exhaustion";
|
||||||
|
case Error::TooManyFormDataFiles: return "Too many form data files";
|
||||||
|
case Error::ExceedMaxPayloadSize: return "Exceeded maximum payload size";
|
||||||
|
case Error::ExceedUriMaxLength: return "Exceeded maximum URI length";
|
||||||
|
case Error::ExceedMaxSocketDescriptorCount:
|
||||||
|
return "Exceeded maximum socket descriptor count";
|
||||||
|
case Error::InvalidRequestLine: return "Invalid request line";
|
||||||
|
case Error::InvalidHTTPMethod: return "Invalid HTTP method";
|
||||||
|
case Error::InvalidHTTPVersion: return "Invalid HTTP version";
|
||||||
|
case Error::InvalidHeaders: return "Invalid headers";
|
||||||
|
case Error::MultipartParsing: return "Multipart parsing failed";
|
||||||
|
case Error::OpenFile: return "Failed to open file";
|
||||||
|
case Error::Listen: return "Failed to listen on socket";
|
||||||
|
case Error::GetSockName: return "Failed to get socket name";
|
||||||
|
case Error::UnsupportedAddressFamily: return "Unsupported address family";
|
||||||
|
case Error::HTTPParsing: return "HTTP parsing failed";
|
||||||
|
case Error::InvalidRangeHeader: return "Invalid Range header";
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7359,6 +7411,11 @@ inline Server &Server::set_logger(Logger logger) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Server &Server::set_error_logger(ErrorLogger error_logger) {
|
||||||
|
error_logger_ = std::move(error_logger);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline Server &Server::set_pre_compression_logger(Logger logger) {
|
inline Server &Server::set_pre_compression_logger(Logger logger) {
|
||||||
pre_compression_logger_ = std::move(logger);
|
pre_compression_logger_ = std::move(logger);
|
||||||
return *this;
|
return *this;
|
||||||
@@ -7498,9 +7555,15 @@ inline bool Server::parse_request_line(const char *s, Request &req) const {
|
|||||||
"GET", "HEAD", "POST", "PUT", "DELETE",
|
"GET", "HEAD", "POST", "PUT", "DELETE",
|
||||||
"CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
|
"CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
|
||||||
|
|
||||||
if (methods.find(req.method) == methods.end()) { return false; }
|
if (methods.find(req.method) == methods.end()) {
|
||||||
|
output_error_log(Error::InvalidHTTPMethod, &req);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }
|
if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
|
||||||
|
output_error_log(Error::InvalidHTTPVersion, &req);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Skip URL fragment
|
// Skip URL fragment
|
||||||
@@ -7607,7 +7670,7 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
if (logger_) { logger_(req, res); }
|
output_log(req, res);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -7683,6 +7746,7 @@ inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
|
|||||||
// Multipart FormData
|
// Multipart FormData
|
||||||
[&](const FormData &file) {
|
[&](const FormData &file) {
|
||||||
if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
|
if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
|
||||||
|
output_error_log(Error::TooManyFormDataFiles, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7712,6 +7776,7 @@ inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
|
|||||||
if (!content_type.find("application/x-www-form-urlencoded")) {
|
if (!content_type.find("application/x-www-form-urlencoded")) {
|
||||||
if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
|
if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
|
||||||
res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
|
res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
|
||||||
|
output_error_log(Error::ExceedMaxPayloadSize, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
detail::parse_query_text(req.body, req.params);
|
detail::parse_query_text(req.body, req.params);
|
||||||
@@ -7740,6 +7805,7 @@ inline bool Server::read_content_core(
|
|||||||
std::string boundary;
|
std::string boundary;
|
||||||
if (!detail::parse_multipart_boundary(content_type, boundary)) {
|
if (!detail::parse_multipart_boundary(content_type, boundary)) {
|
||||||
res.status = StatusCode::BadRequest_400;
|
res.status = StatusCode::BadRequest_400;
|
||||||
|
output_error_log(Error::MultipartParsing, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7765,6 +7831,7 @@ inline bool Server::read_content_core(
|
|||||||
if (req.is_multipart_form_data()) {
|
if (req.is_multipart_form_data()) {
|
||||||
if (!multipart_form_data_parser.is_valid()) {
|
if (!multipart_form_data_parser.is_valid()) {
|
||||||
res.status = StatusCode::BadRequest_400;
|
res.status = StatusCode::BadRequest_400;
|
||||||
|
output_error_log(Error::MultipartParsing, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7794,7 +7861,10 @@ inline bool Server::handle_file_request(const Request &req, Response &res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto mm = std::make_shared<detail::mmap>(path.c_str());
|
auto mm = std::make_shared<detail::mmap>(path.c_str());
|
||||||
if (!mm->is_open()) { return false; }
|
if (!mm->is_open()) {
|
||||||
|
output_error_log(Error::OpenFile, &req);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
mm->size(),
|
mm->size(),
|
||||||
@@ -7810,6 +7880,8 @@ inline bool Server::handle_file_request(const Request &req, Response &res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
output_error_log(Error::OpenFile, &req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7824,11 +7896,15 @@ Server::create_server_socket(const std::string &host, int port,
|
|||||||
return detail::create_socket(
|
return detail::create_socket(
|
||||||
host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
|
host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
|
||||||
ipv6_v6only_, std::move(socket_options),
|
ipv6_v6only_, std::move(socket_options),
|
||||||
[](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
|
[&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
|
||||||
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
|
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
|
||||||
|
output_error_log(Error::BindIPAddress, nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
|
||||||
|
output_error_log(Error::Listen, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -7847,6 +7923,7 @@ inline int Server::bind_internal(const std::string &host, int port,
|
|||||||
socklen_t addr_len = sizeof(addr);
|
socklen_t addr_len = sizeof(addr);
|
||||||
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
|
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
|
||||||
&addr_len) == -1) {
|
&addr_len) == -1) {
|
||||||
|
output_error_log(Error::GetSockName, nullptr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (addr.ss_family == AF_INET) {
|
if (addr.ss_family == AF_INET) {
|
||||||
@@ -7854,6 +7931,7 @@ inline int Server::bind_internal(const std::string &host, int port,
|
|||||||
} else if (addr.ss_family == AF_INET6) {
|
} else if (addr.ss_family == AF_INET6) {
|
||||||
return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
|
return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
|
||||||
} else {
|
} else {
|
||||||
|
output_error_log(Error::UnsupportedAddressFamily, nullptr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -7907,6 +7985,7 @@ inline bool Server::listen_internal() {
|
|||||||
if (svr_sock_ != INVALID_SOCKET) {
|
if (svr_sock_ != INVALID_SOCKET) {
|
||||||
detail::close_socket(svr_sock_);
|
detail::close_socket(svr_sock_);
|
||||||
ret = false;
|
ret = false;
|
||||||
|
output_error_log(Error::Connection, nullptr);
|
||||||
} else {
|
} else {
|
||||||
; // The server socket was closed by user.
|
; // The server socket was closed by user.
|
||||||
}
|
}
|
||||||
@@ -7920,6 +7999,7 @@ inline bool Server::listen_internal() {
|
|||||||
|
|
||||||
if (!task_queue->enqueue(
|
if (!task_queue->enqueue(
|
||||||
[this, sock]() { process_and_close_socket(sock); })) {
|
[this, sock]() { process_and_close_socket(sock); })) {
|
||||||
|
output_error_log(Error::ResourceExhaustion, nullptr);
|
||||||
detail::shutdown_socket(sock);
|
detail::shutdown_socket(sock);
|
||||||
detail::close_socket(sock);
|
detail::close_socket(sock);
|
||||||
}
|
}
|
||||||
@@ -7949,13 +8029,17 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
|||||||
{
|
{
|
||||||
ContentReader reader(
|
ContentReader reader(
|
||||||
[&](ContentReceiver receiver) {
|
[&](ContentReceiver receiver) {
|
||||||
return read_content_with_content_receiver(
|
auto result = read_content_with_content_receiver(
|
||||||
strm, req, res, std::move(receiver), nullptr, nullptr);
|
strm, req, res, std::move(receiver), nullptr, nullptr);
|
||||||
|
if (!result) { output_error_log(Error::Read, &req); }
|
||||||
|
return result;
|
||||||
},
|
},
|
||||||
[&](FormDataHeader header, ContentReceiver receiver) {
|
[&](FormDataHeader header, ContentReceiver receiver) {
|
||||||
return read_content_with_content_receiver(strm, req, res, nullptr,
|
auto result = read_content_with_content_receiver(
|
||||||
std::move(header),
|
strm, req, res, nullptr, std::move(header),
|
||||||
std::move(receiver));
|
std::move(receiver));
|
||||||
|
if (!result) { output_error_log(Error::Read, &req); }
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (req.method == "POST") {
|
if (req.method == "POST") {
|
||||||
@@ -7986,7 +8070,10 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read content into `req.body`
|
// Read content into `req.body`
|
||||||
if (!read_content(strm, req, res)) { return false; }
|
if (!read_content(strm, req, res)) {
|
||||||
|
output_error_log(Error::Read, &req);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular handler
|
// Regular handler
|
||||||
@@ -8100,7 +8187,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type != detail::EncodingType::None) {
|
if (type != detail::EncodingType::None) {
|
||||||
if (pre_compression_logger_) { pre_compression_logger_(req, res); }
|
output_pre_compression_log(req, res);
|
||||||
|
|
||||||
std::unique_ptr<detail::compressor> compressor;
|
std::unique_ptr<detail::compressor> compressor;
|
||||||
std::string content_encoding;
|
std::string content_encoding;
|
||||||
@@ -8184,14 +8271,22 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
|||||||
Headers dummy;
|
Headers dummy;
|
||||||
detail::read_headers(strm, dummy);
|
detail::read_headers(strm, dummy);
|
||||||
res.status = StatusCode::InternalServerError_500;
|
res.status = StatusCode::InternalServerError_500;
|
||||||
|
output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);
|
||||||
return write_response(strm, close_connection, req, res);
|
return write_response(strm, close_connection, req, res);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Request line and headers
|
// Request line and headers
|
||||||
if (!parse_request_line(line_reader.ptr(), req) ||
|
if (!parse_request_line(line_reader.ptr(), req)) {
|
||||||
!detail::read_headers(strm, req.headers)) {
|
|
||||||
res.status = StatusCode::BadRequest_400;
|
res.status = StatusCode::BadRequest_400;
|
||||||
|
output_error_log(Error::InvalidRequestLine, &req);
|
||||||
|
return write_response(strm, close_connection, req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request headers
|
||||||
|
if (!detail::read_headers(strm, req.headers)) {
|
||||||
|
res.status = StatusCode::BadRequest_400;
|
||||||
|
output_error_log(Error::InvalidHeaders, &req);
|
||||||
return write_response(strm, close_connection, req, res);
|
return write_response(strm, close_connection, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8200,6 +8295,7 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
|||||||
Headers dummy;
|
Headers dummy;
|
||||||
detail::read_headers(strm, dummy);
|
detail::read_headers(strm, dummy);
|
||||||
res.status = StatusCode::UriTooLong_414;
|
res.status = StatusCode::UriTooLong_414;
|
||||||
|
output_error_log(Error::ExceedUriMaxLength, &req);
|
||||||
return write_response(strm, close_connection, req, res);
|
return write_response(strm, close_connection, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8226,6 +8322,7 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
|||||||
const auto &accept_header = req.get_header_value("Accept");
|
const auto &accept_header = req.get_header_value("Accept");
|
||||||
if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
|
if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
|
||||||
res.status = StatusCode::BadRequest_400;
|
res.status = StatusCode::BadRequest_400;
|
||||||
|
output_error_log(Error::HTTPParsing, &req);
|
||||||
return write_response(strm, close_connection, req, res);
|
return write_response(strm, close_connection, req, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8234,6 +8331,7 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
|||||||
const auto &range_header_value = req.get_header_value("Range");
|
const auto &range_header_value = req.get_header_value("Range");
|
||||||
if (!detail::parse_range_header(range_header_value, req.ranges)) {
|
if (!detail::parse_range_header(range_header_value, req.ranges)) {
|
||||||
res.status = StatusCode::RangeNotSatisfiable_416;
|
res.status = StatusCode::RangeNotSatisfiable_416;
|
||||||
|
output_error_log(Error::InvalidRangeHeader, &req);
|
||||||
return write_response(strm, close_connection, req, res);
|
return write_response(strm, close_connection, req, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8314,6 +8412,7 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
|||||||
res.content_length_ = 0;
|
res.content_length_ = 0;
|
||||||
res.content_provider_ = nullptr;
|
res.content_provider_ = nullptr;
|
||||||
res.status = StatusCode::NotFound_404;
|
res.status = StatusCode::NotFound_404;
|
||||||
|
output_error_log(Error::OpenFile, &req);
|
||||||
return write_response(strm, close_connection, req, res);
|
return write_response(strm, close_connection, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8373,6 +8472,29 @@ inline bool Server::process_and_close_socket(socket_t sock) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Server::output_log(const Request &req, const Response &res) const {
|
||||||
|
if (logger_) {
|
||||||
|
std::lock_guard<std::mutex> guard(logger_mutex_);
|
||||||
|
logger_(req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Server::output_pre_compression_log(const Request &req,
|
||||||
|
const Response &res) const {
|
||||||
|
if (pre_compression_logger_) {
|
||||||
|
std::lock_guard<std::mutex> guard(logger_mutex_);
|
||||||
|
pre_compression_logger_(req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Server::output_error_log(const Error &err,
|
||||||
|
const Request *req) const {
|
||||||
|
if (error_logger_) {
|
||||||
|
std::lock_guard<std::mutex> guard(logger_mutex_);
|
||||||
|
error_logger_(err, req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP client implementation
|
// HTTP client implementation
|
||||||
inline ClientImpl::ClientImpl(const std::string &host)
|
inline ClientImpl::ClientImpl(const std::string &host)
|
||||||
: ClientImpl(host, 80, std::string(), std::string()) {}
|
: ClientImpl(host, 80, std::string(), std::string()) {}
|
||||||
@@ -8451,6 +8573,7 @@ inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
|
|||||||
server_certificate_verifier_ = rhs.server_certificate_verifier_;
|
server_certificate_verifier_ = rhs.server_certificate_verifier_;
|
||||||
#endif
|
#endif
|
||||||
logger_ = rhs.logger_;
|
logger_ = rhs.logger_;
|
||||||
|
error_logger_ = rhs.error_logger_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline socket_t ClientImpl::create_client_socket(Error &error) const {
|
inline socket_t ClientImpl::create_client_socket(Error &error) const {
|
||||||
@@ -8593,7 +8716,10 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!is_alive) {
|
if (!is_alive) {
|
||||||
if (!create_and_connect_socket(socket_, error)) { return false; }
|
if (!create_and_connect_socket(socket_, error)) {
|
||||||
|
output_error_log(error, &req);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
// TODO: refactoring
|
// TODO: refactoring
|
||||||
@@ -8603,11 +8729,15 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
|
|||||||
auto success = false;
|
auto success = false;
|
||||||
if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
|
if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
|
||||||
error)) {
|
error)) {
|
||||||
|
if (!success) { output_error_log(error, &req); }
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!scli.initialize_ssl(socket_, error)) { return false; }
|
if (!scli.initialize_ssl(socket_, error)) {
|
||||||
|
output_error_log(error, &req);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -8653,7 +8783,10 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
if (error == Error::Success) { error = Error::Unknown; }
|
if (error == Error::Success) {
|
||||||
|
error = Error::Unknown;
|
||||||
|
output_error_log(error, &req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -8681,6 +8814,7 @@ inline bool ClientImpl::handle_request(Stream &strm, Request &req,
|
|||||||
Error &error) {
|
Error &error) {
|
||||||
if (req.path.empty()) {
|
if (req.path.empty()) {
|
||||||
error = Error::Connection;
|
error = Error::Connection;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8756,6 +8890,7 @@ inline bool ClientImpl::handle_request(Stream &strm, Request &req,
|
|||||||
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
|
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
|
||||||
if (req.redirect_count_ == 0) {
|
if (req.redirect_count_ == 0) {
|
||||||
error = Error::ExceedRedirectCount;
|
error = Error::ExceedRedirectCount;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8859,6 +8994,7 @@ inline bool ClientImpl::create_redirect_client(
|
|||||||
#else
|
#else
|
||||||
// SSL not supported - set appropriate error
|
// SSL not supported - set appropriate error
|
||||||
error = Error::SSLConnection;
|
error = Error::SSLConnection;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
@@ -8931,6 +9067,7 @@ inline void ClientImpl::setup_redirect_client(ClientType &client) {
|
|||||||
|
|
||||||
// Copy logging and headers
|
// Copy logging and headers
|
||||||
if (logger_) { client.set_logger(logger_); }
|
if (logger_) { client.set_logger(logger_); }
|
||||||
|
if (error_logger_) { client.set_error_logger(error_logger_); }
|
||||||
|
|
||||||
// NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
|
// NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
|
||||||
// Each new client should generate its own headers based on its target host
|
// Each new client should generate its own headers based on its target host
|
||||||
@@ -9104,6 +9241,7 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
|||||||
auto &data = bstrm.get_buffer();
|
auto &data = bstrm.get_buffer();
|
||||||
if (!detail::write_data(strm, data.data(), data.size())) {
|
if (!detail::write_data(strm, data.data(), data.size())) {
|
||||||
error = Error::Write;
|
error = Error::Write;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9122,18 +9260,21 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
|||||||
size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
|
size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
|
||||||
if (!detail::write_data(strm, data + written, to_write)) {
|
if (!detail::write_data(strm, data + written, to_write)) {
|
||||||
error = Error::Write;
|
error = Error::Write;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
written += to_write;
|
written += to_write;
|
||||||
|
|
||||||
if (!req.upload_progress(written, body_size)) {
|
if (!req.upload_progress(written, body_size)) {
|
||||||
error = Error::Canceled;
|
error = Error::Canceled;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!detail::write_data(strm, req.body.data(), req.body.size())) {
|
if (!detail::write_data(strm, req.body.data(), req.body.size())) {
|
||||||
error = Error::Write;
|
error = Error::Write;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9185,6 +9326,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
|||||||
while (ok && offset < content_length) {
|
while (ok && offset < content_length) {
|
||||||
if (!content_provider(offset, content_length - offset, data_sink)) {
|
if (!content_provider(offset, content_length - offset, data_sink)) {
|
||||||
error = Error::Canceled;
|
error = Error::Canceled;
|
||||||
|
output_error_log(error, &req);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9195,6 +9337,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
|||||||
return true;
|
return true;
|
||||||
})) {
|
})) {
|
||||||
error = Error::Compression;
|
error = Error::Compression;
|
||||||
|
output_error_log(error, &req);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9254,6 +9397,22 @@ ClientImpl::adjust_host_string(const std::string &host) const {
|
|||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void ClientImpl::output_log(const Request &req,
|
||||||
|
const Response &res) const {
|
||||||
|
if (logger_) {
|
||||||
|
std::lock_guard<std::mutex> guard(logger_mutex_);
|
||||||
|
logger_(req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ClientImpl::output_error_log(const Error &err,
|
||||||
|
const Request *req) const {
|
||||||
|
if (error_logger_) {
|
||||||
|
std::lock_guard<std::mutex> guard(logger_mutex_);
|
||||||
|
error_logger_(err, req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
||||||
Response &res, bool close_connection,
|
Response &res, bool close_connection,
|
||||||
Error &error) {
|
Error &error) {
|
||||||
@@ -9266,6 +9425,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
if (!is_proxy_enabled) {
|
if (!is_proxy_enabled) {
|
||||||
if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
|
if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
|
||||||
error = Error::SSLPeerCouldBeClosed_;
|
error = Error::SSLPeerCouldBeClosed_;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9276,6 +9436,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
if (!read_response_line(strm, req, res) ||
|
if (!read_response_line(strm, req, res) ||
|
||||||
!detail::read_headers(strm, res.headers)) {
|
!detail::read_headers(strm, res.headers)) {
|
||||||
error = Error::Read;
|
error = Error::Read;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9289,6 +9450,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
if (req.response_handler && !redirect) {
|
if (req.response_handler && !redirect) {
|
||||||
if (!req.response_handler(res)) {
|
if (!req.response_handler(res)) {
|
||||||
error = Error::Canceled;
|
error = Error::Canceled;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9299,7 +9461,10 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
[&](const char *buf, size_t n, size_t off, size_t len) {
|
[&](const char *buf, size_t n, size_t off, size_t len) {
|
||||||
if (redirect) { return true; }
|
if (redirect) { return true; }
|
||||||
auto ret = req.content_receiver(buf, n, off, len);
|
auto ret = req.content_receiver(buf, n, off, len);
|
||||||
if (!ret) { error = Error::Canceled; }
|
if (!ret) {
|
||||||
|
error = Error::Canceled;
|
||||||
|
output_error_log(error, &req);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
})
|
})
|
||||||
: static_cast<ContentReceiverWithProgress>(
|
: static_cast<ContentReceiverWithProgress>(
|
||||||
@@ -9313,7 +9478,10 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
auto progress = [&](size_t current, size_t total) {
|
auto progress = [&](size_t current, size_t total) {
|
||||||
if (!req.download_progress || redirect) { return true; }
|
if (!req.download_progress || redirect) { return true; }
|
||||||
auto ret = req.download_progress(current, total);
|
auto ret = req.download_progress(current, total);
|
||||||
if (!ret) { error = Error::Canceled; }
|
if (!ret) {
|
||||||
|
error = Error::Canceled;
|
||||||
|
output_error_log(error, &req);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -9322,6 +9490,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
auto len = res.get_header_value_u64("Content-Length");
|
auto len = res.get_header_value_u64("Content-Length");
|
||||||
if (len > res.body.max_size()) {
|
if (len > res.body.max_size()) {
|
||||||
error = Error::Read;
|
error = Error::Read;
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
res.body.reserve(static_cast<size_t>(len));
|
res.body.reserve(static_cast<size_t>(len));
|
||||||
@@ -9334,13 +9503,14 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
|||||||
dummy_status, std::move(progress),
|
dummy_status, std::move(progress),
|
||||||
std::move(out), decompress_)) {
|
std::move(out), decompress_)) {
|
||||||
if (error != Error::Canceled) { error = Error::Read; }
|
if (error != Error::Canceled) { error = Error::Read; }
|
||||||
|
output_error_log(error, &req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
if (logger_) { logger_(req, res); }
|
output_log(req, res);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -10244,6 +10414,10 @@ inline void ClientImpl::set_logger(Logger logger) {
|
|||||||
logger_ = std::move(logger);
|
logger_ = std::move(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
|
||||||
|
error_logger_ = std::move(error_logger);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SSL Implementation
|
* SSL Implementation
|
||||||
*/
|
*/
|
||||||
@@ -10756,6 +10930,7 @@ inline bool SSLClient::connect_with_proxy(
|
|||||||
// Create a new socket for the authenticated CONNECT request
|
// Create a new socket for the authenticated CONNECT request
|
||||||
if (!create_and_connect_socket(socket, error)) {
|
if (!create_and_connect_socket(socket, error)) {
|
||||||
success = false;
|
success = false;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10793,6 +10968,7 @@ inline bool SSLClient::connect_with_proxy(
|
|||||||
// as the response of the request
|
// as the response of the request
|
||||||
if (proxy_res.status != StatusCode::OK_200) {
|
if (proxy_res.status != StatusCode::OK_200) {
|
||||||
error = Error::ProxyConnection;
|
error = Error::ProxyConnection;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
res = std::move(proxy_res);
|
res = std::move(proxy_res);
|
||||||
// Thread-safe to close everything because we are assuming there are
|
// Thread-safe to close everything because we are assuming there are
|
||||||
// no requests in flight
|
// no requests in flight
|
||||||
@@ -10845,6 +11021,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
|||||||
if (server_certificate_verification_) {
|
if (server_certificate_verification_) {
|
||||||
if (!load_certs()) {
|
if (!load_certs()) {
|
||||||
error = Error::SSLLoadingCerts;
|
error = Error::SSLLoadingCerts;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
|
SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
|
||||||
@@ -10854,6 +11031,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
|||||||
socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
|
socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
|
||||||
connection_timeout_usec_, &last_ssl_error_)) {
|
connection_timeout_usec_, &last_ssl_error_)) {
|
||||||
error = Error::SSLConnection;
|
error = Error::SSLConnection;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10867,6 +11045,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
|||||||
if (verification_status == SSLVerifierResponse::CertificateRejected) {
|
if (verification_status == SSLVerifierResponse::CertificateRejected) {
|
||||||
last_openssl_error_ = ERR_get_error();
|
last_openssl_error_ = ERR_get_error();
|
||||||
error = Error::SSLServerVerification;
|
error = Error::SSLServerVerification;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10876,6 +11055,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
|||||||
if (verify_result_ != X509_V_OK) {
|
if (verify_result_ != X509_V_OK) {
|
||||||
last_openssl_error_ = static_cast<unsigned long>(verify_result_);
|
last_openssl_error_ = static_cast<unsigned long>(verify_result_);
|
||||||
error = Error::SSLServerVerification;
|
error = Error::SSLServerVerification;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10885,6 +11065,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
|||||||
if (server_cert == nullptr) {
|
if (server_cert == nullptr) {
|
||||||
last_openssl_error_ = ERR_get_error();
|
last_openssl_error_ = ERR_get_error();
|
||||||
error = Error::SSLServerVerification;
|
error = Error::SSLServerVerification;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10892,6 +11073,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
|||||||
if (!verify_host(server_cert)) {
|
if (!verify_host(server_cert)) {
|
||||||
last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
|
last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
|
||||||
error = Error::SSLServerHostnameVerification;
|
error = Error::SSLServerHostnameVerification;
|
||||||
|
output_error_log(error, nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11658,6 +11840,10 @@ inline void Client::set_logger(Logger logger) {
|
|||||||
cli_->set_logger(std::move(logger));
|
cli_->set_logger(std::move(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Client::set_error_logger(ErrorLogger error_logger) {
|
||||||
|
cli_->set_error_logger(std::move(error_logger));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
|
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
|
||||||
const std::string &ca_cert_dir_path) {
|
const std::string &ca_cert_dir_path) {
|
||||||
|
Reference in New Issue
Block a user