Requests
Requests copied to clipboard
Add Host Bindings feature
Pull Request Type
This is a:
- [ ] Bug fix
- [x] New feature
- [ ] Documentation improvement
- [ ] Code quality improvement
Context
This PR adds support for binding specific hostnames to specific IP addresses, bypassing DNS resolution.
This is useful for:
- Testing against specific server instances
- Load balancing and failover scenarios
- Connecting to servers via specific IPs while preserving the original hostname in the
Hostheader - Development environments where DNS may not be configured
- Preventing rebinding attacks
Detailed Description
New HostBindings utility class
A value object that validates and stores hostname-to-IP mappings. Key features:
-
Security by default: IP addresses are validated using
filter_var(FILTER_VALIDATE_IP)to prevent hostname injection attacks. Accepts both IPv4 and IPv6 addresses (including private/localhost ranges). -
Opt-out mechanism: For pre-validated inputs or non-standard formats, validation can be skipped via
HostBindings::SKIP_IP_VALIDATION(use with caution). - Normalization: Whitespace is trimmed from IP addresses.
Methods:
-
has_host($host)- Check if a host has a mapping -
get_first_ip_for_host($host)- Get the first IP for a host (throwsUnknownHostorMissingIpAddressif not available) -
get_all_ips_for_host($host)- Get all IPs for a host (throwsUnknownHostif not found)
New exceptions
-
UnknownHost- Thrown when requesting a host that isn't in the bindings -
MissingIpAddress- Thrown when a host has no IP addresses configured
New HOST_BINDINGS capability
Added to the Capability interface to allow transport capability testing.
Transport integration
Curl transport (Transport\Curl):
- Uses
CURLOPT_CONNECT_TO(cURL 7.49.0+) as the preferred method - Falls back to
CURLOPT_RESOLVE(cURL 7.21.3+) for older cURL versions - Falls back to URL rewriting for HTTP (not HTTPS) on very old cURL versions
- Properly handles IPv6 addresses by wrapping them in square brackets
- Preserves the original hostname in the
Hostheader
Fsockopen transport (Transport\Fsockopen):
- Replaces the connection host with the mapped IP address
- Original hostname is preserved in the
Hostheader
Usage
The host_bindings option accepts either an array (with automatic IP validation) or a pre-constructed HostBindings object:
// Using an array (IPs are validated)
$response = Requests::get('https://example.com/api', [], [
'host_bindings' => [
'example.com' => ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'],
],
]);
// Using a HostBindings object (for advanced control)
$bindings = new HostBindings(
['example.com' => ['custom-value']],
HostBindings::SKIP_IP_VALIDATION
);
$response = Requests::get('https://example.com/api', [], [
'host_bindings' => $bindings,
]);
Includes
- Comprehensive unit tests for
HostBindingsclass (constructor validation, all methods) - Unit tests for new exceptions
- Integration tests in
Transport\BaseTestCasecovering both transports - Documentation in
Requests::request()docblock