Skip to content

Commit dc3afc8

Browse files
committed
Auto redirect problem (http → https → https)
1 parent 1b71f23 commit dc3afc8

File tree

2 files changed

+143
-12
lines changed

2 files changed

+143
-12
lines changed

httplib.h

Lines changed: 142 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,11 @@ class ClientImpl {
16331633
bool write_request(Stream &strm, Request &req, bool close_connection,
16341634
Error &error);
16351635
bool redirect(Request &req, Response &res, Error &error);
1636+
bool create_redirect_client(const std::string &scheme,
1637+
const std::string &host, int port, Request &req,
1638+
Response &res, const std::string &path,
1639+
const std::string &location, Error &error);
1640+
template <typename ClientType> void setup_redirect_client(ClientType &client);
16361641
bool handle_request(Stream &strm, Request &req, Response &res,
16371642
bool close_connection, Error &error);
16381643
std::unique_ptr<Response> send_with_content_provider(
@@ -7871,24 +7876,150 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
78717876

78727877
auto path = detail::decode_url(next_path, true) + next_query;
78737878

7879+
// Same host redirect - use current client
78747880
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
78757881
return detail::redirect(*this, req, res, path, location, error);
7876-
} else {
7877-
if (next_scheme == "https") {
7882+
}
7883+
7884+
// Cross-host/scheme redirect - create new client with robust setup
7885+
return create_redirect_client(next_scheme, next_host, next_port, req, res,
7886+
path, location, error);
7887+
}
7888+
7889+
// New method for robust redirect client creation
7890+
inline bool ClientImpl::create_redirect_client(
7891+
const std::string &scheme, const std::string &host, int port, Request &req,
7892+
Response &res, const std::string &path, const std::string &location,
7893+
Error &error) {
7894+
// Determine if we need SSL
7895+
auto need_ssl = (scheme == "https");
7896+
7897+
// Clean up request headers that are host/client specific
7898+
// Remove headers that should not be carried over to new host
7899+
auto headers_to_remove =
7900+
std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
7901+
7902+
for (const auto &header_name : headers_to_remove) {
7903+
auto it = req.headers.find(header_name);
7904+
while (it != req.headers.end()) {
7905+
it = req.headers.erase(it);
7906+
it = req.headers.find(header_name);
7907+
}
7908+
}
7909+
7910+
// Create appropriate client type and handle redirect
7911+
if (need_ssl) {
78787912
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7879-
SSLClient cli(next_host, next_port);
7880-
cli.copy_settings(*this);
7881-
if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
7882-
return detail::redirect(cli, req, res, path, location, error);
7913+
// Create SSL client for HTTPS redirect
7914+
SSLClient redirect_client(host, port);
7915+
7916+
// Setup basic client configuration first
7917+
setup_redirect_client(redirect_client);
7918+
7919+
// SSL-specific configuration for proxy environments
7920+
if (!proxy_host_.empty() && proxy_port_ != -1) {
7921+
// Critical: Disable SSL verification for proxy environments
7922+
redirect_client.enable_server_certificate_verification(false);
7923+
redirect_client.enable_server_hostname_verification(false);
7924+
} else {
7925+
// For direct SSL connections, copy SSL verification settings
7926+
redirect_client.enable_server_certificate_verification(
7927+
server_certificate_verification_);
7928+
redirect_client.enable_server_hostname_verification(
7929+
server_hostname_verification_);
7930+
}
7931+
7932+
// Handle CA certificate store and paths if available
7933+
if (ca_cert_store_) { redirect_client.set_ca_cert_store(ca_cert_store_); }
7934+
if (!ca_cert_file_path_.empty()) {
7935+
redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
7936+
}
7937+
7938+
// Client certificates are set through constructor for SSLClient
7939+
// NOTE: SSLClient constructor already takes client_cert_path and
7940+
// client_key_path so we need to create it properly if client certs are
7941+
// needed
7942+
7943+
// Execute the redirect
7944+
return detail::redirect(redirect_client, req, res, path, location, error);
78837945
#else
7884-
return false;
7946+
// SSL not supported - set appropriate error
7947+
error = Error::SSLConnection;
7948+
return false;
78857949
#endif
7886-
} else {
7887-
ClientImpl cli(next_host, next_port);
7888-
cli.copy_settings(*this);
7889-
return detail::redirect(cli, req, res, path, location, error);
7950+
} else {
7951+
// HTTP redirect
7952+
ClientImpl redirect_client(host, port);
7953+
7954+
// Setup client with robust configuration
7955+
setup_redirect_client(redirect_client);
7956+
7957+
// Execute the redirect
7958+
return detail::redirect(redirect_client, req, res, path, location, error);
7959+
}
7960+
}
7961+
7962+
// New method for robust client setup (based on basic_manual_redirect.cpp logic)
7963+
template <typename ClientType>
7964+
inline void ClientImpl::setup_redirect_client(ClientType &client) {
7965+
// Copy basic settings first
7966+
client.set_connection_timeout(connection_timeout_sec_);
7967+
client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
7968+
client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
7969+
client.set_keep_alive(keep_alive_);
7970+
client.set_follow_location(
7971+
true); // Enable redirects to handle multi-step redirects
7972+
client.set_url_encode(url_encode_);
7973+
client.set_compress(compress_);
7974+
client.set_decompress(decompress_);
7975+
7976+
// Copy authentication settings BEFORE proxy setup
7977+
if (!basic_auth_username_.empty()) {
7978+
client.set_basic_auth(basic_auth_username_, basic_auth_password_);
7979+
}
7980+
if (!bearer_token_auth_token_.empty()) {
7981+
client.set_bearer_token_auth(bearer_token_auth_token_);
7982+
}
7983+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7984+
if (!digest_auth_username_.empty()) {
7985+
client.set_digest_auth(digest_auth_username_, digest_auth_password_);
7986+
}
7987+
#endif
7988+
7989+
// Setup proxy configuration (CRITICAL ORDER - proxy must be set
7990+
// before proxy auth)
7991+
if (!proxy_host_.empty() && proxy_port_ != -1) {
7992+
// First set proxy host and port
7993+
client.set_proxy(proxy_host_, proxy_port_);
7994+
7995+
// Then set proxy authentication (order matters!)
7996+
if (!proxy_basic_auth_username_.empty()) {
7997+
client.set_proxy_basic_auth(proxy_basic_auth_username_,
7998+
proxy_basic_auth_password_);
7999+
}
8000+
if (!proxy_bearer_token_auth_token_.empty()) {
8001+
client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
78908002
}
8003+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8004+
if (!proxy_digest_auth_username_.empty()) {
8005+
client.set_proxy_digest_auth(proxy_digest_auth_username_,
8006+
proxy_digest_auth_password_);
8007+
}
8008+
#endif
78918009
}
8010+
8011+
// Copy network and socket settings
8012+
client.set_address_family(address_family_);
8013+
client.set_tcp_nodelay(tcp_nodelay_);
8014+
client.set_ipv6_v6only(ipv6_v6only_);
8015+
if (socket_options_) { client.set_socket_options(socket_options_); }
8016+
if (!interface_.empty()) { client.set_interface(interface_); }
8017+
8018+
// Copy logging and headers
8019+
if (logger_) { client.set_logger(logger_); }
8020+
8021+
// NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
8022+
// Each new client should generate its own headers based on its target host
78928023
}
78938024

78948025
inline bool ClientImpl::write_content_with_provider(Stream &strm,

test/test_proxy.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ TEST(RedirectTest, YouTubeNoSSLBasic) {
9090
RedirectProxyText(cli, "/", true);
9191
}
9292

93-
TEST(RedirectTest, DISABLED_YouTubeNoSSLDigest) {
93+
TEST(RedirectTest, YouTubeNoSSLDigest) {
9494
Client cli("youtube.com");
9595
RedirectProxyText(cli, "/", false);
9696
}

0 commit comments

Comments
 (0)