Priority: P2
Describe the issue
wreq-ruby can bind outgoing sockets to a local address, but it cannot direct a hostname to a specific destination address while preserving that hostname in the URL, Host header, TLS SNI, and certificate verification.
DNS overrides are useful for testing a virtual host locally, selecting a known CDN edge, working around a resolver outage, and reproducing origin-specific behavior. Replacing the URL host with an IP address is not equivalent because it changes HTTP and TLS identity.
The underlying Rust client already supports one or more override addresses for a domain, so the first Ruby API can remain declarative and avoid Ruby callbacks from native resolver threads.
Proposed Ruby API
Use an explicit client-level map:
client = Wreq::Client.new(
dns_overrides: {
"example.com" => ["203.0.113.10", "2001:db8::10"]
}
)
client.get("https://example.com/path")
- Keys are hostname Strings, without a scheme, path, userinfo, or port.
- Values are one IP literal or an ordered Array of IP literals. Accepting
IPAddr objects in addition to Strings is a useful Ruby convenience.
- Preserve address order and try later addresses according to the client's documented connection/fallback behavior.
- The URL hostname continues to drive
Host, SNI, and certificate hostname verification.
- Validate the complete map during client construction; malformed hostnames or addresses raise before network I/O.
- Keep the initial API client-scoped and immutable. A request-level override would also need to prevent reuse of a connection created under a different mapping.
Proxy semantics
An override cannot control target resolution when an HTTP proxy or remote-DNS SOCKS proxy resolves the destination. That limitation must be explicit:
- document which proxy schemes resolve locally and can honor the override;
- state that the mapping is not consulted when the proxy owns target resolution; and
- never apply target-host overrides to resolution of the proxy server itself.
Client construction does not need to reject proxies and overrides merely because both are configured: one reusable client may make direct and proxied requests. The API must simply avoid claiming that a target override was applied on a path where resolution occurred remotely.
Arbitrary Ruby resolver callbacks are out of scope for the first version. They require a separate design for the GVL, Fiber scheduling, cancellation, caching, and callback exceptions.
Acceptance criteria
- A hostname can be connected to through one IPv4 or IPv6 override while HTTP/TLS identity remains the hostname.
- Multiple addresses have deterministic fallback behavior.
- Invalid keys and values raise during client construction.
- Connection pooling never reuses a socket established under a different DNS mapping.
- HTTPS tests prove that SNI and hostname verification are not changed to the IP literal.
- Proxy combinations follow documented local-versus-remote resolution rules and never misapply a target override to the proxy host.
- No Ruby callback is invoked from a native resolver thread.
Ecosystem precedent
Relevant source
Priority: P2
Describe the issue
wreq-ruby can bind outgoing sockets to a local address, but it cannot direct a hostname to a specific destination address while preserving that hostname in the URL,
Hostheader, TLS SNI, and certificate verification.DNS overrides are useful for testing a virtual host locally, selecting a known CDN edge, working around a resolver outage, and reproducing origin-specific behavior. Replacing the URL host with an IP address is not equivalent because it changes HTTP and TLS identity.
The underlying Rust client already supports one or more override addresses for a domain, so the first Ruby API can remain declarative and avoid Ruby callbacks from native resolver threads.
Proposed Ruby API
Use an explicit client-level map:
IPAddrobjects in addition to Strings is a useful Ruby convenience.Host, SNI, and certificate hostname verification.Proxy semantics
An override cannot control target resolution when an HTTP proxy or remote-DNS SOCKS proxy resolves the destination. That limitation must be explicit:
Client construction does not need to reject proxies and overrides merely because both are configured: one reusable client may make direct and proxied requests. The API must simply avoid claiming that a target override was applied on a path where resolution occurred remotely.
Arbitrary Ruby resolver callbacks are out of scope for the first version. They require a separate design for the GVL, Fiber scheduling, cancellation, caching, and callback exceptions.
Acceptance criteria
Ecosystem precedent
addresses:option connects a URL hostname through an ordered list of IP addresses while preserving the URL authority.--resolveprovides the same hostname-to-address override concept for deterministic HTTP/TLS requests.wreq-python'sResolverOptions#add_resolveexposes this capability over the same Rust resolver API.Relevant source
ClientBuilder#resolveand#resolve_to_addrssrc/client.rs