8000 Merge branch '4.4' into 5.3 · symfony/http-client@50e00e4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 50e00e4

Browse files
Merge branch '4.4' into 5.3
* 4.4: [HttpClient] fix resetting DNS/etc when calling CurlHttpClient::reset() Fix invalid guess with enumType
2 parents f1b0537 + 848a70d commit 50e00e4

File tree

4 files changed

+55
-78
lines changed

4 files changed

+55
-78
lines changed

CurlHttpClient.php

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

172172
if ($resolve && 0x072A00 > CurlClientState::$curlVersion['version_number']) {
173173
// DNS cache removals require curl 7.42 or higher
174-
// On lower versions, we have to create a new multi handle
175174
$this->multi->reset();
176175
}
177176

@@ -288,6 +287,7 @@ public function request(string $method, string $url, array $options = []): Respo
288287
if (!$pushedResponse) {
289288
$ch = curl_init();
290289
$this->logger && $this->logger->info(sprintf('Request: "%s %s"', $method, $url));
290+
$curlopts += [\CURLOPT_SHARE => $this->multi->share];
291291
}
292292

293293
foreach ($curlopts as $opt => $value) {
@@ -311,9 +311,9 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa
311311
throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of CurlResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
312312
}
313313

314-
if (\is_resource($mh = $this->multi->handles[0] ?? null) || $mh instanceof \CurlMultiHandle) {
314+
if (\is_resource($this->multi->handle) || $this->multi->handle instanceof \CurlMultiHandle) {
315315
$active = 0;
316-
while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($mh, $active)) {
316+
while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->multi->handle, $active)) {
317317
}
318318
}
319319

Internal/CurlClientState.php

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
*/
2424
final class CurlClientState extends ClientState
2525
{
26-
/** @var array<\CurlMultiHandle|resource> */
27-
public $handles = [];
26+
/** @var \CurlMultiHandle|resource */
27+
public $handle;
28+
/** @var \CurlShareHandle|resource */
29+
public $share;
2830
/** @var PushedResponse[] */
2931
public $pushedResponses = [];
3032
/** @var DnsCache */
@@ -37,27 +39,23 @@ final class CurlClientState extends ClientState
3739

3840
public static $curlVersion;
3941

40-
private $maxHostConnections;
41-
private $maxPendingPushes;
42-
4342
public function __construct(int $maxHostConnections, int $maxPendingPushes)
4443
{
4544
self::$curlVersion = self::$curlVersion ?? curl_version();
4645

47-
array_unshift($this->handles, $mh = curl_multi_init());
46+
$this->handle = curl_multi_init();
4847
$this->dnsCache = new DnsCache();
49-
$this->maxHostConnections = $maxHostConnections;
50-
$this->maxPendingPushes = $maxPendingPushes;
48+
$this->reset();
5149

5250
// Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
5351
if (\defined('CURLPIPE_MULTIPLEX')) {
54-
curl_multi_setopt($mh, \CURLMOPT_PIPELINING, \CURLPIPE_MULTIPLEX);
52+
curl_multi_setopt($this->handle, \CURLMOPT_PIPELINING, \CURLPIPE_MULTIPLEX);
5553
}
5654
if (\defined('CURLMOPT_MAX_HOST_CONNECTIONS')) {
57-
$maxHostConnections = curl_multi_setopt($mh, \CURLMOPT_MAX_HOST_CONNECTIONS, 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX) ? 0 : $maxHostConnections;
55+
$maxHostConnections = curl_multi_setopt($this->handle, \CURLMOPT_MAX_HOST_CONNECTIONS, 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX) ? 0 : $maxHostConnections;
5856
}
5957
if (\defined('CURLMOPT_MAXCONNECTS') && 0 < $maxHostConnections) {
60-
curl_multi_setopt($mh, \CURLMOPT_MAXCONNECTS, $maxHostConnections);
58+
curl_multi_setopt($this->handle, \CURLMOPT_MAXCONNECTS, $maxHostConnections);
6159
}
6260

6361
// Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/77535
@@ -70,40 +68,31 @@ public function __construct(int $maxHostConnections, int $maxPendingPushes)
7068
return;
7169
}
7270

73-
// Clone to prevent a circular reference
74-
$multi = clone $this;
75-
$multi->handles = [$mh];
76-
$multi->pushedResponses = &$this->pushedResponses;
77-
$multi->logger = &$this->logger;
78-
$multi->handlesActivity = &$this->handlesActivity;
79-
$multi->openHandles = &$this->openHandles;
80-
$multi->lastTimeout = &$this->lastTimeout;
81-
82-
curl_multi_setopt($mh, \CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes) {
83-
return $multi->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes);
71+
curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, function ($parent, $pushed, array $requestHeaders) use ($maxPendingPushes) {
72+
return $this->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes);
8473
});
8574
}
8675

8776
public function reset()
8877
{
8978
foreach ($this->pushedResponses as $url => $response) {
9079
$this->logger && $this->logger->debug(sprintf('Unused pushed response: "%s"', $url));
91-
92-
foreach ($this->handles as $mh) {
93-
curl_multi_remove_handle($mh, $response->handle);
94-
}
80+
curl_multi_remove_handle($this->handle, $response->handle);
9581
curl_close($response->handle);
9682
}
9783

9884
$this->pushedResponses = [];
9985
$this->dnsCache->evictions = $this->dnsCache->evictions ?: $this->dnsCache->removals;
10086
$this->dnsCache->removals = $this->dnsCache->hostnames = [];
10187

102-
if (\defined('CURLMOPT_PUSHFUNCTION')) {
103-
curl_multi_setopt($this->handles[0], \CURLMOPT_PUSHFUNCTION, null);
104-
}
88+
$this->share = curl_share_init();
89+
90+
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_DNS);
91+
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_SSL_SESSION);
10592

106-
$this->__construct($this->maxHostConnections, $this->maxPendingPushes);
93+
if (\defined('CURL_LOCK_DATA_CONNECT')) {
94+
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_CONNECT);
95+
}
10796
}
10897

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

Response/CurlResponse.php

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
106106
if (0 < $duration) {
107107
if ($execCounter === $multi->execCounter) {
108108
$multi->execCounter = !\is_float($execCounter) ? 1 + $execCounter : \PHP_INT_MIN;
109-
foreach ($multi->handles as $mh) {
110-
curl_multi_remove_handle($mh, $ch);
111-
}
109+
curl_multi_remove_handle($multi->handle, $ch);
112110
}
113111

114112
$lastExpiry = end($multi->pauseExpiries);
@@ -120,7 +118,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
120118
} else {
121119
unset($multi->pauseExpiries[(int) $ch]);
122120
curl_pause($ch, \CURLPAUSE_CONT);
123-
curl_multi_add_handle($multi->handles[0], $ch);
121+
curl_multi_add_handle($multi->handle, $ch);
124122
}
125123
};
126124

@@ -174,7 +172,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
174172
// Schedule the request in a non-blocking way
175173
$multi->lastTimeout = null;
176174
$multi->openHandles[$id] = [$ch, $options];
177-
curl_multi_add_handle($multi->handles[0], $ch);
175+
curl_multi_add_handle($multi->handle, $ch);
178176

179177
$this->canary = new Canary(static function () use ($ch, $multi, $id) {
180178
unset($multi->pauseExpiries[$id], $multi->openHandles[$id], $multi->handlesActivity[$id]);
@@ -184,9 +182,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
184182
return;
185183
}
186184

187-
foreach ($multi->handles as $mh) {
188-
curl_multi_remove_handle($mh, $ch);
189-
}
185+
curl_multi_remove_handle($multi->handle, $ch);
190186
curl_setopt_array($ch, [
191187
\CURLOPT_NOPROGRESS => true,
192188
\CURLOPT_PROGRESSFUNCTION => null,
@@ -268,7 +264,7 @@ public function __destruct()
268264
*/
269265
private static function schedule(self $response, array &$runningResponses): void
270266
{
271-
if (isset($runningResponses[$i = (int) $response->multi->handles[0]])) {
267+
if (isset($runningResponses[$i = (int) $response->multi->handle])) {
272268
$runningResponses[$i][1][$response->id] = $response;
273269
} else {
274270
$runningResponses[$i] = [$response->multi, [$response->id => $response]];
@@ -301,47 +297,39 @@ private static function perform(ClientState $multi, array &$responses = null): v
301297
try {
302298
self::$performing = true;
303299
++$multi->execCounter;
300+
$active = 0;
301+
while (\CURLM_CALL_MULTI_PERFORM === ($err = curl_multi_exec($multi->handle, $active))) {
302+
}
304303

305-
foreach ($multi->handles as $i => $mh) {
306-
$active = 0;
307-
while (\CURLM_CALL_MULTI_PERFORM === ($err = curl_multi_exec($mh, $active))) {
308-
}
304+
if (\CURLM_OK !== $err) {
305+
throw new TransportException(curl_multi_strerror($err));
306+
}
309307

310-
if (\CURLM_OK !== $err) {
311-
throw new TransportException(curl_multi_strerror($err));
308+
while ($info = curl_multi_info_read($multi->handle)) {
309+
if (\CURLMSG_DONE !== $info['msg']) {
310+
continue;
312311
}
312+
$result = $info['result'];
313+
$id = (int) $ch = $info['handle'];
314+
$waitFor = @curl_getinfo($ch, \CURLINFO_PRIVATE) ?: '_0';
313315

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

333-
if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) {
334-
$multi->handlesActivity[$id][] = new FirstChunk();
322+
if (0 === curl_multi_add_handle($multi->handle, $ch)) {
323+
continue;
335324
}
336-
337-
$multi->handlesActivity[$id][] = null;
338-
$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(sprintf('%s for "%s".', curl_strerror($result), curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL)));
339325
}
340326

341-
if (!$active && 0 < $i) {
342-
curl_multi_close($mh);
343-
unset($multi->handles[$i]);
327+
if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) {
328+
$multi->handlesActivity[$id][] = new FirstChunk();
344329
}
330+
331+
$multi->handlesActivity[$id][] = null;
332+
$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(sprintf('%s for "%s".', curl_strerror($result), curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL)));
345333
}
346334
} finally {
347335
self::$performing = false;
@@ -371,11 +359,11 @@ private static function select(ClientState $multi, float $timeout): int
371359

372360
unset($multi->pauseExpiries[$id]);
373361
curl_pause($multi->openHandles[$id][0], \CURLPAUSE_CONT);
374-
curl_multi_add_handle($multi->handles[0], $multi->openHandles[$id][0]);
362+
curl_multi_add_handle($multi->handle, $multi->openHandles[$id][0]);
375363
}
376364
}
377365

378-
if (0 !== $selected = curl_multi_select($multi->handles[array_key_last($multi->handles)], $timeout)) {
366+
if (0 !== $selected = curl_multi_select($multi->handle, $timeout)) {
379367
return $selected;
380368
}
381369

Tests/CurlHttpClientTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ public function testHandleIsReinitOnReset()
6666
$r = new \ReflectionProperty($httpClient, 'multi');
6767
$r->setAccessible(true);
6868
$clientState = $r->getValue($httpClient);
69-
$initialHandleId = (int) $clientState->handles[0];
69+
$initialShareId = $clientState->share;
7070
$httpClient->reset();
71-
self::assertNotSame($initialHandleId, (int) $clientState->handles[0]);
71+
self::assertNotSame($initialShareId, $clientState->share);
7272
}
7373

7474
public function testProcessAfterReset()

0 commit comments

Comments
 (0)
0