Skip to content

Commit

Permalink
bug symfony#52329 [HttpClient] Psr18Client: parse HTTP Reason Phrase …
Browse files Browse the repository at this point in the history
…for Response (Hanmac)

This PR was submitted for the 6.4 branch but it was squashed and merged into the 5.4 branch instead.

Discussion
----------

[HttpClient] Psr18Client: parse HTTP Reason Phrase for Response

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | Fix symfony#51527
| License       | MIT

Commits
-------

115a5a1 [HttpClient] Psr18Client: parse HTTP Reason Phrase for Response
  • Loading branch information
nicolas-grekas committed Oct 29, 2023
2 parents f45db03 + 115a5a1 commit 4a4676a
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpClient/HttplugClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function __construct(HttpClientInterface $client = null, ResponseFactoryI
public function sendRequest(RequestInterface $request): Psr7ResponseInterface
{
try {
return $this->waitLoop->createPsr7Response($this->sendPsr7Request($request));
return HttplugWaitLoop::createPsr7Response($this->responseFactory, $this->streamFactory, $this->client, $this->sendPsr7Request($request), true);
} catch (TransportExceptionInterface $e) {
throw new NetworkException($e->getMessage(), $request, $e);
}
Expand Down
20 changes: 14 additions & 6 deletions src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public function wait(?ResponseInterface $pendingResponse, float $maxDuration = n

if ([, $promise] = $this->promisePool[$response] ?? null) {
unset($this->promisePool[$response]);
$promise->resolve($this->createPsr7Response($response, true));
$promise->resolve(self::createPsr7Response($this->responseFactory, $this->streamFactory, $this->client, $response, true));
}
} catch (\Exception $e) {
if ([$request, $promise] = $this->promisePool[$response] ?? null) {
Expand Down Expand Up @@ -114,9 +114,17 @@ public function wait(?ResponseInterface $pendingResponse, float $maxDuration = n
return $count;
}

public function createPsr7Response(ResponseInterface $response, bool $buffer = false): Psr7ResponseInterface
public static function createPsr7Response(ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory, HttpClientInterface $client, ResponseInterface $response, bool $buffer): Psr7ResponseInterface
{
$psrResponse = $this->responseFactory->createResponse($response->getStatusCode());
$responseParameters = [$response->getStatusCode()];

foreach ($response->getInfo('response_headers') as $h) {
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? (?:\d\d\d) (.+)#', $h, $m)) {
$responseParameters[1] = $m[1];
}
}

$psrResponse = $responseFactory->createResponse(...$responseParameters);

foreach ($response->getHeaders(false) as $name => $values) {
foreach ($values as $value) {
Expand All @@ -129,11 +137,11 @@ public function createPsr7Response(ResponseInterface $response, bool $buffer = f
}

if ($response instanceof StreamableInterface) {
$body = $this->streamFactory->createStreamFromResource($response->toStream(false));
$body = $streamFactory->createStreamFromResource($response->toStream(false));
} elseif (!$buffer) {
$body = $this->streamFactory->createStreamFromResource(StreamWrapper::createResource($response, $this->client));
$body = $streamFactory->createStreamFromResource(StreamWrapper::createResource($response, $client));
} else {
$body = $this->streamFactory->createStream($response->getContent(false));
$body = $streamFactory->createStream($response->getContent(false));
}

if ($body->isSeekable()) {
Expand Down
23 changes: 3 additions & 20 deletions src/Symfony/Component/HttpClient/Psr18Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Symfony\Component\HttpClient\Internal\HttplugWaitLoop;
use Symfony\Component\HttpClient\Response\StreamableInterface;
use Symfony\Component\HttpClient\Response\StreamWrapper;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface as HttpClientResponseInterface;
use Symfony\Contracts\Service\ResetInterface;

if (!interface_exists(RequestFactoryInterface::class)) {
Expand Down Expand Up @@ -102,26 +104,7 @@ public function sendRequest(RequestInterface $request): ResponseInterface

$response = $this->client->request($request->getMethod(), (string) $request->getUri(), $options);

$psrResponse = $this->responseFactory->createResponse($response->getStatusCode());

foreach ($response->getHeaders(false) as $name => $values) {
foreach ($values as $value) {
try {
$psrResponse = $psrResponse->withAddedHeader($name, $value);
} catch (\InvalidArgumentException $e) {
// ignore invalid header
}
}
}

$body = $response instanceof StreamableInterface ? $response->toStream(false) : StreamWrapper::createResource($response, $this->client);
$body = $this->streamFactory->createStreamFromResource($body);

if ($body->isSeekable()) {
$body->seek(0);
}

return $psrResponse->withBody($body);
return HttplugWaitLoop::createPsr7Response($this->responseFactory, $this->streamFactory, $this->client, $response, false);
} catch (TransportExceptionInterface $e) {
if ($e instanceof \InvalidArgumentException) {
throw new Psr18RequestException($e, $request);
Expand Down
15 changes: 15 additions & 0 deletions src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,19 @@ public function testInvalidHeaderResponse()
$resultResponse = $client->sendRequest($request);
$this->assertCount(1, $resultResponse->getHeaders());
}

public function testResponseReasonPhrase()
{
$responseHeaders = [
'HTTP/1.1 103 Very Early Hints',
];
$response = new MockResponse('body', ['response_headers' => $responseHeaders]);

$client = new HttplugClient(new MockHttpClient($response));
$request = $client->createRequest('POST', 'http://localhost:8057/post')
->withBody($client->createStream('foo=0123456789'));

$resultResponse = $client->sendRequest($request);
$this->assertSame('Very Early Hints', $resultResponse->getReasonPhrase());
}
}
15 changes: 15 additions & 0 deletions src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,19 @@ public function testInvalidHeaderResponse()
$resultResponse = $client->sendRequest($request);
$this->assertCount(1, $resultResponse->getHeaders());
}

public function testResponseReasonPhrase()
{
$responseHeaders = [
'HTTP/1.1 103 Very Early Hints',
];
$response = new MockResponse('body', ['response_headers' => $responseHeaders]);

$client = new Psr18Client(new MockHttpClient($response));
$request = $client->createRequest('POST', 'http://localhost:8057/post')
->withBody($client->createStream('foo=0123456789'));

$resultResponse = $client->sendRequest($request);
$this->assertSame('Very Early Hints', $resultResponse->getReasonPhrase());
}
}

0 comments on commit 4a4676a

Please sign in to comment.