Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1fac9ee

Browse filesBrowse files
Merge branch '5.2' into 5.3
* 5.2: [HttpClient] throw exception when AsyncDecoratorTrait gets an already consumed response [Validator] Fix tests by making constraint options dumps order consistent
2 parents e086194 + 5b21ce2 commit 1fac9ee
Copy full SHA for 1fac9ee

File tree

4 files changed

+43
-8
lines changed
Filter options

4 files changed

+43
-8
lines changed

‎src/Symfony/Component/HttpClient/Response/AsyncResponse.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/Response/AsyncResponse.php
+14-3Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ final class AsyncResponse implements ResponseInterface, StreamableInterface
3131
{
3232
use CommonResponseTrait;
3333

34+
private const FIRST_CHUNK_YIELDED = 1;
35+
private const LAST_CHUNK_YIELDED = 2;
36+
3437
private $client;
3538
private $response;
3639
private $info = ['canceled' => false];
3740
private $passthru;
3841
private $stream;
39-
private $lastYielded = false;
42+
private $yieldedState;
4043

4144
/**
4245
* @param ?callable(ChunkInterface, AsyncContext): ?\Iterator $passthru
@@ -272,6 +275,14 @@ public static function stream(iterable $responses, float $timeout = null, string
272275
continue;
273276
}
274277

278+
if (null !== $chunk->getError()) {
279+
// no-op
280+
} elseif ($chunk->isFirst()) {
281+
$r->yieldedState = self::FIRST_CHUNK_YIELDED;
282+
} elseif (self::FIRST_CHUNK_YIELDED !== $r->yieldedState && null === $chunk->getInformationalStatus()) {
283+
throw new \LogicException(sprintf('Instance of "%s" is already consumed and cannot be managed by "%s". A decorated client should not call any of the response\'s methods in its "request()" method.', get_debug_type($response), $class ?? static::class));
284+
}
285+
275286
foreach (self::passthru($r->client, $r, $chunk, $asyncMap) as $chunk) {
276287
yield $r => $chunk;
277288
}
@@ -282,9 +293,9 @@ public static function stream(iterable $responses, float $timeout = null, string
282293
}
283294

284295
if (null === $chunk->getError() && $chunk->isLast()) {
285-
$r->lastYielded = true;
296+
$r->yieldedState = self::LAST_CHUNK_YIELDED;
286297
}
287-
if (null === $chunk->getError() && !$r->lastYielded && $r->response === $response && null !== $r->client) {
298+
if (null === $chunk->getError() && self::LAST_CHUNK_YIELDED !== $r->yieldedState && $r->response === $response && null !== $r->client) {
288299
throw new \LogicException('A chunk passthru must yield an "isLast()" chunk before ending a stream.');
289300
}
290301

‎src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php
+24-2Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpClient\Tests;
1313

1414
use Symfony\Component\HttpClient\AsyncDecoratorTrait;
15+
use Symfony\Component\HttpClient\DecoratorTrait;
1516
use Symfony\Component\HttpClient\Response\AsyncContext;
1617
use Symfony\Component\HttpClient\Response\AsyncResponse;
1718
use Symfony\Contracts\HttpClient\ChunkInterface;
@@ -22,15 +23,15 @@
2223

2324
class AsyncDecoratorTraitTest extends NativeHttpClientTest
2425
{
25-
protected function getHttpClient(string $testCase, \Closure $chunkFilter = null): HttpClientInterface
26+
protected function getHttpClient(string $testCase, \Closure $chunkFilter = null, HttpClientInterface $decoratedClient = null): HttpClientInterface
2627
{
2728
if ('testHandleIsRemovedOnException' === $testCase) {
2829
$this->markTestSkipped("AsyncDecoratorTrait doesn't cache handles");
2930
}
3031

3132
$chunkFilter = $chunkFilter ?? static function (ChunkInterface $chunk, AsyncContext $context) { yield $chunk; };
3233

33-
return new class(parent::getHttpClient($testCase), $chunkFilter) implements HttpClientInterface {
34+
return new class($decoratedClient ?? parent::getHttpClient($testCase), $chunkFilter) implements HttpClientInterface {
3435
use AsyncDecoratorTrait;
3536

3637
private $chunkFilter;
@@ -303,4 +304,25 @@ public function testMultipleYieldInInitializer()
303304
$this->assertSame(404, $response->getStatusCode());
304305
$this->assertStringContainsString('injectedFoo', $response->getContent(false));
305306
}
307+
308+
public function testConsumingDecoratedClient()
309+
{
310+
$client = $this->getHttpClient(__FUNCTION__, null, new class(parent::getHttpClient(__FUNCTION__)) implements HttpClientInterface {
311+
use DecoratorTrait;
312+
313+
public function request(string $method, string $url, array $options = []): ResponseInterface
314+
{
315+
$response = $this->client->request($method, $url, $options);
316+
$response->getStatusCode(); // should be avoided and breaks compatibility with AsyncDecoratorTrait
317+
318+
return $response;
319+
}
320+
});
321+
322+
$response = $client->request('GET', 'http://localhost:8057/');
323+
324+
$this->expectException(\LogicException::class);
325+
$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.');
326+
$response->getStatusCode();
327+
}
306328
}

‎src/Symfony/Component/Validator/Command/DebugCommand.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Command/DebugCommand.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ private function getConstraintOptions(Constraint $constraint): array
166166
$options[$propertyName] = $constraint->$propertyName;
167167
}
168168

169+
ksort($options);
170+
169171
return $options;
170172
}
171173

‎src/Symfony/Component/Validator/Tests/Command/DebugCommandTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Tests/Command/DebugCommandTest.php
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ public function testOutputWithClassArgument()
7272
| Property | Name | Groups | Options |
7373
+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
7474
| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
75-
| | | | "message" => "This value should not be blank.", |
7675
| | | | "allowNull" => false, |
76+
| | | | "message" => "This value should not be blank.", |
7777
| | | | "normalizer" => null, |
7878
| | | | "payload" => null |
7979
| | | | ] |
@@ -133,8 +133,8 @@ public function testOutputWithPathArgument()
133133
| Property | Name | Groups | Options |
134134
+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
135135
| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
136-
| | | | "message" => "This value should not be blank.", |
137136
| | | | "allowNull" => false, |
137+
| | | | "message" => "This value should not be blank.", |
138138
| | | | "normalizer" => null, |
139139
| | | | "payload" => null |
140140
| | | | ] |
@@ -153,8 +153,8 @@ public function testOutputWithPathArgument()
153153
| Property | Name | Groups | Options |
154154
+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
155155
| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
156-
| | | | "message" => "This value should not be blank.", |
157156
| | | | "allowNull" => false, |
157+
| | | | "message" => "This value should not be blank.", |
158158
| | | | "normalizer" => null, |
159159
| | | | "payload" => null |
160160
| | | | ] |

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.