Keep a redirect on the unix socket in ClientImpl::redirect (#2658)

A client using set_address_family(AF_UNIX) took the host of an absolute Location as a socket path, so a redirect to http://localhost/next failed and one to http://other.sock/next went to ./other.sock. Every hop now stays on the client's socket, as with curl's --unix-socket.

The same host is still decided from the Location, as curl does: only localhost (the Host this client sends) counts as this client, so a redirect to any other host goes through the cross-host client and drops Authorization and Cookie. That client reaches the original socket through addr_map_, and a host the caller mapped with set_direct_hostname_addr_map keeps its mapping. An https Location runs TLS over the socket with the Location's host.

The mapped-target lookup shared with make_connect_request_target is factored into detail::addr_map_target.
This commit is contained in:
metsw24-max authored and GitHub committed 2026-11-07 10:22:00 -05:00
1 parent 7e5d0683ac
commit 6d8dec3c3e
3 files changed
+98 -5

No files matched your search

+26 -5
View File
@@ -13093,14 +13093,22 @@ make_host_and_port_string_always_port(const std::string &host, int port) {
return prepare_host_string(host) + ":" + std::to_string(port);
}
// Where a client connects for `host`, as one string: the mapped address or
// name, or `host` itself.
inline std::string
addr_map_target(const std::map<std::string, std::string> &addr_map,
const std::string &host) {
std::string connect_host;
std::string ip;
apply_addr_map(addr_map, host, connect_host, ip);
return ip.empty() ? connect_host : ip;
}
// Request target of a CONNECT for `host`, with `addr_map` applied.
inline std::string
make_connect_request_target(const std::map<std::string, std::string> &addr_map,
const std::string &host, int port) {
std::string connect_host;
std::string ip;
apply_addr_map(addr_map, host, connect_host, ip);
return make_host_and_port_string_always_port(ip.empty() ? connect_host : ip,
return make_host_and_port_string_always_port(addr_map_target(addr_map, host),
port);
}
@@ -16488,8 +16496,13 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
auto path = std::move(next_path) + next_query;
// A Unix socket client sends "Host: localhost", so a Location naming
// localhost names this client too.
auto is_same_host = next_host == host_ ||
(address_family_ == AF_UNIX && next_host == "localhost");
// Same host redirect - use current client
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
if (next_scheme == scheme && is_same_host && next_port == port_) {
return detail::redirect(*this, req, res, path, location, error);
}
@@ -16640,6 +16653,14 @@ inline void ClientImpl::setup_redirect_client(ClientType &client) {
// Copy network and socket settings
client.addr_map_ = addr_map_;
client.tunnel_addr_map_ = tunnel_addr_map_;
// A Unix socket client reaches every host through its own socket, as curl's
// --unix-socket does, unless the caller mapped that host elsewhere. The
// Location's host is never taken as a socket path.
if (address_family_ == AF_UNIX) {
auto &target = client.addr_map_[client.host()];
if (target.empty()) { target = detail::addr_map_target(addr_map_, host_); }
}
client.set_address_family(address_family_);
client.set_tcp_nodelay(tcp_nodelay_);
client.set_ipv6_v6only(ipv6_v6only_);