Skip to content

Commit 128f3a2

Browse files
Merge branch '6.4' into 7.1
* 6.4: [DoctrineBridge] Fix Connection::createSchemaManager() for Doctrine DBAL v2 [HttpClient] Various cleanups after recent changes do not add child nodes to EmptyNode instances consider write property visibility to decide whether a property is writable add comment explaining why HttpClient tests are run separately silence warnings issued by Redis Sentinel on connection issues
2 parents 214e292 + cbeefef commit 128f3a2

9 files changed

+53
-76
lines changed

CurlHttpClient.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,8 @@ private static function createRedirectResolver(array $options, string $host, int
417417
}
418418
}
419419

420-
return static function ($ch, string $location, bool $noContent, bool &$locationHasHost) use (&$redirectHeaders, $options) {
420+
return static function ($ch, string $location, bool $noContent) use (&$redirectHeaders, $options) {
421421
try {
422-
$locationHasHost = false;
423422
$location = self::parseUrl($location);
424423
$url = self::parseUrl(curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL));
425424
$url = self::resolveUrl($location, $url);
@@ -433,9 +432,7 @@ private static function createRedirectResolver(array $options, string $host, int
433432
$redirectHeaders['with_auth'] = array_filter($redirectHeaders['with_auth'], $filterContentHeaders);
434433
}
435434

436-
$locationHasHost = isset($location['authority']);
437-
438-
if ($redirectHeaders && $locationHasHost) {
435+
if ($redirectHeaders && isset($location['authority'])) {
439436
$port = parse_url($location['authority'], \PHP_URL_PORT) ?: ('http:' === $location['scheme'] ? 80 : 443);
440437
$requestHeaders = parse_url($location['authority'], \PHP_URL_HOST) === $redirectHeaders['host'] && $redirectHeaders['port'] === $port ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
441438
curl_setopt($ch, \CURLOPT_HTTPHEADER, $requestHeaders);

NativeHttpClient.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ public function request(string $method, string $url, array $options = []): Respo
155155

156156
$progressInfo = $info;
157157
$progressInfo['url'] = implode('', $info['url']);
158+
$progressInfo['resolve'] = $resolve;
158159
unset($progressInfo['size_body']);
159160

160161
// Memoize the last progress to ease calling the callback periodically when no network transfer happens
@@ -167,7 +168,7 @@ public function request(string $method, string $url, array $options = []): Respo
167168
$lastProgress = $progress ?: $lastProgress;
168169
}
169170

170-
$onProgress($lastProgress[0], $lastProgress[1], $progressInfo, $resolve);
171+
$onProgress($lastProgress[0], $lastProgress[1], $progressInfo);
171172
};
172173
} elseif (0 < $options['max_duration']) {
173174
$maxDuration = $options['max_duration'];
@@ -352,6 +353,7 @@ private static function dnsResolve(string $host, NativeClientState $multi, array
352353

353354
$multi->dnsCache[$host] = $ip = $ip[0];
354355
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
356+
$host = $ip;
355357
} else {
356358
$info['debug'] .= "* Hostname was found in DNS cache\n";
357359
$host = str_contains($ip, ':') ? "[$ip]" : $ip;

NoPrivateNetworkHttpClient.php

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,20 @@ public function request(string $method, string $url, array $options = []): Respo
5656

5757
$subnets = $this->subnets;
5858

59-
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use ($onProgress, $subnets): void {
59+
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets): void {
6060
static $lastUrl = '';
6161
static $lastPrimaryIp = '';
6262

6363
if ($info['url'] !== $lastUrl) {
64-
$host = trim(parse_url($info['url'], PHP_URL_HOST) ?: '', '[]');
65-
$resolve ??= static fn () => null;
64+
$host = parse_url($info['url'], PHP_URL_HOST) ?: '';
65+
$resolve = $info['resolve'] ?? static function () { return null; };
6666

67-
if (($ip = $host)
68-
&& !filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)
69-
&& !filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)
70-
&& !$ip = $resolve($host)
67+
if (($ip = trim($host, '[]'))
68+
&& !filter_var($ip, \FILTER_VALIDATE_IP)
69+
&& !($ip = $resolve($host))
70+
&& $ip = @(gethostbynamel($host)[0] ?? dns_get_record($host, \DNS_AAAA)[0]['ipv6'] ?? null)
7171
) {
72-
if ($ip = @(dns_get_record($host, \DNS_A)[0]['ip'] ?? null)) {
73-
$resolve($host, $ip);
74-
} elseif ($ip = @(dns_get_record($host, \DNS_AAAA)[0]['ipv6'] ?? null)) {
75-
$resolve($host, '['.$ip.']');
76-
}
72+
$resolve($host, $ip);
7773
}
7874

7975
if ($ip && IpUtils::checkIp($ip, $subnets ?? IpUtils::PRIVATE_SUBNETS)) {

Response/AmpResponse.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ public function __construct(
100100
$onProgress = $options['on_progress'] ?? static function () {};
101101
$onProgress = $this->onProgress = static function () use (&$info, $onProgress, $resolve) {
102102
$info['total_time'] = microtime(true) - $info['start_time'];
103-
$onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info, $resolve);
103+
$info['resolve'] = $resolve;
104+
$onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info);
104105
};
105106

106107
$pauseDeferred = new Deferred();

Response/AsyncContext.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ public function replaceRequest(string $method, string $url, array $options = [])
161161
$this->info['previous_info'][] = $info = $this->response->getInfo();
162162
if (null !== $onProgress = $options['on_progress'] ?? null) {
163163
$thisInfo = &$this->info;
164-
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$thisInfo, $onProgress) {
165-
$onProgress($dlNow, $dlSize, $thisInfo + $info, $resolve);
164+
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
165+
$onProgress($dlNow, $dlSize, $thisInfo + $info);
166166
};
167167
}
168168
if (0 < ($info['max_duration'] ?? 0) && 0 < ($info['total_time'] ?? 0)) {

Response/AsyncResponse.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ public function __construct(HttpClientInterface $client, string $method, string
5252

5353
if (null !== $onProgress = $options['on_progress'] ?? null) {
5454
$thisInfo = &$this->info;
55-
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$thisInfo, $onProgress) {
56-
$onProgress($dlNow, $dlSize, $thisInfo + $info, $resolve);
55+
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
56+
$onProgress($dlNow, $dlSize, $thisInfo + $info);
5757
};
5858
}
5959
$this->response = $client->request($method, $url, ['buffer' => false] + $options);
@@ -118,11 +118,20 @@ public function getHeaders(bool $throw = true): array
118118

119119
public function getInfo(?string $type = null): mixed
120120
{
121+
if ('debug' === ($type ?? 'debug')) {
122+
$debug = implode('', array_column($this->info['previous_info'] ?? [], 'debug'));
123+
$debug .= $this->response->getInfo('debug');
124+
125+
if ('debug' === $type) {
126+
return $debug;
127+
}
128+
}
129+
121130
if (null !== $type) {
122131
return $this->info[$type] ?? $this->response->getInfo($type);
123132
}
124133

125-
return $this->info + $this->response->getInfo();
134+
return array_merge($this->info + $this->response->getInfo(), ['debug' => $debug]);
126135
}
127136

128137
/**
@@ -253,6 +262,7 @@ public static function stream(iterable $responses, ?float $timeout = null, ?stri
253262
return;
254263
}
255264

265+
$chunk = null;
256266
foreach ($client->stream($wrappedResponses, $timeout) as $response => $chunk) {
257267
$r = $asyncMap[$response];
258268

@@ -295,6 +305,9 @@ public static function stream(iterable $responses, ?float $timeout = null, ?stri
295305
}
296306
}
297307

308+
if (null === $chunk) {
309+
throw new \LogicException(\sprintf('"%s" is not compliant with HttpClientInterface: its "stream()" method didn\'t yield any chunks when it should have.', get_debug_type($client)));
310+
}
298311
if (null === $chunk->getError() && $chunk->isLast()) {
299312
$r->yieldedState = self::LAST_CHUNK_YIELDED;
300313
}

Response/CurlResponse.php

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public function __construct(
135135
try {
136136
rewind($debugBuffer);
137137
$debug = ['debug' => stream_get_contents($debugBuffer)];
138-
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug, $resolve);
138+
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug + ['resolve' => $resolve]);
139139
} catch (\Throwable $e) {
140140
$multi->handlesActivity[(int) $ch][] = null;
141141
$multi->handlesActivity[(int) $ch][] = $e;
@@ -425,21 +425,11 @@ private static function parseHeaderLine($ch, string $data, array &$info, array &
425425
$info['http_method'] = 'HEAD' === $info['http_method'] ? 'HEAD' : 'GET';
426426
curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, $info['http_method']);
427427
}
428-
$locationHasHost = false;
429428

430-
if (null === $info['redirect_url'] = $resolveRedirect($ch, $location, $noContent, $locationHasHost)) {
429+
if (null === $info['redirect_url'] = $resolveRedirect($ch, $location, $noContent)) {
431430
$options['max_redirects'] = curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT);
432431
curl_setopt($ch, \CURLOPT_FOLLOWLOCATION, false);
433432
curl_setopt($ch, \CURLOPT_MAXREDIRS, $options['max_redirects']);
434-
} elseif ($locationHasHost) {
435-
$url = parse_url($info['redirect_url']);
436-
437-
if (null !== $ip = $multi->dnsCache->hostnames[$url['host'] = strtolower($url['host'])] ?? null) {
438-
// Populate DNS cache for redirects if needed
439-
$port = $url['port'] ?? ('http' === $url['scheme'] ? 80 : 443);
440-
curl_setopt($ch, \CURLOPT_RESOLVE, ["{$url['host']}:$port:$ip"]);
441-
$multi->dnsCache->removals["-{$url['host']}:$port"] = "-{$url['host']}:$port";
442-
}
443433
}
444434
}
445435

Tests/NoPrivateNetworkHttpClientTest.php

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function testExcludeByIp(string $ipAddr, $subnets, bool $mustThrow)
7575
$this->expectExceptionMessage(sprintf('IP "%s" is blocked for "%s".', $ipAddr, $url));
7676
}
7777

78-
$previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content);
78+
$previousHttpClient = $this->getMockHttpClient($ipAddr, $content);
7979
$client = new NoPrivateNetworkHttpClient($previousHttpClient, $subnets);
8080
$response = $client->request('GET', $url);
8181

@@ -91,14 +91,15 @@ public function testExcludeByIp(string $ipAddr, $subnets, bool $mustThrow)
9191
public function testExcludeByHost(string $ipAddr, $subnets, bool $mustThrow)
9292
{
9393
$content = 'foo';
94-
$url = sprintf('http://%s/', str_contains($ipAddr, ':') ? sprintf('[%s]', $ipAddr) : $ipAddr);
94+
$host = str_contains($ipAddr, ':') ? sprintf('[%s]', $ipAddr) : $ipAddr;
95+
$url = sprintf('http://%s/', $host);
9596

9697
if ($mustThrow) {
9798
$this->expectException(TransportException::class);
98-
$this->expectExceptionMessage(sprintf('Host "%s" is blocked for "%s".', $ipAddr, $url));
99+
$this->expectExceptionMessage(sprintf('Host "%s" is blocked for "%s".', $host, $url));
99100
}
100101

101-
$previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content);
102+
$previousHttpClient = $this->getMockHttpClient($ipAddr, $content);
102103
$client = new NoPrivateNetworkHttpClient($previousHttpClient, $subnets);
103104
$response = $client->request('GET', $url);
104105

@@ -119,7 +120,7 @@ public function testCustomOnProgressCallback()
119120
++$executionCount;
120121
};
121122

122-
$previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content);
123+
$previousHttpClient = $this->getMockHttpClient($ipAddr, $content);
123124
$client = new NoPrivateNetworkHttpClient($previousHttpClient);
124125
$response = $client->request('GET', $url, ['on_progress' => $customCallback]);
125126

@@ -132,7 +133,6 @@ public function testNonCallableOnProgressCallback()
132133
{
133134
$ipAddr = '104.26.14.6';
134135
$url = sprintf('http://%s/', $ipAddr);
135-
$content = 'bar';
136136
$customCallback = sprintf('cb_%s', microtime(true));
137137

138138
$this->expectException(InvalidArgumentException::class);
@@ -142,38 +142,16 @@ public function testNonCallableOnProgressCallback()
142142
$client->request('GET', $url, ['on_progress' => $customCallback]);
143143
}
144144

145-
private function getHttpClientMock(string $url, string $ipAddr, string $content)
145+
public function testConstructor()
146146
{
147-
$previousHttpClient = $this
148-
->getMockBuilder(HttpClientInterface::class)
149-
->getMock();
150-
151-
$previousHttpClient
152-
->expects($this->once())
153-
->method('request')
154-
->with(
155-
'GET',
156-
$url,
157-
$this->callback(function ($options) {
158-
$this->assertArrayHasKey('on_progress', $options);
159-
$onProgress = $options['on_progress'];
160-
$this->assertIsCallable($onProgress);
161-
162-
return true;
163-
})
164-
)
165-
->willReturnCallback(function ($method, $url, $options) use ($ipAddr, $content): ResponseInterface {
166-
$info = [
167-
'primary_ip' => $ipAddr,
168-
'url' => $url,
169-
];
170-
171-
$onProgress = $options['on_progress'];
172-
$onProgress(0, 0, $info);
173-
174-
return MockResponse::fromRequest($method, $url, [], new MockResponse($content));
175-
});
176-
177-
return $previousHttpClient;
147+
$this->expectException(\TypeError::class);
148+
$this->expectExceptionMessage('Argument 2 passed to "Symfony\Component\HttpClient\NoPrivateNetworkHttpClient::__construct()" must be of the type array, string or null. "int" given.');
149+
150+
new NoPrivateNetworkHttpClient(new MockHttpClient(), 3);
151+
}
152+
153+
private function getMockHttpClient(string $ipAddr, string $content)
154+
{
155+
return new MockHttpClient(new MockResponse($content, ['primary_ip' => $ipAddr]));
178156
}
179157
}

TraceableHttpClient.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ public function request(string $method, string $url, array $options = []): Respo
5555
$content = false;
5656
}
5757

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

6161
if (null !== $onProgress) {
62-
$onProgress($dlNow, $dlSize, $info, $resolve);
62+
$onProgress($dlNow, $dlSize, $info);
6363
}
6464
};
6565

0 commit comments

Comments
 (0)