Skip to content

Commit bd3df8f

Browse files
[HttpClient] fix using proxies with NativeHttpClient
1 parent 09a8120 commit bd3df8f

File tree

1 file changed

+42
-17
lines changed

1 file changed

+42
-17
lines changed

NativeHttpClient.php

Lines 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)