Skip to content

Commit 04ee771

Browse files
feature #59068 [HttpClient] Add IPv6 support to NativeHttpClient (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 #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
2 parents a993465 + 44a7a58 commit 04ee771

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

src/Symfony/Component/HttpClient/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.3
5+
---
6+
7+
* Add IPv6 support to `NativeHttpClient`
8+
49
7.2
510
---
611

src/Symfony/Component/HttpClient/NativeHttpClient.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,25 +332,38 @@ private static function dnsResolve(string $host, NativeClientState $multi, array
332332
{
333333
$flag = '' !== $host && '[' === $host[0] && ']' === $host[-1] && str_contains($host, ':') ? \FILTER_FLAG_IPV6 : \FILTER_FLAG_IPV4;
334334
$ip = \FILTER_FLAG_IPV6 === $flag ? substr($host, 1, -1) : $host;
335+
$now = microtime(true);
335336

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

342-
if (!$ip = gethostbynamel($host)) {
342+
if ($ip = gethostbynamel($host)) {
343+
$ip = $ip[0];
344+
} elseif (!\defined('STREAM_PF_INET6')) {
345+
throw new TransportException(\sprintf('Could not resolve host "%s".', $host));
346+
} elseif ($ip = dns_get_record($host, \DNS_AAAA)) {
347+
$ip = $ip[0]['ipv6'];
348+
} elseif (\extension_loaded('sockets')) {
349+
if (!$addrInfo = socket_addrinfo_lookup($host, 0, ['ai_socktype' => \SOCK_STREAM, 'ai_family' => \AF_INET6])) {
350+
throw new TransportException(\sprintf('Could not resolve host "%s".', $host));
351+
}
352+
353+
$ip = socket_addrinfo_explain($addrInfo[0])['ai_addr']['sin6_addr'];
354+
} elseif ('localhost' === $host || 'localhost.' === $host) {
355+
$ip = '::1';
356+
} else {
343357
throw new TransportException(\sprintf('Could not resolve host "%s".', $host));
344358
}
345359

346-
$multi->dnsCache[$host] = $ip = $ip[0];
360+
$multi->dnsCache[$host] = $ip;
347361
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
348-
$host = $ip;
349362
} else {
350363
$info['debug'] .= "* Hostname was found in DNS cache\n";
351-
$host = str_contains($ip, ':') ? "[$ip]" : $ip;
352364
}
353365

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

src/Symfony/Component/HttpClient/Tests/NativeHttpClientTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
namespace Symfony\Component\HttpClient\Tests;
1313

14+
use Symfony\Bridge\PhpUnit\DnsMock;
1415
use Symfony\Component\HttpClient\NativeHttpClient;
1516
use Symfony\Contracts\HttpClient\HttpClientInterface;
17+
use Symfony\Contracts\HttpClient\Test\TestHttpServer;
1618

1719
/**
1820
* @group dns-sensitive
@@ -48,4 +50,25 @@ public function testHttp2PushVulcainWithUnusedResponse()
4850
{
4951
$this->markTestSkipped('NativeHttpClient doesn\'t support HTTP/2.');
5052
}
53+
54+
public function testIPv6Resolve()
55+
{
56+
TestHttpServer::start(-8087);
57+
58+
DnsMock::withMockedHosts([
59+
'symfony.com' => [
60+
[
61+
'type' => 'AAAA',
62+
'ipv6' => '::1',
63+
],
64+
],
65+
]);
66+
67+
$client = $this->getHttpClient(__FUNCTION__);
68+
$response = $client->request('GET', 'http://symfony.com:8087/');
69+
70+
$this->assertSame(200, $response->getStatusCode());
71+
72+
DnsMock::withMockedHosts([]);
73+
}
5174
}

0 commit comments

Comments
 (0)