From e19f0c6326c2e0c541366bf26ae0ffd041f88729 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 18 Nov 2024 17:08:46 +0100 Subject: [PATCH] [HttpClient] Fix option "bindto" with IPv6 addresses --- .../Component/HttpClient/CurlHttpClient.php | 2 +- .../Component/HttpClient/NativeHttpClient.php | 4 ++- .../HttpClient/Tests/CurlHttpClientTest.php | 15 --------- .../HttpClient/Test/Fixtures/web/index.php | 32 +++++++++++-------- .../HttpClient/Test/HttpClientTestCase.php | 27 ++++++++++++++++ .../HttpClient/Test/TestHttpServer.php | 6 ++-- 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 649f87b17c1e1..41d2d0a3e0136 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -274,7 +274,7 @@ public function request(string $method, string $url, array $options = []): Respo if (file_exists($options['bindto'])) { $curlopts[\CURLOPT_UNIX_SOCKET_PATH] = $options['bindto']; } elseif (!str_starts_with($options['bindto'], 'if!') && preg_match('/^(.*):(\d+)$/', $options['bindto'], $matches)) { - $curlopts[\CURLOPT_INTERFACE] = $matches[1]; + $curlopts[\CURLOPT_INTERFACE] = trim($matches[1], '[]'); $curlopts[\CURLOPT_LOCALPORT] = $matches[2]; } else { $curlopts[\CURLOPT_INTERFACE] = $options['bindto']; diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 5e4bb64ee27cd..42c8be1bd8f27 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -334,7 +334,9 @@ private static function dnsResolve($host, NativeClientState $multi, array &$info $info['debug'] .= "* Hostname was NOT found in DNS cache\n"; $now = microtime(true); - if (!$ip = gethostbynamel($host)) { + if ('[' === $host[0] && ']' === $host[-1] && filter_var(substr($host, 1, -1), \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + $ip = [$host]; + } elseif (!$ip = gethostbynamel($host)) { throw new TransportException(sprintf('Could not resolve host "%s".', $host)); } diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php index d8165705ca111..7e9aab212364c 100644 --- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php @@ -37,21 +37,6 @@ protected function getHttpClient(string $testCase): HttpClientInterface return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false], 6, 50); } - public function testBindToPort() - { - $client = $this->getHttpClient(__FUNCTION__); - $response = $client->request('GET', 'http://localhost:8057', ['bindto' => '127.0.0.1:9876']); - $response->getStatusCode(); - - $r = new \ReflectionProperty($response, 'handle'); - $r->setAccessible(true); - - $curlInfo = curl_getinfo($r->getValue($response)); - - self::assertSame('127.0.0.1', $curlInfo['local_ip']); - self::assertSame(9876, $curlInfo['local_port']); - } - public function testTimeoutIsNotAFatalError() { if ('\\' === \DIRECTORY_SEPARATOR) { diff --git a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php index fafab198aafe6..db4d5519e40e6 100644 --- a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php +++ b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php @@ -12,20 +12,26 @@ $_POST['content-type'] = $_SERVER['HTTP_CONTENT_TYPE'] ?? '?'; } +$headers = [ + 'SERVER_PROTOCOL', + 'SERVER_NAME', + 'REQUEST_URI', + 'REQUEST_METHOD', + 'PHP_AUTH_USER', + 'PHP_AUTH_PW', + 'REMOTE_ADDR', + 'REMOTE_PORT', +]; + +foreach ($headers as $k) { + if (isset($_SERVER[$k])) { + $vars[$k] = $_SERVER[$k]; + } +} + foreach ($_SERVER as $k => $v) { - switch ($k) { - default: - if (0 !== strpos($k, 'HTTP_')) { - continue 2; - } - // no break - case 'SERVER_NAME': - case 'SERVER_PROTOCOL': - case 'REQUEST_URI': - case 'REQUEST_METHOD': - case 'PHP_AUTH_USER': - case 'PHP_AUTH_PW': - $vars[$k] = $v; + if (0 === strpos($k, 'HTTP_')) { + $vars[$k] = $v; } } diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php index 10c6395c6acf8..eb10dbedbdbaf 100644 --- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php +++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php @@ -36,6 +36,7 @@ public static function tearDownAfterClass(): void { TestHttpServer::stop(8067); TestHttpServer::stop(8077); + TestHttpServer::stop(8087); } abstract protected function getHttpClient(string $testCase): HttpClientInterface; @@ -1152,4 +1153,30 @@ public function testWithOptions() $response = $client2->request('GET', '/'); $this->assertSame(200, $response->getStatusCode()); } + + public function testBindToPort() + { + $client = $this->getHttpClient(__FUNCTION__); + $response = $client->request('GET', 'http://localhost:8057', ['bindto' => '127.0.0.1:9876']); + $response->getStatusCode(); + + $vars = $response->toArray(); + + self::assertSame('127.0.0.1', $vars['REMOTE_ADDR']); + self::assertSame('9876', $vars['REMOTE_PORT']); + } + + public function testBindToPortV6() + { + TestHttpServer::start(8087, '[::1]'); + + $client = $this->getHttpClient(__FUNCTION__); + $response = $client->request('GET', 'http://[::1]:8087', ['bindto' => '[::1]:9876']); + $response->getStatusCode(); + + $vars = $response->toArray(); + + self::assertSame('::1', $vars['REMOTE_ADDR']); + self::assertSame('9876', $vars['REMOTE_PORT']); + } } diff --git a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php index 463b4b75c60ad..d8b828c932484 100644 --- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php +++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php @@ -21,7 +21,7 @@ class TestHttpServer /** * @return Process */ - public static function start(int $port = 8057) + public static function start(int $port = 8057, $ip = '127.0.0.1') { if (isset(self::$process[$port])) { self::$process[$port]->stop(); @@ -32,14 +32,14 @@ public static function start(int $port = 8057) } $finder = new PhpExecutableFinder(); - $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:'.$port])); + $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', $ip.':'.$port])); $process->setWorkingDirectory(__DIR__.'/Fixtures/web'); $process->start(); self::$process[$port] = $process; do { usleep(50000); - } while (!@fopen('http://127.0.0.1:'.$port, 'r')); + } while (!@fopen('http://'.$ip.':'.$port, 'r')); return $process; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy