Skip to content

Commit

Permalink
[HttpClient] Resolve hostnames in NoPrivateNetworkHttpClient
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Nov 13, 2024
1 parent ebcaeea commit 3b643b8
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 15 deletions.
2 changes: 2 additions & 0 deletions HttpOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ public function buffer(bool $buffer)
}

/**
* @param callable(int, int, array, \Closure|null=):void $callback
*
* @return $this
*/
public function setOnProgress(callable $callback)
Expand Down
12 changes: 10 additions & 2 deletions NativeHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,15 @@ public function request(string $method, string $url, array $options = []): Respo
// Memoize the last progress to ease calling the callback periodically when no network transfer happens
$lastProgress = [0, 0];
$maxDuration = 0 < $options['max_duration'] ? $options['max_duration'] : \INF;
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info, $maxDuration) {
$multi = $this->multi;
$resolve = static function (string $host, ?string $ip = null) use ($multi): ?string {
if (null !== $ip) {
$multi->dnsCache[$host] = $ip;
}

return $multi->dnsCache[$host] ?? null;
};
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info, $maxDuration, $resolve) {
if ($info['total_time'] >= $maxDuration) {
throw new TransportException(sprintf('Max duration was reached for "%s".', implode('', $info['url'])));
}
Expand All @@ -154,7 +162,7 @@ public function request(string $method, string $url, array $options = []): Respo
$lastProgress = $progress ?: $lastProgress;
}

$onProgress($lastProgress[0], $lastProgress[1], $progressInfo);
$onProgress($lastProgress[0], $lastProgress[1], $progressInfo, $resolve);
};
} elseif (0 < $options['max_duration']) {
$maxDuration = $options['max_duration'];
Expand Down
17 changes: 15 additions & 2 deletions NoPrivateNetworkHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,24 @@ public function request(string $method, string $url, array $options = []): Respo
$lastUrl = '';
$lastPrimaryIp = '';

$options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastUrl, &$lastPrimaryIp): void {
$options['on_progress'] = function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use ($onProgress, $subnets, &$lastUrl, &$lastPrimaryIp): void {
if ($info['url'] !== $lastUrl) {
$host = trim(parse_url($info['url'], PHP_URL_HOST) ?: '', '[]');
$resolve ??= static fn () => null;

if (($ip = $host)
&& !filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)
&& !filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)
&& !$ip = $resolve($host)
) {
if ($ip = @(dns_get_record($host, \DNS_A)[0]['ip'] ?? null)) {
$resolve($host, $ip);
} elseif ($ip = @(dns_get_record($host, \DNS_AAAA)[0]['ipv6'] ?? null)) {
$resolve($host, '['.$ip.']');
}
}

if ($host && IpUtils::checkIp($host, $subnets ?? self::PRIVATE_SUBNETS)) {
if ($ip && IpUtils::checkIp($ip, $subnets ?? self::PRIVATE_SUBNETS)) {
throw new TransportException(sprintf('Host "%s" is blocked for "%s".', $host, $info['url']));
}

Expand Down
11 changes: 9 additions & 2 deletions Response/AmpResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,17 @@ public function __construct(AmpClientState $multi, Request $request, array $opti
$info['max_duration'] = $options['max_duration'];
$info['debug'] = '';

$resolve = static function (string $host, ?string $ip = null) use ($multi): ?string {
if (null !== $ip) {
$multi->dnsCache[$host] = $ip;
}

return $multi->dnsCache[$host] ?? null;
};
$onProgress = $options['on_progress'] ?? static function () {};
$onProgress = $this->onProgress = static function () use (&$info, $onProgress) {
$onProgress = $this->onProgress = static function () use (&$info, $onProgress, $resolve) {
$info['total_time'] = microtime(true) - $info['start_time'];
$onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info);
$onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info, $resolve);
};

$pauseDeferred = new Deferred();
Expand Down
4 changes: 2 additions & 2 deletions Response/AsyncContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ public function replaceRequest(string $method, string $url, array $options = [])
$this->info['previous_info'][] = $info = $this->response->getInfo();
if (null !== $onProgress = $options['on_progress'] ?? null) {
$thisInfo = &$this->info;
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
$onProgress($dlNow, $dlSize, $thisInfo + $info);
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$thisInfo, $onProgress) {
$onProgress($dlNow, $dlSize, $thisInfo + $info, $resolve);
};
}
if (0 < ($info['max_duration'] ?? 0) && 0 < ($info['total_time'] ?? 0)) {
Expand Down
4 changes: 2 additions & 2 deletions Response/AsyncResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public function __construct(HttpClientInterface $client, string $method, string

if (null !== $onProgress = $options['on_progress'] ?? null) {
$thisInfo = &$this->info;
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
$onProgress($dlNow, $dlSize, $thisInfo + $info);
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$thisInfo, $onProgress) {
$onProgress($dlNow, $dlSize, $thisInfo + $info, $resolve);
};
}
$this->response = $client->request($method, $url, ['buffer' => false] + $options);
Expand Down
11 changes: 9 additions & 2 deletions Response/CurlResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,20 @@ public function __construct(CurlClientState $multi, $ch, ?array $options = null,
curl_pause($ch, \CURLPAUSE_CONT);

if ($onProgress = $options['on_progress']) {
$resolve = static function (string $host, ?string $ip = null) use ($multi): ?string {
if (null !== $ip) {
$multi->dnsCache->hostnames[$host] = $ip;
}

return $multi->dnsCache->hostnames[$host] ?? null;
};
$url = isset($info['url']) ? ['url' => $info['url']] : [];
curl_setopt($ch, \CURLOPT_NOPROGRESS, false);
curl_setopt($ch, \CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi, $debugBuffer) {
curl_setopt($ch, \CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi, $debugBuffer, $resolve) {
try {
rewind($debugBuffer);
$debug = ['debug' => stream_get_contents($debugBuffer)];
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug);
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug, $resolve);
} catch (\Throwable $e) {
$multi->handlesActivity[(int) $ch][] = null;
$multi->handlesActivity[(int) $ch][] = $e;
Expand Down
23 changes: 23 additions & 0 deletions Tests/HttpClientTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\Internal\ClientState;
use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;
use Symfony\Component\HttpClient\Response\StreamWrapper;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
Expand Down Expand Up @@ -466,4 +467,26 @@ public function testMisspelledScheme()

$httpClient->request('GET', 'http:/localhost:8057/');
}

public function testNoPrivateNetwork()
{
$client = $this->getHttpClient(__FUNCTION__);
$client = new NoPrivateNetworkHttpClient($client);

$this->expectException(TransportException::class);
$this->expectExceptionMessage('Host "localhost" is blocked');

$client->request('GET', 'http://localhost:8888');
}

public function testNoPrivateNetworkWithResolve()
{
$client = $this->getHttpClient(__FUNCTION__);
$client = new NoPrivateNetworkHttpClient($client);

$this->expectException(TransportException::class);
$this->expectExceptionMessage('Host "symfony.com" is blocked');

$client->request('GET', 'http://symfony.com', ['resolve' => ['symfony.com' => '127.0.0.1']]);
}
}
5 changes: 4 additions & 1 deletion Tests/MockHttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,17 @@ protected function getHttpClient(string $testCase): HttpClientInterface

switch ($testCase) {
default:
return new MockHttpClient(function (string $method, string $url, array $options) use ($client) {
return new MockHttpClient(function (string $method, string $url, array $options) use ($client, $testCase) {
try {
// force the request to be completed so that we don't test side effects of the transport
$response = $client->request($method, $url, ['buffer' => false] + $options);
$content = $response->getContent(false);

return new MockResponse($content, $response->getInfo());
} catch (\Throwable $e) {
if (str_starts_with($testCase, 'testNoPrivateNetwork')) {
throw $e;
}
$this->fail($e->getMessage());
}
});
Expand Down
4 changes: 2 additions & 2 deletions TraceableHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ public function request(string $method, string $url, array $options = []): Respo
$content = false;
}

$options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use (&$traceInfo, $onProgress) {
$options['on_progress'] = function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$traceInfo, $onProgress) {
$traceInfo = $info;

if (null !== $onProgress) {
$onProgress($dlNow, $dlSize, $info);
$onProgress($dlNow, $dlSize, $info, $resolve);
}
};

Expand Down

0 comments on commit 3b643b8

Please sign in to comment.