Skip to content

Commit 44a7a58

Browse files
dmitrii-baranov-tgnicolas-grekas
authored andcommitted
[HttpClient] Add IPv6 support to NativeHttpClient
1 parent 73d4904 commit 44a7a58

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)