1515use Psr \Log \LoggerAwareTrait ;
1616use Psr \Log \LoggerInterface ;
1717use Symfony \Component \HttpClient \Exception \TransportException ;
18+ use Symfony \Component \HttpClient \Internal \CurlClientState ;
19+ use Symfony \Component \HttpClient \Internal \PushedResponse ;
1820use Symfony \Component \HttpClient \Response \CurlResponse ;
1921use Symfony \Component \HttpClient \Response \ResponseStream ;
2022use Symfony \Contracts \HttpClient \HttpClientInterface ;
@@ -37,6 +39,12 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface
3739 use LoggerAwareTrait;
3840
3941 private $ defaultOptions = self ::OPTIONS_DEFAULTS ;
42+
43+ /**
44+ * An internal object to share state between the client and its responses.
45+ *
46+ * @var CurlClientState
47+ */
4048 private $ multi ;
4149
4250 /**
@@ -56,22 +64,13 @@ public function __construct(array $defaultOptions = [], int $maxHostConnections
5664 [, $ this ->defaultOptions ] = self ::prepareRequest (null , null , $ defaultOptions , self ::OPTIONS_DEFAULTS );
5765 }
5866
59- $ mh = curl_multi_init ();
67+ $ this -> multi = $ multi = new CurlClientState ();
6068
6169 // Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
6270 if (\defined ('CURLPIPE_MULTIPLEX ' )) {
63- curl_multi_setopt ($ mh , CURLMOPT_PIPELINING , CURLPIPE_MULTIPLEX );
71+ curl_multi_setopt ($ this -> multi -> handle , CURLMOPT_PIPELINING , CURLPIPE_MULTIPLEX );
6472 }
65- curl_multi_setopt ($ mh , CURLMOPT_MAX_HOST_CONNECTIONS , 0 < $ maxHostConnections ? $ maxHostConnections : PHP_INT_MAX );
66-
67- // Use an internal stdClass object to share state between the client and its responses
68- $ this ->multi = $ multi = (object ) [
69- 'openHandles ' => [],
70- 'handlesActivity ' => [],
71- 'handle ' => $ mh ,
72- 'pushedResponses ' => [],
73- 'dnsCache ' => [[], [], []],
74- ];
73+ curl_multi_setopt ($ this ->multi ->handle , CURLMOPT_MAX_HOST_CONNECTIONS , 0 < $ maxHostConnections ? $ maxHostConnections : PHP_INT_MAX );
7574
7675 // Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/bug.php?id=77535
7776 if (0 >= $ maxPendingPushes || \PHP_VERSION_ID < 70217 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304 )) {
@@ -85,7 +84,7 @@ public function __construct(array $defaultOptions = [], int $maxHostConnections
8584
8685 $ logger = &$ this ->logger ;
8786
88- curl_multi_setopt ($ mh , CURLMOPT_PUSHFUNCTION , static function ($ parent , $ pushed , array $ requestHeaders ) use ($ multi , $ maxPendingPushes , &$ logger ) {
87+ curl_multi_setopt ($ this -> multi -> handle , CURLMOPT_PUSHFUNCTION , static function ($ parent , $ pushed , array $ requestHeaders ) use ($ multi , $ maxPendingPushes , &$ logger ) {
8988 return self ::handlePush ($ parent , $ pushed , $ requestHeaders , $ multi , $ maxPendingPushes , $ logger );
9089 });
9190 }
@@ -103,7 +102,7 @@ public function request(string $method, string $url, array $options = []): Respo
103102 $ host = parse_url ($ authority , PHP_URL_HOST );
104103 $ url = implode ('' , $ url );
105104
106- if ([ $ pushedResponse, $ pushedHeaders ] = $ this ->multi ->pushedResponses [$ url ] ?? null ) {
105+ if ($ pushedResponse = $ this ->multi ->pushedResponses [$ url ] ?? null ) {
107106 unset($ this ->multi ->pushedResponses [$ url ]);
108107 // Accept pushed responses only if their headers related to authentication match the request
109108 $ expectedHeaders = [
@@ -113,13 +112,13 @@ public function request(string $method, string $url, array $options = []): Respo
113112 $ options ['headers ' ]['range ' ] ?? null ,
114113 ];
115114
116- if ('GET ' === $ method && $ expectedHeaders === $ pushedHeaders && !$ options ['body ' ]) {
115+ if ('GET ' === $ method && $ expectedHeaders === $ pushedResponse -> headers && !$ options ['body ' ]) {
117116 $ this ->logger && $ this ->logger ->debug (sprintf ('Connecting request to pushed response: "%s %s" ' , $ method , $ url ));
118117
119118 // Reinitialize the pushed response with request's options
120- $ pushedResponse ->__construct ($ this ->multi , $ url , $ options , $ this ->logger );
119+ $ pushedResponse ->response -> __construct ($ this ->multi , $ url , $ options , $ this ->logger );
121120
122- return $ pushedResponse ;
121+ return $ pushedResponse-> response ;
123122 }
124123
125124 $ this ->logger && $ this ->logger ->debug (sprintf ('Rejecting pushed response for "%s": authorization headers don \'t match the request ' , $ url ));
@@ -159,14 +158,14 @@ public function request(string $method, string $url, array $options = []): Respo
159158 }
160159
161160 // curl's resolve feature varies by host:port but ours varies by host only, let's handle this with our own DNS map
162- if (isset ($ this ->multi ->dnsCache [ 0 ] [$ host ])) {
163- $ options ['resolve ' ] += [$ host => $ this ->multi ->dnsCache [ 0 ] [$ host ]];
161+ if (isset ($ this ->multi ->dnsCache -> hostnames [$ host ])) {
162+ $ options ['resolve ' ] += [$ host => $ this ->multi ->dnsCache -> hostnames [$ host ]];
164163 }
165164
166- if ($ options ['resolve ' ] || $ this ->multi ->dnsCache [ 2 ] ) {
165+ if ($ options ['resolve ' ] || $ this ->multi ->dnsCache -> evictions ) {
167166 // First reset any old DNS cache entries then add the new ones
168- $ resolve = $ this ->multi ->dnsCache [ 2 ] ;
169- $ this ->multi ->dnsCache [ 2 ] = [];
167+ $ resolve = $ this ->multi ->dnsCache -> evictions ;
168+ $ this ->multi ->dnsCache -> evictions = [];
170169 $ port = parse_url ($ authority , PHP_URL_PORT ) ?: ('http: ' === $ scheme ? 80 : 443 );
171170
172171 if ($ resolve && 0x072a00 > curl_version ()['version_number ' ]) {
@@ -178,8 +177,8 @@ public function request(string $method, string $url, array $options = []): Respo
178177
179178 foreach ($ options ['resolve ' ] as $ host => $ ip ) {
180179 $ resolve [] = null === $ ip ? "- $ host: $ port " : "$ host: $ port: $ ip " ;
181- $ this ->multi ->dnsCache [ 0 ] [$ host ] = $ ip ;
182- $ this ->multi ->dnsCache [ 1 ] ["- $ host: $ port " ] = "- $ host: $ port " ;
180+ $ this ->multi ->dnsCache -> hostnames [$ host ] = $ ip ;
181+ $ this ->multi ->dnsCache -> removals ["- $ host: $ port " ] = "- $ host: $ port " ;
183182 }
184183
185184 $ curlopts [CURLOPT_RESOLVE ] = $ resolve ;
@@ -299,7 +298,7 @@ public function __destruct()
299298 }
300299 }
301300
302- private static function handlePush ($ parent , $ pushed , array $ requestHeaders , \ stdClass $ multi , int $ maxPendingPushes , ?LoggerInterface $ logger ): int
301+ private static function handlePush ($ parent , $ pushed , array $ requestHeaders , CurlClientState $ multi , int $ maxPendingPushes , ?LoggerInterface $ logger ): int
303302 {
304303 $ headers = [];
305304 $ origin = curl_getinfo ($ parent , CURLINFO_EFFECTIVE_URL );
@@ -336,15 +335,15 @@ private static function handlePush($parent, $pushed, array $requestHeaders, \std
336335 $ url .= $ headers [':path ' ];
337336 $ logger && $ logger ->debug (sprintf ('Queueing pushed response: "%s" ' , $ url ));
338337
339- $ multi ->pushedResponses [$ url ] = [
338+ $ multi ->pushedResponses [$ url ] = new PushedResponse (
340339 new CurlResponse ($ multi , $ pushed ),
341340 [
342341 $ headers ['authorization ' ] ?? null ,
343342 $ headers ['cookie ' ] ?? null ,
344343 $ headers ['x-requested-with ' ] ?? null ,
345344 null ,
346- ],
347- ] ;
345+ ]
346+ ) ;
348347
349348 return CURL_PUSH_OK ;
350349 }
0 commit comments