Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit aca260f

Browse filesBrowse files
bug #38375 [HttpClient] fix using proxies with NativeHttpClient (nicolas-grekas)
This PR was merged into the 4.4 branch. Discussion ---------- [HttpClient] fix using proxies with NativeHttpClient | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - As spotted by @stof in #38368 (comment), we cannot use local DNS resolution with HTTP proxies. Commits ------- 28f301b [HttpClient] fix using proxies with NativeHttpClient
2 parents 9c8a300 + 28f301b commit aca260f
Copy full SHA for aca260f

File tree

1 file changed

+42
-17
lines changed
Filter options

1 file changed

+42
-17
lines changed

‎src/Symfony/Component/HttpClient/NativeHttpClient.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/NativeHttpClient.php
+42-17Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public function request(string $method, string $url, array $options = []): Respo
171171

172172
$this->logger && $this->logger->info(sprintf('Request: "%s %s"', $method, implode('', $url)));
173173

174-
[$host, $port, $url['authority']] = self::dnsResolve($url, $this->multi, $info, $onProgress);
174+
[$host, $port] = self::parseHostPort($url, $info);
175175

176176
if (!isset($options['normalized_headers']['host'])) {
177177
$options['headers'][] = 'Host: '.$host.$port;
@@ -198,7 +198,6 @@ public function request(string $method, string $url, array $options = []): Respo
198198
'follow_location' => false, // We follow redirects ourselves - the native logic is too limited
199199
],
200200
'ssl' => array_filter([
201-
'peer_name' => $host,
202201
'verify_peer' => $options['verify_peer'],
203202
'verify_peer_name' => $options['verify_host'],
204203
'cafile' => $options['cafile'],
@@ -225,7 +224,11 @@ public function request(string $method, string $url, array $options = []): Respo
225224

226225
$resolveRedirect = self::createRedirectResolver($options, $host, $proxy, $noProxy, $info, $onProgress);
227226
$context = stream_context_create($context, ['notification' => $notification]);
228-
self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, $noProxy, 'https:' === $url['scheme']);
227+
228+
if (!self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, $noProxy, 'https:' === $url['scheme'])) {
229+
$ip = self::dnsResolve($host, $this->multi, $info, $onProgress);
230+
$url['authority'] = substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host));
231+
}
229232

230233
return new NativeResponse($this->multi, $context, implode('', $url), $options, $info, $resolveRedirect, $onProgress, $this->logger);
231234
}
@@ -306,9 +309,9 @@ private static function getProxy(?string $proxy, array $url): ?array
306309
}
307310

308311
/**
309-
* Resolves the IP of the host using the local DNS cache if possible.
312+
* Extracts the host and the port from the URL.
310313
*/
311-
private static function dnsResolve(array $url, NativeClientState $multi, array &$info, ?\Closure $onProgress): array
314+
private static function parseHostPort(array $url, array &$info): array
312315
{
313316
if ($port = parse_url($url['authority'], \PHP_URL_PORT) ?: '') {
314317
$info['primary_port'] = $port;
@@ -317,8 +320,14 @@ private static function dnsResolve(array $url, NativeClientState $multi, array &
317320
$info['primary_port'] = 'http:' === $url['scheme'] ? 80 : 443;
318321
}
319322

320-
$host = parse_url($url['authority'], \PHP_URL_HOST);
323+
return [parse_url($url['authority'], \PHP_URL_HOST), $port];
324+
}
321325

326+
/**
327+
* Resolves the IP of the host using the local DNS cache if possible.
328+
*/
329+
private static function dnsResolve($host, NativeClientState $multi, array &$info, ?\Closure $onProgress): string
330+
{
322331
if (null === $ip = $multi->dnsCache[$host] ?? null) {
323332
$info['debug'] .= "* Hostname was NOT found in DNS cache\n";
324333
$now = microtime(true);
@@ -341,7 +350,7 @@ private static function dnsResolve(array $url, NativeClientState $multi, array &
341350
$onProgress();
342351
}
343352

344-
return [$host, $port, substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host))];
353+
return $ip;
345354
}
346355

347356
/**
@@ -404,24 +413,33 @@ private static function createRedirectResolver(array $options, string $host, ?ar
404413
}
405414
}
406415

407-
[$host, $port, $url['authority']] = self::dnsResolve($url, $multi, $info, $onProgress);
408-
stream_context_set_option($context, 'ssl', 'peer_name', $host);
416+
[$host, $port] = self::parseHostPort($url, $info);
409417

410418
if (false !== (parse_url($location, \PHP_URL_HOST) ?? false)) {
411419
// Authorization and Cookie headers MUST NOT follow except for the initial host name
412420
$requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
413421
$requestHeaders[] = 'Host: '.$host.$port;
414-
self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy, $noProxy, 'https:' === $url['scheme']);
422+
$dnsResolve = !self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy, $noProxy, 'https:' === $url['scheme']);
423+
} else {
424+
$dnsResolve = isset(stream_context_get_options($context)['ssl']['peer_name']);
425+
}
426+
427+
if ($dnsResolve) {
428+
$ip = self::dnsResolve($host, $multi, $info, $onProgress);
429+
$url['authority'] = substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host));
415430
}
416431

417432
return implode('', $url);
418433
};
419434
}
420435

421-
private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy, array $noProxy, bool $isSsl)
436+
private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy, array $noProxy, bool $isSsl): bool
422437
{
423438
if (null === $proxy) {
424-
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
439+
stream_context_set_option($context, 'http', 'header', $requestHeaders);
440+
stream_context_set_option($context, 'ssl', 'peer_name', $host);
441+
442+
return false;
425443
}
426444

427445
// Matching "no_proxy" should follow the behavior of curl
@@ -430,17 +448,24 @@ private static function configureHeadersAndProxy($context, string $host, array $
430448
$dotRule = '.'.ltrim($rule, '.');
431449

432450
if ('*' === $rule || $host === $rule || substr($host, -\strlen($dotRule)) === $dotRule) {
433-
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
451+
stream_context_set_option($context, 'http', 'proxy', null);
452+
stream_context_set_option($context, 'http', 'request_fulluri', false);
453+
stream_context_set_option($context, 'http', 'header', $requestHeaders);
454+
stream_context_set_option($context, 'ssl', 'peer_name', $host);
455+
456+
return false;
434457
}
435458
}
436459

437-
stream_context_set_option($context, 'http', 'proxy', $proxy['url']);
438-
stream_context_set_option($context, 'http', 'request_fulluri', !$isSsl);
439-
440460
if (null !== $proxy['auth']) {
441461
$requestHeaders[] = 'Proxy-Authorization: '.$proxy['auth'];
442462
}
443463

444-
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
464+
stream_context_set_option($context, 'http', 'proxy', $proxy['url']);
465+
stream_context_set_option($context, 'http', 'request_fulluri', !$isSsl);
466+
stream_context_set_option($context, 'http', 'header', $requestHeaders);
467+
stream_context_set_option($context, 'ssl', 'peer_name', null);
468+
469+
return true;
445470
}
446471
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.