Skip to content

Commit d843ada

Browse files
Merge branch '5.4' into 6.4
* 5.4: [HttpClient] Fix option "resolve" with IPv6 addresses Fix twig deprecations in web profiler twig files [HttpClient] Fix option "bindto" with IPv6 addresses fixes #58904 [Validator] review latvian translations
2 parents 6322705 + 582cf3a commit d843ada

File tree

6 files changed

+46
-25
lines changed

6 files changed

+46
-25
lines changed

CurlHttpClient.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ public function request(string $method, string $url, array $options = []): Respo
278278
if (file_exists($options['bindto'])) {
279279
$curlopts[\CURLOPT_UNIX_SOCKET_PATH] = $options['bindto'];
280280
} elseif (!str_starts_with($options['bindto'], 'if!') && preg_match('/^(.*):(\d+)$/', $options['bindto'], $matches)) {
281-
$curlopts[\CURLOPT_INTERFACE] = $matches[1];
281+
$curlopts[\CURLOPT_INTERFACE] = trim($matches[1], '[]');
282282
$curlopts[\CURLOPT_LOCALPORT] = $matches[2];
283283
} else {
284284
$curlopts[\CURLOPT_INTERFACE] = $options['bindto'];

HttpClientTrait.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,14 @@ private static function mergeDefaultOptions(array $options, array $defaultOption
207207
if ($resolve = $options['resolve'] ?? false) {
208208
$options['resolve'] = [];
209209
foreach ($resolve as $k => $v) {
210-
$options['resolve'][substr(self::parseUrl('http://'.$k)['authority'], 2)] = (string) $v;
210+
if ('' === $v = (string) $v) {
211+
throw new InvalidArgumentException(sprintf('Option "resolve" for host "%s" cannot be empty.', $k));
212+
}
213+
if ('[' === $v[0] && ']' === substr($v, -1) && str_contains($v, ':')) {
214+
$v = substr($v, 1, -1);
215+
}
216+
217+
$options['resolve'][substr(self::parseUrl('http://'.$k)['authority'], 2)] = $v;
211218
}
212219
}
213220

@@ -230,7 +237,14 @@ private static function mergeDefaultOptions(array $options, array $defaultOption
230237

231238
if ($resolve = $defaultOptions['resolve'] ?? false) {
232239
foreach ($resolve as $k => $v) {
233-
$options['resolve'] += [substr(self::parseUrl('http://'.$k)['authority'], 2) => (string) $v];
240+
if ('' === $v = (string) $v) {
241+
throw new InvalidArgumentException(sprintf('Option "resolve" for host "%s" cannot be empty.', $k));
242+
}
243+
if ('[' === $v[0] && ']' === substr($v, -1) && str_contains($v, ':')) {
244+
$v = substr($v, 1, -1);
245+
}
246+
247+
$options['resolve'] += [substr(self::parseUrl('http://'.$k)['authority'], 2) => $v];
234248
}
235249
}
236250

Internal/AmpListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@ public function startTlsNegotiation(Request $request): Promise
8181
public function startSendingRequest(Request $request, Stream $stream): Promise
8282
{
8383
$host = $stream->getRemoteAddress()->getHost();
84+
$this->info['primary_ip'] = $host;
8485

8586
if (str_contains($host, ':')) {
8687
$host = '['.$host.']';
8788
}
8889

89-
$this->info['primary_ip'] = $host;
9090
$this->info['primary_port'] = $stream->getRemoteAddress()->getPort();
9191
$this->info['pretransfer_time'] = microtime(true) - $this->info['start_time'];
9292
$this->info['debug'] .= sprintf("* Connected to %s (%s) port %d\n", $request->getUri()->getHost(), $host, $this->info['primary_port']);

Internal/AmpResolver.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,31 @@ public function __construct(array &$dnsMap)
3434

3535
public function resolve(string $name, ?int $typeRestriction = null): Promise
3636
{
37-
if (!isset($this->dnsMap[$name]) || !\in_array($typeRestriction, [Record::A, null], true)) {
37+
$recordType = Record::A;
38+
$ip = $this->dnsMap[$name] ?? null;
39+
40+
if (null !== $ip && str_contains($ip, ':')) {
41+
$recordType = Record::AAAA;
42+
}
43+
if (null === $ip || $recordType !== ($typeRestriction ?? $recordType)) {
3844
return Dns\resolver()->resolve($name, $typeRestriction);
3945
}
4046

41-
return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
47+
return new Success([new Record($ip, $recordType, null)]);
4248
}
4349

4450
public function query(string $name, int $type): Promise
4551
{
46-
if (!isset($this->dnsMap[$name]) || Record::A !== $type) {
52+
$recordType = Record::A;
53+
$ip = $this->dnsMap[$name] ?? null;
54+
55+
if (null !== $ip && str_contains($ip, ':')) {
56+
$recordType = Record::AAAA;
57+
}
58+
if (null === $ip || $recordType !== $type) {
4759
return Dns\resolver()->query($name, $type);
4860
}
4961

50-
return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
62+
return new Success([new Record($ip, $recordType, null)]);
5163
}
5264
}

NativeHttpClient.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ public function request(string $method, string $url, array $options = []): Respo
8080
if (str_starts_with($options['bindto'], 'host!')) {
8181
$options['bindto'] = substr($options['bindto'], 5);
8282
}
83+
if ((\PHP_VERSION_ID < 80223 || 80300 <= \PHP_VERSION_ID && 80311 < \PHP_VERSION_ID) && '\\' === \DIRECTORY_SEPARATOR && '[' === $options['bindto'][0]) {
84+
$options['bindto'] = preg_replace('{^\[[^\]]++\]}', '[$0]', $options['bindto']);
85+
}
8386
}
8487

8588
$hasContentLength = isset($options['normalized_headers']['content-length']);
@@ -334,29 +337,35 @@ private static function parseHostPort(array $url, array &$info): array
334337
*/
335338
private static function dnsResolve(string $host, NativeClientState $multi, array &$info, ?\Closure $onProgress): string
336339
{
337-
if (null === $ip = $multi->dnsCache[$host] ?? null) {
340+
$flag = '' !== $host && '[' === $host[0] && ']' === $host[-1] && str_contains($host, ':') ? \FILTER_FLAG_IPV6 : \FILTER_FLAG_IPV4;
341+
$ip = \FILTER_FLAG_IPV6 === $flag ? substr($host, 1, -1) : $host;
342+
343+
if (filter_var($ip, \FILTER_VALIDATE_IP, $flag)) {
344+
// The host is already an IP address
345+
} elseif (null === $ip = $multi->dnsCache[$host] ?? null) {
338346
$info['debug'] .= "* Hostname was NOT found in DNS cache\n";
339347
$now = microtime(true);
340348

341349
if (!$ip = gethostbynamel($host)) {
342350
throw new TransportException(sprintf('Could not resolve host "%s".', $host));
343351
}
344352

345-
$info['namelookup_time'] = microtime(true) - ($info['start_time'] ?: $now);
346353
$multi->dnsCache[$host] = $ip = $ip[0];
347354
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
348355
} else {
349356
$info['debug'] .= "* Hostname was found in DNS cache\n";
357+
$host = str_contains($ip, ':') ? "[$ip]" : $ip;
350358
}
351359

360+
$info['namelookup_time'] = microtime(true) - ($info['start_time'] ?: $now);
352361
$info['primary_ip'] = $ip;
353362

354363
if ($onProgress) {
355364
// Notify DNS resolution
356365
$onProgress();
357366
}
358367

359-
return $ip;
368+
return $host;
360369
}
361370

362371
/**

Tests/CurlHttpClientTest.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,6 @@ protected function getHttpClient(string $testCase): HttpClientInterface
3333
return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false], 6, 50);
3434
}
3535

36-
public function testBindToPort()
37-
{
38-
$client = $this->getHttpClient(__FUNCTION__);
39-
$response = $client->request('GET', 'http://localhost:8057', ['bindto' => '127.0.0.1:9876']);
40-
$response->getStatusCode();
41-
42-
$r = new \ReflectionProperty($response, 'handle');
43-
44-
$curlInfo = curl_getinfo($r->getValue($response));
45-
46-
self::assertSame('127.0.0.1', $curlInfo['local_ip']);
47-
self::assertSame(9876, $curlInfo['local_port']);
48-
}
49-
5036
public function testTimeoutIsNotAFatalError()
5137
{
5238
if ('\\' === \DIRECTORY_SEPARATOR) {

0 commit comments

Comments
 (0)