Skip to content

Commit

Permalink
feature symfony#59068 [HttpClient] Add IPv6 support to NativeHttpClie…
Browse files Browse the repository at this point in the history
…nt (dmitrii-baranov-tg)

This PR was squashed before being merged into the 7.3 branch.

Discussion
----------

[HttpClient] Add IPv6 support to NativeHttpClient

| Q             | A
| ------------- | ---
| Branch?       | 7.3
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Issues        | Fix symfony#59027
| License       | MIT

[HttpClient] Add IPv6 support to NativeHttpClient

At the moment, NativeHttpClient works only with IPv4 because of the way it does DNS resolution.
But by taking inspiration from DNS resolution in NoPrivateNetworkHttpClient, we are able to make it IPv6 compatible.

Commits
-------

44a7a58 [HttpClient] Add IPv6 support to NativeHttpClient
  • Loading branch information
nicolas-grekas committed Jan 2, 2025
2 parents a993465 + 44a7a58 commit 04ee771
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 5 deletions.
5 changes: 5 additions & 0 deletions src/Symfony/Component/HttpClient/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

7.3
---

* Add IPv6 support to `NativeHttpClient`

7.2
---

Expand Down
23 changes: 18 additions & 5 deletions src/Symfony/Component/HttpClient/NativeHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,25 +332,38 @@ private static function dnsResolve(string $host, NativeClientState $multi, array
{
$flag = '' !== $host && '[' === $host[0] && ']' === $host[-1] && str_contains($host, ':') ? \FILTER_FLAG_IPV6 : \FILTER_FLAG_IPV4;
$ip = \FILTER_FLAG_IPV6 === $flag ? substr($host, 1, -1) : $host;
$now = microtime(true);

if (filter_var($ip, \FILTER_VALIDATE_IP, $flag)) {
// The host is already an IP address
} elseif (null === $ip = $multi->dnsCache[$host] ?? null) {
$info['debug'] .= "* Hostname was NOT found in DNS cache\n";
$now = microtime(true);

if (!$ip = gethostbynamel($host)) {
if ($ip = gethostbynamel($host)) {
$ip = $ip[0];
} elseif (!\defined('STREAM_PF_INET6')) {
throw new TransportException(\sprintf('Could not resolve host "%s".', $host));
} elseif ($ip = dns_get_record($host, \DNS_AAAA)) {
$ip = $ip[0]['ipv6'];
} elseif (\extension_loaded('sockets')) {
if (!$addrInfo = socket_addrinfo_lookup($host, 0, ['ai_socktype' => \SOCK_STREAM, 'ai_family' => \AF_INET6])) {
throw new TransportException(\sprintf('Could not resolve host "%s".', $host));
}

$ip = socket_addrinfo_explain($addrInfo[0])['ai_addr']['sin6_addr'];
} elseif ('localhost' === $host || 'localhost.' === $host) {
$ip = '::1';
} else {
throw new TransportException(\sprintf('Could not resolve host "%s".', $host));
}

$multi->dnsCache[$host] = $ip = $ip[0];
$multi->dnsCache[$host] = $ip;
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
$host = $ip;
} else {
$info['debug'] .= "* Hostname was found in DNS cache\n";
$host = str_contains($ip, ':') ? "[$ip]" : $ip;
}

$host = str_contains($ip, ':') ? "[$ip]" : $ip;
$info['namelookup_time'] = microtime(true) - ($info['start_time'] ?: $now);
$info['primary_ip'] = $ip;

Expand Down
23 changes: 23 additions & 0 deletions src/Symfony/Component/HttpClient/Tests/NativeHttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

namespace Symfony\Component\HttpClient\Tests;

use Symfony\Bridge\PhpUnit\DnsMock;
use Symfony\Component\HttpClient\NativeHttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\Test\TestHttpServer;

/**
* @group dns-sensitive
Expand Down Expand Up @@ -48,4 +50,25 @@ public function testHttp2PushVulcainWithUnusedResponse()
{
$this->markTestSkipped('NativeHttpClient doesn\'t support HTTP/2.');
}

public function testIPv6Resolve()
{
TestHttpServer::start(-8087);

DnsMock::withMockedHosts([
'symfony.com' => [
[
'type' => 'AAAA',
'ipv6' => '::1',
],
],
]);

$client = $this->getHttpClient(__FUNCTION__);
$response = $client->request('GET', 'http://symfony.com:8087/');

$this->assertSame(200, $response->getStatusCode());

DnsMock::withMockedHosts([]);
}
}

0 comments on commit 04ee771

Please sign in to comment.