@@ -150,7 +150,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
150150// Schedule the request in a non-blocking way
151151$ multi ->lastTimeout =null ;
152152$ multi ->openHandles [$ id ] = [$ ch ,$ options ];
153- curl_multi_add_handle ($ multi ->handle ,$ ch );
153+ curl_multi_add_handle ($ multi ->handles [ 0 ] ,$ ch );
154154
155155$ this ->canary =new Canary (static function ()use ($ ch ,$ multi ,$ id ) {
156156 unset($ multi ->openHandles [$ id ],$ multi ->handlesActivity [$ id ]);
@@ -160,7 +160,9 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
160160return ;
161161 }
162162
163- curl_multi_remove_handle ($ multi ->handle ,$ ch );
163+ foreach ($ multi ->handles as $ mh ) {
164+ curl_multi_remove_handle ($ mh ,$ ch );
165+ }
164166curl_setopt_array ($ ch , [
165167 \CURLOPT_NOPROGRESS =>true ,
166168 \CURLOPT_PROGRESSFUNCTION =>null ,
@@ -242,7 +244,7 @@ public function __destruct()
242244 */
243245private static function schedule (self $ response ,array &$ runningResponses ):void
244246 {
245- if (isset ($ runningResponses [$ i = (int )$ response ->multi ->handle ])) {
247+ if (isset ($ runningResponses [$ i = (int )$ response ->multi ->handles [ 0 ] ])) {
246248$ runningResponses [$ i ][1 ][$ response ->id ] =$ response ;
247249 }else {
248250$ runningResponses [$ i ] = [$ response ->multi , [$ response ->id =>$ response ]];
@@ -274,39 +276,47 @@ private static function perform(ClientState $multi, array &$responses = null): v
274276
275277try {
276278self ::$ performing =true ;
277- $ active =0 ;
278- while (\CURLM_CALL_MULTI_PERFORM === ($ err =curl_multi_exec ($ multi ->handle ,$ active ))) {
279- }
280-
281- if (\CURLM_OK !==$ err ) {
282- throw new TransportException (curl_multi_strerror ($ err ));
283- }
284279
285- while ($ info = curl_multi_info_read ( $ multi -> handle ) ) {
286- if (\ CURLMSG_DONE !== $ info [ ' msg ' ]) {
287- continue ;
280+ foreach ($ multi -> handles as $ i => $ mh ) {
281+ $ active = 0 ;
282+ while (\ CURLM_CALL_MULTI_PERFORM === ( $ err = curl_multi_exec ( $ mh , $ active ))) {
288283 }
289- $ result =$ info ['result ' ];
290- $ id = (int )$ ch =$ info ['handle ' ];
291- $ waitFor = @curl_getinfo ($ ch , \CURLINFO_PRIVATE ) ?:'_0 ' ;
292284
293- if (\in_array ($ result , [\CURLE_SEND_ERROR , \CURLE_RECV_ERROR ,/*CURLE_HTTP2*/ 16 ,/*CURLE_HTTP2_STREAM*/ 92 ],true ) &&$ waitFor [1 ] &&'C ' !==$ waitFor [0 ]) {
294- curl_multi_remove_handle ($ multi ->handle ,$ ch );
295- $ waitFor [1 ] = (string ) ((int )$ waitFor [1 ] -1 );// decrement the retry counter
296- curl_setopt ($ ch , \CURLOPT_PRIVATE ,$ waitFor );
297- curl_setopt ($ ch , \CURLOPT_FORBID_REUSE ,true );
285+ if (\CURLM_OK !==$ err ) {
286+ throw new TransportException (curl_multi_strerror ($ err ));
287+ }
298288
299- if (0 ===curl_multi_add_handle ($ multi ->handle ,$ ch )) {
289+ while ($ info =curl_multi_info_read ($ mh )) {
290+ if (\CURLMSG_DONE !==$ info ['msg ' ]) {
300291continue ;
301292 }
302- }
293+ $ result =$ info ['result ' ];
294+ $ id = (int )$ ch =$ info ['handle ' ];
295+ $ waitFor = @curl_getinfo ($ ch , \CURLINFO_PRIVATE ) ?:'_0 ' ;
296+
297+ if (\in_array ($ result , [\CURLE_SEND_ERROR , \CURLE_RECV_ERROR ,/*CURLE_HTTP2*/ 16 ,/*CURLE_HTTP2_STREAM*/ 92 ],true ) &&$ waitFor [1 ] &&'C ' !==$ waitFor [0 ]) {
298+ curl_multi_remove_handle ($ mh ,$ ch );
299+ $ waitFor [1 ] = (string ) ((int )$ waitFor [1 ] -1 );// decrement the retry counter
300+ curl_setopt ($ ch , \CURLOPT_PRIVATE ,$ waitFor );
301+ curl_setopt ($ ch , \CURLOPT_FORBID_REUSE ,true );
302+
303+ if (0 ===curl_multi_add_handle ($ mh ,$ ch )) {
304+ continue ;
305+ }
306+ }
307+
308+ if (\CURLE_RECV_ERROR ===$ result &&'H ' ===$ waitFor [0 ] &&400 <= ($ responses [(int )$ ch ]->info ['http_code ' ] ??0 )) {
309+ $ multi ->handlesActivity [$ id ][] =new FirstChunk ();
310+ }
303311
304- if (\ CURLE_RECV_ERROR === $ result && ' H ' === $ waitFor [ 0 ] && 400 <= ( $ responses [( int ) $ ch ]-> info [ ' http_code ' ] ?? 0 )) {
305- $ multi ->handlesActivity [$ id ][] =new FirstChunk ( );
312+ $ multi -> handlesActivity [ $ id ][] = null ;
313+ $ 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 )) );
306314 }
307315
308- $ multi ->handlesActivity [$ id ][] =null ;
309- $ 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 )));
316+ if (!$ active &&0 <$ i ) {
317+ curl_multi_close ($ mh );
318+ unset($ multi ->handles [$ i ]);
319+ }
310320 }
311321 }finally {
312322self ::$ performing =false ;
@@ -325,7 +335,7 @@ private static function select(ClientState $multi, float $timeout): int
325335$ timeout =min ($ timeout ,0.01 );
326336 }
327337
328- return curl_multi_select ($ multi ->handle ,$ timeout );
338+ return curl_multi_select ($ multi ->handles [ array_key_last ( $ multi -> handles )] ,$ timeout );
329339 }
330340
331341/**