Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3a1f34a

Browse files
Merge branch '6.0' into 6.1
* 6.0: [HttpClient] fix monitoring responses issued before reset() [Cache] workaround transient test on M1 [HttpClient] Fix dealing with "HTTP/1.1 000 " responses [HttpClient] minor change Fix aliased namespaces matching
2 parents ff4af00 + 8e32728 commit 3a1f34a

File tree

11 files changed

+136
-83
lines changed

11 files changed

+136
-83
lines changed

src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public function testGetMetadata()
128128

129129
$metadata = $item->getMetadata();
130130
$this->assertArrayHasKey(CacheItem::METADATA_CTIME, $metadata);
131-
$this->assertEqualsWithDelta(999, $metadata[CacheItem::METADATA_CTIME], 10);
131+
$this->assertEqualsWithDelta(999, $metadata[CacheItem::METADATA_CTIME], 150);
132132
$this->assertArrayHasKey(CacheItem::METADATA_EXPIRY, $metadata);
133133
$this->assertEqualsWithDelta(9 + time(), $metadata[CacheItem::METADATA_EXPIRY], 1);
134134
}

src/Symfony/Component/HttpClient/CurlHttpClient.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,9 @@ public function stream(ResponseInterface|iterable $responses, float $timeout = n
304304
$responses = [$responses];
305305
}
306306

307-
if ($this->multi->handle instanceof \CurlMultiHandle) {
307+
if (($mh = $this->multi->handles[0] ?? null) instanceof \CurlMultiHandle) {
308308
$active = 0;
309-
while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->multi->handle, $active)) {
309+
while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($mh, $active)) {
310310
}
311311
}
312312

src/Symfony/Component/HttpClient/Internal/CurlClientState.php

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
*/
2424
final class CurlClientState extends ClientState
2525
{
26-
public \CurlMultiHandle $handle;
26+
/** @var array<\CurlMultiHandle> */
27+
public array $handles = [];
2728
/** @var PushedResponse[] */
2829
public array $pushedResponses = [];
2930
public DnsCache $dnsCache;
@@ -41,20 +42,20 @@ public function __construct(int $maxHostConnections, int $maxPendingPushes)
4142
{
4243
self::$curlVersion = self::$curlVersion ?? curl_version();
4344

44-
$this->handle = curl_multi_init();
45+
array_unshift($this->handles, $mh = curl_multi_init());
4546
$this->dnsCache = new DnsCache();
4647
$this->maxHostConnections = $maxHostConnections;
4748
$this->maxPendingPushes = $maxPendingPushes;
4849

4950
// Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
5051
if (\defined('CURLPIPE_MULTIPLEX')) {
51-
curl_multi_setopt($this->handle, \CURLMOPT_PIPELINING, \CURLPIPE_MULTIPLEX);
52+
curl_multi_setopt($mh, \CURLMOPT_PIPELINING, \CURLPIPE_MULTIPLEX);
5253
}
5354
if (\defined('CURLMOPT_MAX_HOST_CONNECTIONS')) {
54-
$maxHostConnections = curl_multi_setopt($this->handle, \CURLMOPT_MAX_HOST_CONNECTIONS, 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX) ? 0 : $maxHostConnections;
55+
$maxHostConnections = curl_multi_setopt($mh, \CURLMOPT_MAX_HOST_CONNECTIONS, 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX) ? 0 : $maxHostConnections;
5556
}
5657
if (\defined('CURLMOPT_MAXCONNECTS') && 0 < $maxHostConnections) {
57-
curl_multi_setopt($this->handle, \CURLMOPT_MAXCONNECTS, $maxHostConnections);
58+
curl_multi_setopt($mh, \CURLMOPT_MAXCONNECTS, $maxHostConnections);
5859
}
5960

6061
// Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/77535
@@ -66,41 +67,41 @@ public function __construct(int $maxHostConnections, int $maxPendingPushes)
6667
if (!\defined('CURLMOPT_PUSHFUNCTION') || 0x073D00 > self::$curlVersion['version_number'] || !(\CURL_VERSION_HTTP2 & self::$curlVersion['features'])) {
6768
return;
6869
}
70+
71+
// Clone to prevent a circular reference
72+
$multi = clone $this;
73+
$multi->handles = [$mh];
74+
$multi->pushedResponses = &$this->pushedResponses;
75+
$multi->logger = &$this->logger;
76+
$multi->handlesActivity = &$this->handlesActivity;
77+
$multi->openHandles = &$this->openHandles;
78+
$multi->lastTimeout = &$this->lastTimeout;
79+
80+
curl_multi_setopt($mh, \CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes) {
81+
return $multi->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes);
82+
});
6983
}
7084

7185
public function reset()
7286
{
73-
if ($this->logger) {
74-
foreach ($this->pushedResponses as $url => $response) {
75-
$this->logger->debug(sprintf('Unused pushed response: "%s"', $url));
87+
foreach ($this->pushedResponses as $url => $response) {
88+
$this->logger?->debug(sprintf('Unused pushed response: "%s"', $url));
89+
90+
foreach ($this->handles as $mh) {
91+
curl_multi_remove_handle($mh, $response->handle);
7692
}
93+
curl_close($response->handle);
7794
}
7895

7996
$this->pushedResponses = [];
8097
$this->dnsCache->evictions = $this->dnsCache->evictions ?: $this->dnsCache->removals;
8198
$this->dnsCache->removals = $this->dnsCache->hostnames = [];
8299

83-
if ($this->handle instanceof \CurlMultiHandle) {
84-
if (\defined('CURLMOPT_PUSHFUNCTION')) {
85-
curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, null);
86-
}
87-
88-
$this->__construct($this->maxHostConnections, $this->maxPendingPushes);
100+
if (\defined('CURLMOPT_PUSHFUNCTION')) {
101+
curl_multi_setopt($this->handles[0], \CURLMOPT_PUSHFUNCTION, null);
89102
}
90-
}
91103

92-
public function __wakeup()
93-
{
94-
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
95-
}
96-
97-
public function __destruct()
98-
{
99-
foreach ($this->openHandles as [$ch]) {
100-
if ($ch instanceof \CurlHandle) {
101-
curl_setopt($ch, \CURLOPT_VERBOSE, false);
102-
}
103-
}
104+
$this->__construct($this->maxHostConnections, $this->maxPendingPushes);
104105
}
105106

106107
private function handlePush($parent, $pushed, array $requestHeaders, int $maxPendingPushes): int

src/Symfony/Component/HttpClient/Response/AmpResponse.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ private static function generateResponse(Request $request, AmpClientState $multi
237237
try {
238238
/* @var Response $response */
239239
if (null === $response = yield from self::getPushedResponse($request, $multi, $info, $headers, $options, $logger)) {
240-
$logger && $logger->info(sprintf('Request: "%s %s"', $info['http_method'], $info['url']));
240+
$logger?->info(sprintf('Request: "%s %s"', $info['http_method'], $info['url']));
241241

242242
$response = yield from self::followRedirects($request, $multi, $info, $headers, $canceller, $options, $onProgress, $handle, $logger, $pause);
243243
}
@@ -321,7 +321,7 @@ private static function followRedirects(Request $originRequest, AmpClientState $
321321
return $response;
322322
}
323323

324-
$logger && $logger->info(sprintf('Redirecting: "%s %s"', $status, $info['url']));
324+
$logger?->info(sprintf('Redirecting: "%s %s"', $status, $info['url']));
325325

326326
try {
327327
// Discard body of redirects
@@ -429,14 +429,14 @@ private static function getPushedResponse(Request $request, AmpClientState $mult
429429
foreach ($response->getHeaderArray('vary') as $vary) {
430430
foreach (preg_split('/\s*+,\s*+/', $vary) as $v) {
431431
if ('*' === $v || ($pushedRequest->getHeaderArray($v) !== $request->getHeaderArray($v) && 'accept-encoding' !== strtolower($v))) {
432-
$logger && $logger->debug(sprintf('Skipping pushed response: "%s"', $info['url']));
432+
$logger?->debug(sprintf('Skipping pushed response: "%s"', $info['url']));
433433
continue 3;
434434
}
435435
}
436436
}
437437

438438
$pushDeferred->resolve();
439-
$logger && $logger->debug(sprintf('Accepting pushed response: "%s %s"', $info['http_method'], $info['url']));
439+
$logger?->debug(sprintf('Accepting pushed response: "%s %s"', $info['http_method'], $info['url']));
440440
self::addResponseHeaders($response, $info, $headers);
441441
unset($multi->pushedResponses[$authority][$i]);
442442

src/Symfony/Component/HttpClient/Response/CurlResponse.php

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra
108108
if (0 < $duration) {
109109
if ($execCounter === $multi->execCounter) {
110110
$multi->execCounter = !\is_float($execCounter) ? 1 + $execCounter : \PHP_INT_MIN;
111-
curl_multi_remove_handle($multi->handle, $ch);
111+
foreach ($multi->handles as $mh) {
112+
curl_multi_remove_handle($mh, $ch);
113+
}
112114
}
113115

114116
$lastExpiry = end($multi->pauseExpiries);
@@ -120,7 +122,7 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra
120122
} else {
121123
unset($multi->pauseExpiries[(int) $ch]);
122124
curl_pause($ch, \CURLPAUSE_CONT);
123-
curl_multi_add_handle($multi->handle, $ch);
125+
curl_multi_add_handle($multi->handles[0], $ch);
124126
}
125127
};
126128

@@ -174,7 +176,7 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra
174176
// Schedule the request in a non-blocking way
175177
$multi->lastTimeout = null;
176178
$multi->openHandles[$id] = [$ch, $options];
177-
curl_multi_add_handle($multi->handle, $ch);
179+
curl_multi_add_handle($multi->handles[0], $ch);
178180

179181
$this->canary = new Canary(static function () use ($ch, $multi, $id) {
180182
unset($multi->pauseExpiries[$id], $multi->openHandles[$id], $multi->handlesActivity[$id]);
@@ -184,7 +186,9 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra
184186
return;
185187
}
186188

187-
curl_multi_remove_handle($multi->handle, $ch);
189+
foreach ($multi->handles as $mh) {
190+
curl_multi_remove_handle($mh, $ch);
191+
}
188192
curl_setopt_array($ch, [
189193
\CURLOPT_NOPROGRESS => true,
190194
\CURLOPT_PROGRESSFUNCTION => null,
@@ -266,7 +270,7 @@ public function __destruct()
266270
*/
267271
private static function schedule(self $response, array &$runningResponses): void
268272
{
269-
if (isset($runningResponses[$i = (int) $response->multi->handle])) {
273+
if (isset($runningResponses[$i = (int) $response->multi->handles[0]])) {
270274
$runningResponses[$i][1][$response->id] = $response;
271275
} else {
272276
$runningResponses[$i] = [$response->multi, [$response->id => $response]];
@@ -299,38 +303,47 @@ private static function perform(ClientState $multi, array &$responses = null): v
299303
try {
300304
self::$performing = true;
301305
++$multi->execCounter;
302-
$active = 0;
303-
while (\CURLM_CALL_MULTI_PERFORM === ($err = curl_multi_exec($multi->handle, $active)));
304-
305-
if (\CURLM_OK !== $err) {
306-
throw new TransportException(curl_multi_strerror($err));
307-
}
308306

309-
while ($info = curl_multi_info_read($multi->handle)) {
310-
if (\CURLMSG_DONE !== $info['msg']) {
311-
continue;
307+
foreach ($multi->handles as $i => $mh) {
308+
$active = 0;
309+
while (\CURLM_CALL_MULTI_PERFORM === ($err = curl_multi_exec($mh, $active))) {
312310
}
313-
$result = $info['result'];
314-
$id = (int) $ch = $info['handle'];
315-
$waitFor = @curl_getinfo($ch, \CURLINFO_PRIVATE) ?: '_0';
316311

317-
if (\in_array($result, [\CURLE_SEND_ERROR, \CURLE_RECV_ERROR, /*CURLE_HTTP2*/ 16, /*CURLE_HTTP2_STREAM*/ 92], true) && $waitFor[1] && 'C' !== $waitFor[0]) {
318-
curl_multi_remove_handle($multi->handle, $ch);
319-
$waitFor[1] = (string) ((int) $waitFor[1] - 1); // decrement the retry counter
320-
curl_setopt($ch, \CURLOPT_PRIVATE, $waitFor);
321-
curl_setopt($ch, \CURLOPT_FORBID_REUSE, true);
312+
if (\CURLM_OK !== $err) {
313+
throw new TransportException(curl_multi_strerror($err));
314+
}
322315

323-
if (0 === curl_multi_add_handle($multi->handle, $ch)) {
316+
while ($info = curl_multi_info_read($mh)) {
317+
if (\CURLMSG_DONE !== $info['msg']) {
324318
continue;
325319
}
326-
}
320+
$result = $info['result'];
321+
$id = (int) $ch = $info['handle'];
322+
$waitFor = @curl_getinfo($ch, \CURLINFO_PRIVATE) ?: '_0';
323+
324+
if (\in_array($result, [\CURLE_SEND_ERROR, \CURLE_RECV_ERROR, /*CURLE_HTTP2*/ 16, /*CURLE_HTTP2_STREAM*/ 92], true) && $waitFor[1] && 'C' !== $waitFor[0]) {
325+
curl_multi_remove_handle($mh, $ch);
326+
$waitFor[1] = (string) ((int) $waitFor[1] - 1); // decrement the retry counter
327+
curl_setopt($ch, \CURLOPT_PRIVATE, $waitFor);
328+
curl_setopt($ch, \CURLOPT_FORBID_REUSE, true);
329+
330+
if (0 === curl_multi_add_handle($mh, $ch)) {
331+
continue;
332+
}
333+
}
327334

328-
if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) {
329-
$multi->handlesActivity[$id][] = new FirstChunk();
335+
if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) {
336+
$multi->handlesActivity[$id][] = new FirstChunk();
337+
}
338+
339+
$multi->handlesActivity[$id][] = null;
340+
$multi->handlesActivity[$id][] = \in_array($result, [\CURLE_OK, \CURLE_TOO_MANY_REDIRECTS], true) || '_0' === $waitFor || curl_getinfo($ch, \CURLINFO_SIZE_DOWNLOAD) === curl_getinfo($ch, \CURLINFO_CONTENT_LENGTH_DOWNLOAD) ? null : new TransportException(ucfirst(curl_error($ch) ?: curl_strerror($result)).sprintf(' for "%s".', curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL)));
330341
}
331342

332-
$multi->handlesActivity[$id][] = null;
333-
$multi->handlesActivity[$id][] = \in_array($result, [\CURLE_OK, \CURLE_TOO_MANY_REDIRECTS], true) || '_0' === $waitFor || curl_getinfo($ch, \CURLINFO_SIZE_DOWNLOAD) === curl_getinfo($ch, \CURLINFO_CONTENT_LENGTH_DOWNLOAD) ? null : new TransportException(ucfirst(curl_error($ch) ?: curl_strerror($result)).sprintf(' for "%s".', curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL)));
343+
if (!$active && 0 < $i) {
344+
curl_multi_close($mh);
345+
unset($multi->handles[$i]);
346+
}
334347
}
335348
} finally {
336349
self::$performing = false;
@@ -355,11 +368,11 @@ private static function select(ClientState $multi, float $timeout): int
355368

356369
unset($multi->pauseExpiries[$id]);
357370
curl_pause($multi->openHandles[$id][0], \CURLPAUSE_CONT);
358-
curl_multi_add_handle($multi->handle, $multi->openHandles[$id][0]);
371+
curl_multi_add_handle($multi->handles[0], $multi->openHandles[$id][0]);
359372
}
360373
}
361374

362-
if (0 !== $selected = curl_multi_select($multi->handle, $timeout)) {
375+
if (0 !== $selected = curl_multi_select($multi->handles[array_key_last($multi->handles)], $timeout)) {
363376
return $selected;
364377
}
365378

@@ -382,15 +395,8 @@ private static function parseHeaderLine($ch, string $data, array &$info, array &
382395
}
383396

384397
if ('' !== $data) {
385-
try {
386-
// Regular header line: add it to the list
387-
self::addResponseHeaders([$data], $info, $headers);
388-
} catch (TransportException $e) {
389-
$multi->handlesActivity[$id][] = null;
390-
$multi->handlesActivity[$id][] = $e;
391-
392-
return \strlen($data);
393-
}
398+
// Regular header line: add it to the list
399+
self::addResponseHeaders([$data], $info, $headers);
394400

395401
if (!str_starts_with($data, 'HTTP/')) {
396402
if (0 === stripos($data, 'Location:')) {

src/Symfony/Component/HttpClient/Response/TransportResponseTrait.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ abstract protected static function select(ClientState $multi, float $timeout): i
111111
private static function addResponseHeaders(array $responseHeaders, array &$info, array &$headers, string &$debug = ''): void
112112
{
113113
foreach ($responseHeaders as $h) {
114-
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([1-9]\d\d)(?: |$)#', $h, $m)) {
114+
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? (\d\d\d)(?: |$)#', $h, $m)) {
115115
if ($headers) {
116116
$debug .= "< \r\n";
117117
$headers = [];
@@ -126,10 +126,6 @@ private static function addResponseHeaders(array $responseHeaders, array &$info,
126126
}
127127

128128
$debug .= "< \r\n";
129-
130-
if (!$info['http_code']) {
131-
throw new TransportException(sprintf('Invalid or missing HTTP status line for "%s".', implode('', $info['url'])));
132-
}
133129
}
134130

135131
/**

src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,20 @@ public function testHandleIsReinitOnReset()
6262
$r = new \ReflectionProperty($httpClient, 'multi');
6363
$r->setAccessible(true);
6464
$clientState = $r->getValue($httpClient);
65-
$initialHandleId = (int) $clientState->handle;
65+
$initialHandleId = (int) $clientState->handles[0];
6666
$httpClient->reset();
67-
self::assertNotSame($initialHandleId, (int) $clientState->handle);
67+
self::assertNotSame($initialHandleId, (int) $clientState->handles[0]);
68+
}
69+
70+
public function testProcessAfterReset()
71+
{
72+
$client = $this->getHttpClient(__FUNCTION__);
73+
74+
$response = $client->request('GET', 'http://127.0.0.1:8057/json');
75+
76+
$client->reset();
77+
78+
$this->assertSame(['application/json'], $response->getHeaders()['content-type']);
6879
}
6980

7081
public function testOverridingRefererUsingCurlOptions()

src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ public function invalidResponseFactoryProvider()
187187
];
188188
}
189189

190+
public function testZeroStatusCode()
191+
{
192+
$client = new MockHttpClient(new MockResponse('', ['response_headers' => ['HTTP/1.1 000 ']]));
193+
$response = $client->request('GET', 'https://foo.bar');
194+
$this->assertSame(0, $response->getStatusCode());
195+
}
196+
190197
public function testThrowExceptionInBodyGenerator()
191198
{
192199
$mockHttpClient = new MockHttpClient([

src/Symfony/Component/PropertyInfo/PhpStan/NameScope.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@ public function resolveStringName(string $name): string
4141
}
4242

4343
$nameParts = explode('\\', $name);
44-
if (isset($this->uses[$nameParts[0]])) {
44+
$firstNamePart = $nameParts[0];
45+
if (isset($this->uses[$firstNamePart])) {
4546
if (1 === \count($nameParts)) {
46-
return $this->uses[$nameParts[0]];
47+
return $this->uses[$firstNamePart];
4748
}
4849
array_shift($nameParts);
4950

50-
return sprintf('%s\\%s', $this->uses[$nameParts[0]], implode('\\', $nameParts));
51+
return sprintf('%s\\%s', $this->uses[$firstNamePart], implode('\\', $nameParts));
5152
}
5253

5354
if (null !== $this->namespace) {

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,14 @@ public function unionTypesProvider(): array
370370
['e', [new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class, true, [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])], [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING, false, null, true, [], [new Type(Type::BUILTIN_TYPE_OBJECT, false, DefaultValue::class)])])]), new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
371371
];
372372
}
373+
374+
public function testDummyNamespace()
375+
{
376+
$this->assertEquals(
377+
[new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')],
378+
$this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DummyNamespace', 'dummy')
379+
);
380+
}
373381
}
374382

375383
class PhpStanOmittedParamTagTypeDocBlock

0 commit comments

Comments
 (0)