1
0
mirror of synced 2025-07-04 07:22:28 +03:00

Proxy problems (#2165)

* Fix proxy problems

* Auto redirect problem (http → https → https)
This commit is contained in:
yhirose
2025-06-28 00:14:01 -04:00
committed by GitHub
parent 696c02f2bc
commit e1ab5a604b
6 changed files with 180 additions and 29 deletions

171
httplib.h
View File

@ -1669,6 +1669,11 @@ private:
bool write_request(Stream &strm, Request &req, bool close_connection,
Error &error);
bool redirect(Request &req, Response &res, Error &error);
bool create_redirect_client(const std::string &scheme,
const std::string &host, int port, Request &req,
Response &res, const std::string &path,
const std::string &location, Error &error);
template <typename ClientType> void setup_redirect_client(ClientType &client);
bool handle_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error);
std::unique_ptr<Response> send_with_content_provider(
@ -8140,24 +8145,150 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
auto path = detail::decode_url(next_path, true) + next_query;
// Same host redirect - use current client
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
return detail::redirect(*this, req, res, path, location, error);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host, next_port);
cli.copy_settings(*this);
if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
return detail::redirect(cli, req, res, path, location, error);
#else
return false;
#endif
} else {
ClientImpl cli(next_host, next_port);
cli.copy_settings(*this);
return detail::redirect(cli, req, res, path, location, error);
}
// Cross-host/scheme redirect - create new client with robust setup
return create_redirect_client(next_scheme, next_host, next_port, req, res,
path, location, error);
}
// New method for robust redirect client creation
inline bool ClientImpl::create_redirect_client(
const std::string &scheme, const std::string &host, int port, Request &req,
Response &res, const std::string &path, const std::string &location,
Error &error) {
// Determine if we need SSL
auto need_ssl = (scheme == "https");
// Clean up request headers that are host/client specific
// Remove headers that should not be carried over to new host
auto headers_to_remove =
std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
for (const auto &header_name : headers_to_remove) {
auto it = req.headers.find(header_name);
while (it != req.headers.end()) {
it = req.headers.erase(it);
it = req.headers.find(header_name);
}
}
// Create appropriate client type and handle redirect
if (need_ssl) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
// Create SSL client for HTTPS redirect
SSLClient redirect_client(host, port);
// Setup basic client configuration first
setup_redirect_client(redirect_client);
// SSL-specific configuration for proxy environments
if (!proxy_host_.empty() && proxy_port_ != -1) {
// Critical: Disable SSL verification for proxy environments
redirect_client.enable_server_certificate_verification(false);
redirect_client.enable_server_hostname_verification(false);
} else {
// For direct SSL connections, copy SSL verification settings
redirect_client.enable_server_certificate_verification(
server_certificate_verification_);
redirect_client.enable_server_hostname_verification(
server_hostname_verification_);
}
// Handle CA certificate store and paths if available
if (ca_cert_store_) { redirect_client.set_ca_cert_store(ca_cert_store_); }
if (!ca_cert_file_path_.empty()) {
redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
}
// Client certificates are set through constructor for SSLClient
// NOTE: SSLClient constructor already takes client_cert_path and
// client_key_path so we need to create it properly if client certs are
// needed
// Execute the redirect
return detail::redirect(redirect_client, req, res, path, location, error);
#else
// SSL not supported - set appropriate error
error = Error::SSLConnection;
return false;
#endif
} else {
// HTTP redirect
ClientImpl redirect_client(host, port);
// Setup client with robust configuration
setup_redirect_client(redirect_client);
// Execute the redirect
return detail::redirect(redirect_client, req, res, path, location, error);
}
}
// New method for robust client setup (based on basic_manual_redirect.cpp logic)
template <typename ClientType>
inline void ClientImpl::setup_redirect_client(ClientType &client) {
// Copy basic settings first
client.set_connection_timeout(connection_timeout_sec_);
client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
client.set_keep_alive(keep_alive_);
client.set_follow_location(
true); // Enable redirects to handle multi-step redirects
client.set_url_encode(url_encode_);
client.set_compress(compress_);
client.set_decompress(decompress_);
// Copy authentication settings BEFORE proxy setup
if (!basic_auth_username_.empty()) {
client.set_basic_auth(basic_auth_username_, basic_auth_password_);
}
if (!bearer_token_auth_token_.empty()) {
client.set_bearer_token_auth(bearer_token_auth_token_);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (!digest_auth_username_.empty()) {
client.set_digest_auth(digest_auth_username_, digest_auth_password_);
}
#endif
// Setup proxy configuration (CRITICAL ORDER - proxy must be set
// before proxy auth)
if (!proxy_host_.empty() && proxy_port_ != -1) {
// First set proxy host and port
client.set_proxy(proxy_host_, proxy_port_);
// Then set proxy authentication (order matters!)
if (!proxy_basic_auth_username_.empty()) {
client.set_proxy_basic_auth(proxy_basic_auth_username_,
proxy_basic_auth_password_);
}
if (!proxy_bearer_token_auth_token_.empty()) {
client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (!proxy_digest_auth_username_.empty()) {
client.set_proxy_digest_auth(proxy_digest_auth_username_,
proxy_digest_auth_password_);
}
#endif
}
// Copy network and socket settings
client.set_address_family(address_family_);
client.set_tcp_nodelay(tcp_nodelay_);
client.set_ipv6_v6only(ipv6_v6only_);
if (socket_options_) { client.set_socket_options(socket_options_); }
if (!interface_.empty()) { client.set_interface(interface_); }
// Copy logging and headers
if (logger_) { client.set_logger(logger_); }
// 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
}
inline bool ClientImpl::write_content_with_provider(Stream &strm,
@ -9901,6 +10032,18 @@ inline bool SSLClient::connect_with_proxy(
!proxy_digest_auth_password_.empty()) {
std::map<std::string, std::string> auth;
if (detail::parse_www_authenticate(proxy_res, auth, true)) {
// Close the current socket and create a new one for the authenticated
// request
shutdown_ssl(socket, true);
shutdown_socket(socket);
close_socket(socket);
// Create a new socket for the authenticated CONNECT request
if (!create_and_connect_socket(socket, error)) {
success = false;
return false;
}
proxy_res = Response();
if (!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_,