15
15
use Psr \Log \LoggerAwareTrait ;
16
16
use Psr \Log \LoggerInterface ;
17
17
use Symfony \Component \HttpClient \Exception \TransportException ;
18
+ use Symfony \Component \HttpClient \Internal \CurlClientState ;
19
+ use Symfony \Component \HttpClient \Internal \PushedResponse ;
18
20
use Symfony \Component \HttpClient \Response \CurlResponse ;
19
21
use Symfony \Component \HttpClient \Response \ResponseStream ;
20
22
use Symfony \Contracts \HttpClient \HttpClientInterface ;
@@ -37,6 +39,12 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface
37
39
use LoggerAwareTrait;
38
40
39
41
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
+ */
40
48
private $ multi ;
41
49
42
50
/**
@@ -56,22 +64,13 @@ public function __construct(array $defaultOptions = [], int $maxHostConnections
56
64
[, $ this ->defaultOptions ] = self ::prepareRequest (null , null , $ defaultOptions , self ::OPTIONS_DEFAULTS );
57
65
}
58
66
59
- $ mh = curl_multi_init ();
67
+ $ this -> multi = $ multi = new CurlClientState ();
60
68
61
69
// Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
62
70
if (\defined ('CURLPIPE_MULTIPLEX ' )) {
63
- curl_multi_setopt ($ mh , CURLMOPT_PIPELINING , CURLPIPE_MULTIPLEX );
71
+ curl_multi_setopt ($ this -> multi -> handle , CURLMOPT_PIPELINING , CURLPIPE_MULTIPLEX );
64
72
}
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 );
75
74
76
75
// Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/bug.php?id=77535
77
76
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
85
84
86
85
$ logger = &$ this ->logger ;
87
86
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 ) {
89
88
return self ::handlePush ($ parent , $ pushed , $ requestHeaders , $ multi , $ maxPendingPushes , $ logger );
90
89
});
91
90
}
@@ -103,7 +102,7 @@ public function request(string $method, string $url, array $options = []): Respo
103
102
$ host = parse_url ($ authority , PHP_URL_HOST );
104
103
$ url = implode ('' , $ url );
105
104
106
- if ([ $ pushedResponse, $ pushedHeaders ] = $ this ->multi ->pushedResponses [$ url ] ?? null ) {
105
+ if ($ pushedResponse = $ this ->multi ->pushedResponses [$ url ] ?? null ) {
107
106
unset($ this ->multi ->pushedResponses [$ url ]);
108
107
// Accept pushed responses only if their headers related to authentication match the request
109
108
$ expectedHeaders = [
@@ -113,13 +112,13 @@ public function request(string $method, string $url, array $options = []): Respo
113
112
$ options ['headers ' ]['range ' ] ?? null ,
114
113
];
115
114
116
- if ('GET ' === $ method && $ expectedHeaders === $ pushedHeaders && !$ options ['body ' ]) {
115
+ if ('GET ' === $ method && $ expectedHeaders === $ pushedResponse -> headers && !$ options ['body ' ]) {
117
116
$ this ->logger && $ this ->logger ->debug (sprintf ('Connecting request to pushed response: "%s %s" ' , $ method , $ url ));
118
117
119
118
// 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 );
121
120
122
- return $ pushedResponse ;
121
+ return $ pushedResponse-> response ;
123
122
}
124
123
125
124
$ 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
159
158
}
160
159
161
160
// 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 ]];
164
163
}
165
164
166
- if ($ options ['resolve ' ] || $ this ->multi ->dnsCache [ 2 ] ) {
165
+ if ($ options ['resolve ' ] || $ this ->multi ->dnsCache -> evictions ) {
167
166
// 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 = [];
170
169
$ port = parse_url ($ authority , PHP_URL_PORT ) ?: ('http: ' === $ scheme ? 80 : 443 );
171
170
172
171
if ($ resolve && 0x072a00 > curl_version ()['version_number ' ]) {
@@ -178,8 +177,8 @@ public function request(string $method, string $url, array $options = []): Respo
178
177
179
178
foreach ($ options ['resolve ' ] as $ host => $ ip ) {
180
179
$ 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 " ;
183
182
}
184
183
185
184
$ curlopts [CURLOPT_RESOLVE ] = $ resolve ;
@@ -299,7 +298,7 @@ public function __destruct()
299
298
}
300
299
}
301
300
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
303
302
{
304
303
$ headers = [];
305
304
$ origin = curl_getinfo ($ parent , CURLINFO_EFFECTIVE_URL );
@@ -336,15 +335,15 @@ private static function handlePush($parent, $pushed, array $requestHeaders, \std
336
335
$ url .= $ headers [':path ' ];
337
336
$ logger && $ logger ->debug (sprintf ('Queueing pushed response: "%s" ' , $ url ));
338
337
339
- $ multi ->pushedResponses [$ url ] = [
338
+ $ multi ->pushedResponses [$ url ] = new PushedResponse (
340
339
new CurlResponse ($ multi , $ pushed ),
341
340
[
342
341
$ headers ['authorization ' ] ?? null ,
343
342
$ headers ['cookie ' ] ?? null ,
344
343
$ headers ['x-requested-with ' ] ?? null ,
345
344
null ,
346
- ],
347
- ] ;
345
+ ]
346
+ ) ;
348
347
349
348
return CURL_PUSH_OK ;
350
349
}
0 commit comments