Skip to content

Commit 109fc65

Browse files
Merge branch '4.4' into 5.0
* 4.4: Fix closing tag in mailer collector template [HttpClient] Don't read from the network faster than the CPU can deal with [DI] DecoratorServicePass should keep container.service_locator on the decorated definition
2 parents 14a6308 + 071a00c commit 109fc65

File tree

6 files changed

+98
-162
lines changed

6 files changed

+98
-162
lines changed

CurlHttpClient.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public function request(string $method, string $url, array $options = []): Respo
116116
$url = implode('', $url);
117117

118118
if (!isset($options['normalized_headers']['user-agent'])) {
119-
$options['normalized_headers']['user-agent'][] = $options['headers'][] = 'User-Agent: Symfony HttpClient/Curl';
119+
$options['headers'][] = 'User-Agent: Symfony HttpClient/Curl';
120120
}
121121

122122
$curlopts = [
@@ -217,8 +217,8 @@ public function request(string $method, string $url, array $options = []): Respo
217217
$curlopts[CURLOPT_NOSIGNAL] = true;
218218
}
219219

220-
if (!isset($options['normalized_headers']['accept-encoding']) && CURL_VERSION_LIBZ & self::$curlVersion['features']) {
221-
$curlopts[CURLOPT_ENCODING] = 'gzip'; // Expose only one encoding, some servers mess up when more are provided
220+
if (\extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
221+
$options['headers'][] = 'Accept-Encoding: gzip'; // Expose only one encoding, some servers mess up when more are provided
222222
}
223223

224224
foreach ($options['headers'] as $header) {

NativeHttpClient.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public function request(string $method, string $url, array $options = []): Respo
7777
$options['headers'][] = 'Content-Type: application/x-www-form-urlencoded';
7878
}
7979

80-
if ($gzipEnabled = \extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
80+
if (\extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
8181
// gzip is the most widely available algo, no need to deal with deflate
8282
$options['headers'][] = 'Accept-Encoding: gzip';
8383
}
@@ -227,7 +227,7 @@ public function request(string $method, string $url, array $options = []): Respo
227227
$context = stream_context_create($context, ['notification' => $notification]);
228228
self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, $noProxy);
229229

230-
return new NativeResponse($this->multi, $context, implode('', $url), $options, $gzipEnabled, $info, $resolveRedirect, $onProgress, $this->logger);
230+
return new NativeResponse($this->multi, $context, implode('', $url), $options, $info, $resolveRedirect, $onProgress, $this->logger);
231231
}
232232

233233
/**

Response/CurlResponse.php

Lines changed: 15 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
5252

5353
$this->id = $id = (int) $ch;
5454
$this->logger = $logger;
55+
$this->shouldBuffer = $options['buffer'] ?? true;
5556
$this->timeout = $options['timeout'] ?? null;
5657
$this->info['http_method'] = $method;
5758
$this->info['user_data'] = $options['user_data'] ?? null;
@@ -65,50 +66,25 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
6566
curl_setopt($ch, CURLOPT_PRIVATE, \in_array($method, ['GET', 'HEAD', 'OPTIONS', 'TRACE'], true) && 1.0 < (float) ($options['http_version'] ?? 1.1) ? 'H2' : 'H0'); // H = headers + retry counter
6667
}
6768

68-
if (null === $content = &$this->content) {
69-
$content = null === $options || true === $options['buffer'] ? fopen('php://temp', 'w+') : (\is_resource($options['buffer']) ? $options['buffer'] : null);
70-
} else {
71-
// Move the pushed response to the activity list
72-
$buffer = $options['buffer'];
73-
74-
if ('H' !== curl_getinfo($ch, CURLINFO_PRIVATE)[0]) {
75-
if ($options['buffer'] instanceof \Closure) {
76-
try {
77-
[$content, $buffer] = [null, $content];
78-
[$content, $buffer] = [$buffer, $options['buffer']($headers)];
79-
} catch (\Throwable $e) {
80-
$multi->handlesActivity[$id][] = null;
81-
$multi->handlesActivity[$id][] = $e;
82-
[$content, $buffer] = [$buffer, false];
83-
}
84-
}
85-
86-
if (ftell($content)) {
87-
rewind($content);
88-
$multi->handlesActivity[$id][] = stream_get_contents($content);
89-
}
90-
}
91-
92-
if (\is_resource($buffer)) {
93-
$content = $buffer;
94-
} elseif (true !== $buffer) {
95-
$content = null;
96-
}
97-
}
98-
99-
curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger, &$content): int {
100-
return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger, $content);
69+
curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger): int {
70+
return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger);
10171
});
10272

10373
if (null === $options) {
10474
// Pushed response: buffer until requested
105-
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content): int {
106-
return fwrite($content, $data);
75+
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use ($multi, $id): int {
76+
$multi->handlesActivity[$id][] = $data;
77+
curl_pause($ch, CURLPAUSE_RECV);
78+
79+
return \strlen($data);
10780
});
10881

10982
return;
11083
}
11184

85+
$this->inflate = !isset($options['normalized_headers']['accept-encoding']);
86+
curl_pause($ch, CURLPAUSE_CONT);
87+
11288
if ($onProgress = $options['on_progress']) {
11389
$url = isset($info['url']) ? ['url' => $info['url']] : [];
11490
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
@@ -128,33 +104,16 @@ public function __construct(CurlClientState $multi, $ch, array $options = null,
128104
});
129105
}
130106

131-
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content, $multi, $id): int {
107+
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use ($multi, $id): int {
132108
$multi->handlesActivity[$id][] = $data;
133109

134-
return null !== $content ? fwrite($content, $data) : \strlen($data);
110+
return \strlen($data);
135111
});
136112

137113
$this->initializer = static function (self $response) {
138-
if (null !== $response->info['error']) {
139-
throw new TransportException($response->info['error']);
140-
}
141-
142114
$waitFor = curl_getinfo($ch = $response->handle, CURLINFO_PRIVATE);
143115

144-
if ('H' === $waitFor[0] || 'D' === $waitFor[0]) {
145-
try {
146-
foreach (self::stream([$response]) as $chunk) {
147-
if ($chunk->isFirst()) {
148-
break;
149-
}
150-
}
151-
} catch (\Throwable $e) {
152-
// Persist timeouts thrown during initialization
153-
$response->info['error'] = $e->getMessage();
154-
$response->close();
155-
throw $e;
156-
}
157-
}
116+
return 'H' === $waitFor[0] || 'D' === $waitFor[0];
158117
};
159118

160119
// Schedule the request in a non-blocking way
@@ -241,6 +200,7 @@ public function __destruct()
241200
*/
242201
private function close(): void
243202
{
203+
$this->inflate = null;
244204
unset($this->multi->openHandles[$this->id], $this->multi->handlesActivity[$this->id]);
245205
curl_setopt($this->handle, CURLOPT_PRIVATE, '_0');
246206

@@ -419,22 +379,6 @@ private static function parseHeaderLine($ch, string $data, array &$info, array &
419379
}
420380

421381
curl_setopt($ch, CURLOPT_PRIVATE, $waitFor);
422-
423-
try {
424-
if (!$content && $options['buffer'] instanceof \Closure && $content = $options['buffer']($headers) ?: null) {
425-
$content = \is_resource($content) ? $content : fopen('php://temp', 'w+');
426-
}
427-
428-
if (null !== $info['error']) {
429-
throw new TransportException($info['error']);
430-
}
431-
} catch (\Throwable $e) {
432-
$multi->handlesActivity[$id] = $multi->handlesActivity[$id] ?? [new FirstChunk()];
433-
$multi->handlesActivity[$id][] = null;
434-
$multi->handlesActivity[$id][] = $e;
435-
436-
return 0;
437-
}
438382
} elseif (null !== $info['redirect_url'] && $logger) {
439383
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
440384
}

Response/MockResponse.php

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public function cancel(): void
9494
*/
9595
protected function close(): void
9696
{
97+
$this->inflate = null;
9798
$this->body = [];
9899
}
99100

@@ -105,22 +106,9 @@ public static function fromRequest(string $method, string $url, array $options,
105106
$response = new self([]);
106107
$response->requestOptions = $options;
107108
$response->id = ++self::$idSequence;
108-
109-
if (!($options['buffer'] ?? null) instanceof \Closure) {
110-
$response->content = true === ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : (\is_resource($options['buffer']) ? $options['buffer'] : null);
111-
}
109+
$response->shouldBuffer = $options['buffer'] ?? true;
112110
$response->initializer = static function (self $response) {
113-
if (null !== $response->info['error']) {
114-
throw new TransportException($response->info['error']);
115-
}
116-
117-
if (\is_array($response->body[0] ?? null)) {
118-
foreach (self::stream([$response]) as $chunk) {
119-
if ($chunk->isFirst()) {
120-
break;
121-
}
122-
}
123-
}
111+
return \is_array($response->body[0] ?? null);
124112
};
125113

126114
$response->info['redirect_count'] = 0;
@@ -198,11 +186,6 @@ protected static function perform(ClientState $multi, array &$responses): void
198186
} else {
199187
// Data or timeout chunk
200188
$multi->handlesActivity[$id][] = $chunk;
201-
202-
if (\is_string($chunk) && null !== $response->content) {
203-
// Buffer response body
204-
fwrite($response->content, $chunk);
205-
}
206189
}
207190
}
208191
}

Response/NativeResponse.php

Lines changed: 12 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,14 @@ final class NativeResponse implements ResponseInterface
3232
private $onProgress;
3333
private $remaining;
3434
private $buffer;
35-
private $inflate;
3635
private $multi;
3736
private $debugBuffer;
3837
private $shouldBuffer;
3938

4039
/**
4140
* @internal
4241
*/
43-
public function __construct(NativeClientState $multi, $context, string $url, $options, bool $gzipEnabled, array &$info, callable $resolveRedirect, ?callable $onProgress, ?LoggerInterface $logger)
42+
public function __construct(NativeClientState $multi, $context, string $url, array $options, array &$info, callable $resolveRedirect, ?callable $onProgress, ?LoggerInterface $logger)
4443
{
4544
$this->multi = $multi;
4645
$this->id = (int) $context;
@@ -51,28 +50,17 @@ public function __construct(NativeClientState $multi, $context, string $url, $op
5150
$this->info = &$info;
5251
$this->resolveRedirect = $resolveRedirect;
5352
$this->onProgress = $onProgress;
54-
$this->content = true === $options['buffer'] ? fopen('php://temp', 'w+') : (\is_resource($options['buffer']) ? $options['buffer'] : null);
55-
$this->shouldBuffer = $options['buffer'] instanceof \Closure ? $options['buffer'] : null;
53+
$this->inflate = !isset($options['normalized_headers']['accept-encoding']);
54+
$this->shouldBuffer = $options['buffer'] ?? true;
5655

57-
// Temporary resources to dechunk/inflate the response stream
56+
// Temporary resource to dechunk the response stream
5857
$this->buffer = fopen('php://temp', 'w+');
59-
$this->inflate = $gzipEnabled ? inflate_init(ZLIB_ENCODING_GZIP) : null;
6058

6159
$info['user_data'] = $options['user_data'];
6260
++$multi->responseCount;
6361

6462
$this->initializer = static function (self $response) {
65-
if (null !== $response->info['error']) {
66-
throw new TransportException($response->info['error']);
67-
}
68-
69-
if (null === $response->remaining) {
70-
foreach (self::stream([$response]) as $chunk) {
71-
if ($chunk->isFirst()) {
72-
break;
73-
}
74-
}
75-
}
63+
return null === $response->remaining;
7664
};
7765
}
7866

@@ -169,7 +157,7 @@ private function open(): void
169157
stream_set_blocking($h, false);
170158
$this->context = $this->resolveRedirect = null;
171159

172-
// Create dechunk and inflate buffers
160+
// Create dechunk buffers
173161
if (isset($this->headers['content-length'])) {
174162
$this->remaining = (int) $this->headers['content-length'][0];
175163
} elseif ('chunked' === ($this->headers['transfer-encoding'][0] ?? null)) {
@@ -179,27 +167,6 @@ private function open(): void
179167
$this->remaining = -2;
180168
}
181169

182-
if ($this->inflate && 'gzip' !== ($this->headers['content-encoding'][0] ?? null)) {
183-
$this->inflate = null;
184-
}
185-
186-
try {
187-
if (null !== $this->shouldBuffer && null === $this->content && $this->content = ($this->shouldBuffer)($this->headers) ?: null) {
188-
$this->content = \is_resource($this->content) ? $this->content : fopen('php://temp', 'w+');
189-
}
190-
191-
if (null !== $this->info['error']) {
192-
throw new TransportException($this->info['error']);
193-
}
194-
} catch (\Throwable $e) {
195-
$this->close();
196-
$this->multi->handlesActivity[$this->id] = [new FirstChunk()];
197-
$this->multi->handlesActivity[$this->id][] = null;
198-
$this->multi->handlesActivity[$this->id][] = $e;
199-
200-
return;
201-
}
202-
203170
$this->multi->handlesActivity[$this->id] = [new FirstChunk()];
204171

205172
if ('HEAD' === $context['http']['method'] || \in_array($this->info['http_code'], [204, 304], true)) {
@@ -209,7 +176,7 @@ private function open(): void
209176
return;
210177
}
211178

212-
$this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->inflate, $this->content, $this->onProgress, &$this->remaining, &$this->info];
179+
$this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->onProgress, &$this->remaining, &$this->info];
213180
}
214181

215182
/**
@@ -249,15 +216,15 @@ private static function perform(NativeClientState $multi, array &$responses = nu
249216
$multi->handles = [];
250217
}
251218

252-
foreach ($multi->openHandles as $i => [$h, $buffer, $inflate, $content, $onProgress]) {
219+
foreach ($multi->openHandles as $i => [$h, $buffer, $onProgress]) {
253220
$hasActivity = false;
254-
$remaining = &$multi->openHandles[$i][5];
255-
$info = &$multi->openHandles[$i][6];
221+
$remaining = &$multi->openHandles[$i][3];
222+
$info = &$multi->openHandles[$i][4];
256223
$e = null;
257224

258225
// Read incoming buffer and write it to the dechunk one
259226
try {
260-
while ($remaining && '' !== $data = (string) fread($h, 0 > $remaining ? 16372 : $remaining)) {
227+
if ($remaining && '' !== $data = (string) fread($h, 0 > $remaining ? 16372 : $remaining)) {
261228
fwrite($buffer, $data);
262229
$hasActivity = true;
263230
$multi->sleep = false;
@@ -285,16 +252,8 @@ private static function perform(NativeClientState $multi, array &$responses = nu
285252
rewind($buffer);
286253
ftruncate($buffer, 0);
287254

288-
if (null !== $inflate && false === $data = @inflate_add($inflate, $data)) {
289-
$e = new TransportException('Error while processing content unencoding.');
290-
}
291-
292-
if ('' !== $data && null === $e) {
255+
if (null === $e) {
293256
$multi->handlesActivity[$i][] = $data;
294-
295-
if (null !== $content && \strlen($data) !== fwrite($content, $data)) {
296-
$e = new TransportException(sprintf('Failed writing %d bytes to the response buffer.', \strlen($data)));
297-
}
298257
}
299258
}
300259

0 commit comments

Comments
 (0)