Skip to content

Commit 3c6fc53

Browse files
Merge branch '5.4' into 6.0
* 5.4: Fix merge [Mime] Throw exception when body in Email attach method is not ok [VarDumper][VarExporter] Deal with DatePeriod->include_end_date on PHP 8.2 [Cache] Throw when "redis_sentinel" is used with a non-Predis "class" option fix merge Bootstrap 4 fieldset for row errors [Form] Fix same choice loader with different choice values [Filesystem] Safeguard (sym)link calls Fix dumping extension config without bundle [HttpClient] Honor "max_duration" when replacing requests with async decorators [HttpClient] Add missing HttpOptions::setMaxDuration() [HttpFoundation] [Session] Overwrite invalid session id
2 parents d347895 + dc0b15e commit 3c6fc53

File tree

8 files changed

+50
-1
lines changed

8 files changed

+50
-1
lines changed

HttpOptions.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,16 @@ public function setTimeout(float $timeout): static
195195
return $this;
196196
}
197197

198+
/**
199+
* @return $this
200+
*/
201+
public function setMaxDuration(float $maxDuration): static
202+
{
203+
$this->options['max_duration'] = $maxDuration;
204+
205+
return $this;
206+
}
207+
198208
/**
199209
* @return $this
200210
*/

Response/AmpResponse.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public function __construct(AmpClientState $multi, Request $request, array $opti
8787
$info['upload_content_length'] = -1.0;
8888
$info['download_content_length'] = -1.0;
8989
$info['user_data'] = $options['user_data'];
90+
$info['max_duration'] = $options['max_duration'];
9091
$info['debug'] = '';
9192

9293
$onProgress = $options['on_progress'] ?? static function () {};

Response/AsyncContext.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\HttpClient\Chunk\DataChunk;
1515
use Symfony\Component\HttpClient\Chunk\LastChunk;
16+
use Symfony\Component\HttpClient\Exception\TransportException;
1617
use Symfony\Contracts\HttpClient\ChunkInterface;
1718
use Symfony\Contracts\HttpClient\HttpClientInterface;
1819
use Symfony\Contracts\HttpClient\ResponseInterface;
@@ -155,13 +156,18 @@ public function getResponse(): ResponseInterface
155156
*/
156157
public function replaceRequest(string $method, string $url, array $options = []): ResponseInterface
157158
{
158-
$this->info['previous_info'][] = $this->response->getInfo();
159+
$this->info['previous_info'][] = $info = $this->response->getInfo();
159160
if (null !== $onProgress = $options['on_progress'] ?? null) {
160161
$thisInfo = &$this->info;
161162
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
162163
$onProgress($dlNow, $dlSize, $thisInfo + $info);
163164
};
164165
}
166+
if (0 < ($info['max_duration'] ?? 0) && 0 < ($info['total_time'] ?? 0)) {
167+
if (0 >= $options['max_duration'] = $info['max_duration'] - $info['total_time']) {
168+
throw new TransportException(sprintf('Max duration was reached for "%s".', $info['url']));
169+
}
170+
}
165171

166172
return $this->response = $this->client->request($method, $url, ['buffer' => false] + $options);
167173
}

Response/AsyncResponse.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ public function __construct(HttpClientInterface $client, string $method, string
8585
if (\array_key_exists('user_data', $options)) {
8686
$this->info['user_data'] = $options['user_data'];
8787
}
88+
if (\array_key_exists('max_duration', $options)) {
89+
$this->info['max_duration'] = $options['max_duration'];
90+
}
8891
}
8992

9093
public function getStatusCode(): int

Response/CurlResponse.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra
6767
$this->timeout = $options['timeout'] ?? null;
6868
$this->info['http_method'] = $method;
6969
$this->info['user_data'] = $options['user_data'] ?? null;
70+
$this->info['max_duration'] = $options['max_duration'] ?? null;
7071
$this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
7172
$info = &$this->info;
7273
$headers = &$this->headers;

Response/MockResponse.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public static function fromRequest(string $method, string $url, array $options,
140140
$response->info['http_method'] = $method;
141141
$response->info['http_code'] = 0;
142142
$response->info['user_data'] = $options['user_data'] ?? null;
143+
$response->info['max_duration'] = $options['max_duration'] ?? null;
143144
$response->info['url'] = $url;
144145

145146
if ($mock instanceof self) {
@@ -285,6 +286,7 @@ private static function readResponse(self $response, array $options, ResponseInt
285286
$response->info = [
286287
'start_time' => $response->info['start_time'],
287288
'user_data' => $response->info['user_data'],
289+
'max_duration' => $response->info['max_duration'],
288290
'http_code' => $response->info['http_code'],
289291
] + $info + $response->info;
290292

Response/NativeResponse.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public function __construct(NativeClientState $multi, $context, string $url, arr
6767
$this->buffer = fopen('php://temp', 'w+');
6868

6969
$info['user_data'] = $options['user_data'];
70+
$info['max_duration'] = $options['max_duration'];
7071
++$multi->responseCount;
7172

7273
$this->initializer = static function (self $response) {

Tests/AsyncDecoratorTraitTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\HttpClient\AsyncDecoratorTrait;
1515
use Symfony\Component\HttpClient\DecoratorTrait;
16+
use Symfony\Component\HttpClient\Exception\TransportException;
1617
use Symfony\Component\HttpClient\HttpClient;
1718
use Symfony\Component\HttpClient\Response\AsyncContext;
1819
use Symfony\Component\HttpClient\Response\AsyncResponse;
@@ -339,4 +340,28 @@ public function request(string $method, string $url, array $options = []): Respo
339340
$this->expectExceptionMessage('Instance of "Symfony\Component\HttpClient\Response\NativeResponse" is already consumed and cannot be managed by "Symfony\Component\HttpClient\Response\AsyncResponse". A decorated client should not call any of the response\'s methods in its "request()" method.');
340341
$response->getStatusCode();
341342
}
343+
344+
public function testMaxDuration()
345+
{
346+
$sawFirst = false;
347+
$client = $this->getHttpClient(__FUNCTION__, function (ChunkInterface $chunk, AsyncContext $context) use (&$sawFirst) {
348+
try {
349+
if (!$chunk->isFirst() || !$sawFirst) {
350+
$sawFirst = $sawFirst || $chunk->isFirst();
351+
yield $chunk;
352+
}
353+
} catch (TransportExceptionInterface $e) {
354+
$context->getResponse()->cancel();
355+
$context->replaceRequest('GET', 'http://localhost:8057/timeout-body', ['timeout' => 0.4]);
356+
}
357+
});
358+
359+
$response = $client->request('GET', 'http://localhost:8057/timeout-body', ['max_duration' => 0.75, 'timeout' => 0.4]);
360+
361+
$this->assertSame(0.75, $response->getInfo('max_duration'));
362+
363+
$this->expectException(TransportException::class);
364+
$this->expectExceptionMessage('Max duration was reached for "http://localhost:8057/timeout-body".');
365+
$response->getContent();
366+
}
342367
}

0 commit comments

Comments
 (0)