12
12
namespace Symfony \Component \HttpClient ;
13
13
14
14
use Psr \Log \LoggerAwareInterface ;
15
- use Psr \Log \LoggerAwareTrait ;
15
+ use Psr \Log \LoggerInterface ;
16
16
use Symfony \Component \HttpClient \Exception \InvalidArgumentException ;
17
17
use Symfony \Component \HttpClient \Exception \TransportException ;
18
18
use Symfony \Component \HttpClient \Internal \CurlClientState ;
35
35
final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface
36
36
{
37
37
use HttpClientTrait;
38
- use LoggerAwareTrait;
39
38
40
39
private $ defaultOptions = self ::OPTIONS_DEFAULTS + [
41
40
'auth_ntlm ' => null , // array|string - an array containing the username as first value, and optionally the
42
41
// password as the second one; or string like username:password - enabling NTLM auth
43
42
];
44
43
44
+ /**
45
+ * @var LoggerInterface|null
46
+ */
47
+ private $ logger ;
48
+
45
49
/**
46
50
* An internal object to share state between the client and its responses.
47
51
*
48
52
* @var CurlClientState
49
53
*/
50
54
private $ multi ;
51
55
52
- private static $ curlVersion ;
53
-
54
56
/**
55
57
* @param array $defaultOptions Default request's options
56
58
* @param int $maxHostConnections The maximum number of connections to a single host
@@ -70,33 +72,12 @@ public function __construct(array $defaultOptions = [], int $maxHostConnections
70
72
[, $ this ->defaultOptions ] = self ::prepareRequest (null , null , $ defaultOptions , $ this ->defaultOptions );
71
73
}
72
74
73
- $ this ->multi = new CurlClientState ();
74
- self ::$ curlVersion = self ::$ curlVersion ?? curl_version ();
75
-
76
- // Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
77
- if (\defined ('CURLPIPE_MULTIPLEX ' )) {
78
- curl_multi_setopt ($ this ->multi ->handle , \CURLMOPT_PIPELINING , \CURLPIPE_MULTIPLEX );
79
- }
80
- if (\defined ('CURLMOPT_MAX_HOST_CONNECTIONS ' )) {
81
- $ maxHostConnections = curl_multi_setopt ($ this ->multi ->handle , \CURLMOPT_MAX_HOST_CONNECTIONS , 0 < $ maxHostConnections ? $ maxHostConnections : \PHP_INT_MAX ) ? 0 : $ maxHostConnections ;
82
- }
83
- if (\defined ('CURLMOPT_MAXCONNECTS ' ) && 0 < $ maxHostConnections ) {
84
- curl_multi_setopt ($ this ->multi ->handle , \CURLMOPT_MAXCONNECTS , $ maxHostConnections );
85
- }
86
-
87
- // Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/77535
88
- if (0 >= $ maxPendingPushes || \PHP_VERSION_ID < 70217 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304 )) {
89
- return ;
90
- }
91
-
92
- // HTTP/2 push crashes before curl 7.61
93
- if (!\defined ('CURLMOPT_PUSHFUNCTION ' ) || 0x073D00 > self ::$ curlVersion ['version_number ' ] || !(\CURL_VERSION_HTTP2 & self ::$ curlVersion ['features ' ])) {
94
- return ;
95
- }
75
+ $ this ->multi = new CurlClientState ($ maxHostConnections , $ maxPendingPushes );
76
+ }
96
77
97
- curl_multi_setopt ( $ this -> multi -> handle , \ CURLMOPT_PUSHFUNCTION , function ( $ parent , $ pushed , array $ requestHeaders ) use ( $ maxPendingPushes ) {
98
- return $ this -> handlePush ( $ parent , $ pushed , $ requestHeaders , $ maxPendingPushes );
99
- }) ;
78
+ public function setLogger ( LoggerInterface $ logger ): void
79
+ {
80
+ $ this -> logger = $ this -> multi -> logger = $ logger ;
100
81
}
101
82
102
83
/**
@@ -142,7 +123,7 @@ public function request(string $method, string $url, array $options = []): Respo
142
123
$ curlopts [\CURLOPT_HTTP_VERSION ] = \CURL_HTTP_VERSION_1_0 ;
143
124
} elseif (1.1 === (float ) $ options ['http_version ' ]) {
144
125
$ curlopts [\CURLOPT_HTTP_VERSION ] = \CURL_HTTP_VERSION_1_1 ;
145
- } elseif (\defined ('CURL_VERSION_HTTP2 ' ) && (\CURL_VERSION_HTTP2 & self ::$ curlVersion ['features ' ]) && ('https: ' === $ scheme || 2.0 === (float ) $ options ['http_version ' ])) {
126
+ } elseif (\defined ('CURL_VERSION_HTTP2 ' ) && (\CURL_VERSION_HTTP2 & CurlClientState ::$ curlVersion ['features ' ]) && ('https: ' === $ scheme || 2.0 === (float ) $ options ['http_version ' ])) {
146
127
$ curlopts [\CURLOPT_HTTP_VERSION ] = \CURL_HTTP_VERSION_2_0 ;
147
128
}
148
129
@@ -185,7 +166,7 @@ public function request(string $method, string $url, array $options = []): Respo
185
166
$ this ->multi ->dnsCache ->evictions = [];
186
167
$ port = parse_url ($ authority , \PHP_URL_PORT ) ?: ('http: ' === $ scheme ? 80 : 443 );
187
168
188
- if ($ resolve && 0x072A00 > self ::$ curlVersion ['version_number ' ]) {
169
+ if ($ resolve && 0x072A00 > CurlClientState ::$ curlVersion ['version_number ' ]) {
189
170
// DNS cache removals require curl 7.42 or higher
190
171
// On lower versions, we have to create a new multi handle
191
172
curl_multi_close ($ this ->multi ->handle );
@@ -312,7 +293,7 @@ public function request(string $method, string $url, array $options = []): Respo
312
293
}
313
294
}
314
295
315
- return $ pushedResponse ?? new CurlResponse ($ this ->multi , $ ch , $ options , $ this ->logger , $ method , self ::createRedirectResolver ($ options , $ host ), self ::$ curlVersion ['version_number ' ]);
296
+ return $ pushedResponse ?? new CurlResponse ($ this ->multi , $ ch , $ options , $ this ->logger , $ method , self ::createRedirectResolver ($ options , $ host ), CurlClientState ::$ curlVersion ['version_number ' ]);
316
297
}
317
298
318
299
/**
@@ -336,70 +317,9 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa
336
317
337
318
public function reset ()
338
319
{
339
- $ this ->multi ->logger = $ this ->logger ;
340
320
$ this ->multi ->reset ();
341
321
}
342
322
343
- /**
344
- * @return array
345
- */
346
- public function __sleep ()
347
- {
348
- throw new \BadMethodCallException ('Cannot serialize ' .__CLASS__ );
349
- }
350
-
351
- public function __wakeup ()
352
- {
353
- throw new \BadMethodCallException ('Cannot unserialize ' .__CLASS__ );
354
- }
355
-
356
- public function __destruct ()
357
- {
358
- $ this ->multi ->logger = $ this ->logger ;
359
- }
360
-
361
- private function handlePush ($ parent , $ pushed , array $ requestHeaders , int $ maxPendingPushes ): int
362
- {
363
- $ headers = [];
364
- $ origin = curl_getinfo ($ parent , \CURLINFO_EFFECTIVE_URL );
365
-
366
- foreach ($ requestHeaders as $ h ) {
367
- if (false !== $ i = strpos ($ h , ': ' , 1 )) {
368
- $ headers [substr ($ h , 0 , $ i )][] = substr ($ h , 1 + $ i );
369
- }
370
- }
371
-
372
- if (!isset ($ headers [':method ' ]) || !isset ($ headers [':scheme ' ]) || !isset ($ headers [':authority ' ]) || !isset ($ headers [':path ' ])) {
373
- $ this ->logger && $ this ->logger ->debug (sprintf ('Rejecting pushed response from "%s": pushed headers are invalid ' , $ origin ));
374
-
375
- return \CURL_PUSH_DENY ;
376
- }
377
-
378
- $ url = $ headers [':scheme ' ][0 ].':// ' .$ headers [':authority ' ][0 ];
379
-
380
- // curl before 7.65 doesn't validate the pushed ":authority" header,
381
- // but this is a MUST in the HTTP/2 RFC; let's restrict pushes to the original host,
382
- // ignoring domains mentioned as alt-name in the certificate for now (same as curl).
383
- if (!str_starts_with ($ origin , $ url .'/ ' )) {
384
- $ this ->logger && $ this ->logger ->debug (sprintf ('Rejecting pushed response from "%s": server is not authoritative for "%s" ' , $ origin , $ url ));
385
-
386
- return \CURL_PUSH_DENY ;
387
- }
388
-
389
- if ($ maxPendingPushes <= \count ($ this ->multi ->pushedResponses )) {
390
- $ fifoUrl = key ($ this ->multi ->pushedResponses );
391
- unset($ this ->multi ->pushedResponses [$ fifoUrl ]);
392
- $ this ->logger && $ this ->logger ->debug (sprintf ('Evicting oldest pushed response: "%s" ' , $ fifoUrl ));
393
- }
394
-
395
- $ url .= $ headers [':path ' ][0 ];
396
- $ this ->logger && $ this ->logger ->debug (sprintf ('Queueing pushed response: "%s" ' , $ url ));
397
-
398
- $ this ->multi ->pushedResponses [$ url ] = new PushedResponse (new CurlResponse ($ this ->multi , $ pushed ), $ headers , $ this ->multi ->openHandles [(int ) $ parent ][1 ] ?? [], $ pushed );
399
-
400
- return \CURL_PUSH_OK ;
401
- }
402
-
403
323
/**
404
324
* Accepts pushed responses only if their headers related to authentication match the request.
405
325
*/
0 commit comments