Skip to content

Commit ad0a07c

Browse files
feature #62059 [HttpClient] Add option auto_upgrade_http_version to control how the request HTTP version is handled in HttplugClient and Psr18Client (ajgarlag)
This PR was squashed before being merged into the 7.4 branch. Discussion ---------- [HttpClient] Add option `auto_upgrade_http_version` to control how the request HTTP version is handled in `HttplugClient` and `Psr18Client` | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | | License | MIT When a PSR-7 request is sent using `Psr18Client`, the PSR-7 request protocol version is only converted to the underlying `http_version` option when is set to `1.0`, so we cannot control that option for `1.1` or `2.0`. I have a project where I need to dynamically force the use of protocol version `1.1` but the final request is always sent using version `2.0`. This PR add option `auto_upgrade_http_version` to control how the request HTTP version is handled in `HttplugClient` and `Psr18Client`. ```php $psr18Client = $psr18Client->withOptions(['auto_upgrade_http_version' => false]); $request = $psr18Client->createRequest()->withProtocolVersion('1.1'); // [...] // The request will use HTTP/1.1, whereas the defaults would upgrade to 2.0 $response = $psr18Client->sendRequest($request); ``` Commits ------- b6ac394 [HttpClient] Add option `auto_upgrade_http_version` to control how the request HTTP version is handled in `HttplugClient` and `Psr18Client`
2 parents 058f45d + b6ac394 commit ad0a07c

File tree

5 files changed

+77
-4
lines changed

5 files changed

+77
-4
lines changed

src/Symfony/Component/HttpClient/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Deprecate using amphp/http-client < 5
88
* Add RFC 9111–based caching support to `CachingHttpClient`
99
* Deprecate passing an instance of `StoreInterface` as `$cache` argument to `CachingHttpClient` constructor
10+
* Add option `auto_upgrade_http_version` to control how the request HTTP version is handled in `HttplugClient` and `Psr18Client`
1011

1112
7.3
1213
---

src/Symfony/Component/HttpClient/HttplugClient.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ final class HttplugClient implements ClientInterface, HttpAsyncClient, RequestFa
6161
private HttpClientInterface $client;
6262
private ResponseFactoryInterface $responseFactory;
6363
private StreamFactoryInterface $streamFactory;
64+
private bool $autoUpgradeHttpVersion = true;
6465

6566
/**
6667
* @var \SplObjectStorage<ResponseInterface, array{RequestInterface, Promise}>|null
@@ -96,6 +97,10 @@ public function __construct(?HttpClientInterface $client = null, ?ResponseFactor
9697
public function withOptions(array $options): static
9798
{
9899
$clone = clone $this;
100+
if (\array_key_exists('auto_upgrade_http_version', $options)) {
101+
$clone->autoUpgradeHttpVersion = $options['auto_upgrade_http_version'];
102+
unset($options['auto_upgrade_http_version']);
103+
}
99104
$clone->client = $clone->client->withOptions($options);
100105

101106
return $clone;
@@ -265,8 +270,8 @@ private function sendPsr7Request(RequestInterface $request, ?bool $buffer = null
265270
'buffer' => $buffer,
266271
];
267272

268-
if ('1.0' === $request->getProtocolVersion()) {
269-
$options['http_version'] = '1.0';
273+
if (!$this->autoUpgradeHttpVersion || '1.0' === $request->getProtocolVersion()) {
274+
$options['http_version'] = $request->getProtocolVersion();
270275
}
271276

272277
return $this->client->request($request->getMethod(), (string) $request->getUri(), $options);

src/Symfony/Component/HttpClient/Psr18Client.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ final class Psr18Client implements ClientInterface, RequestFactoryInterface, Str
5353
private HttpClientInterface $client;
5454
private ResponseFactoryInterface $responseFactory;
5555
private StreamFactoryInterface $streamFactory;
56+
private bool $autoUpgradeHttpVersion = true;
5657

5758
public function __construct(?HttpClientInterface $client = null, ?ResponseFactoryInterface $responseFactory = null, ?StreamFactoryInterface $streamFactory = null)
5859
{
@@ -79,6 +80,10 @@ public function __construct(?HttpClientInterface $client = null, ?ResponseFactor
7980
public function withOptions(array $options): static
8081
{
8182
$clone = clone $this;
83+
if (\array_key_exists('auto_upgrade_http_version', $options)) {
84+
$clone->autoUpgradeHttpVersion = $options['auto_upgrade_http_version'];
85+
unset($options['auto_upgrade_http_version']);
86+
}
8287
$clone->client = $clone->client->withOptions($options);
8388

8489
return $clone;
@@ -128,8 +133,8 @@ public function sendRequest(RequestInterface $request): ResponseInterface
128133
'body' => $body,
129134
];
130135

131-
if ('1.0' === $request->getProtocolVersion()) {
132-
$options['http_version'] = '1.0';
136+
if (!$this->autoUpgradeHttpVersion || '1.0' === $request->getProtocolVersion()) {
137+
$options['http_version'] = $request->getProtocolVersion();
133138
}
134139

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

src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,4 +299,35 @@ public function testResponseReasonPhrase()
299299
$resultResponse = $client->sendRequest($request);
300300
$this->assertSame('Very Early Hints', $resultResponse->getReasonPhrase());
301301
}
302+
303+
public function testAutoUpgradeHttpVersion()
304+
{
305+
$clientWithoutOption = new HttplugClient(new MockHttpClient(function (string $method, string $url, array $options) {
306+
return new MockResponse(json_encode([
307+
'SERVER_PROTOCOL' => 'HTTP/'.$options['http_version'] ?? '',
308+
]), [
309+
'response_headers' => [
310+
'Content-Type' => 'application/json',
311+
],
312+
]);
313+
}));
314+
$clientWithOptionFalse = $clientWithoutOption->withOptions(['auto_upgrade_http_version' => false]);
315+
316+
foreach (['1.0', '1.1', '2.0', '3.0'] as $httpVersion) {
317+
$request = $clientWithoutOption->createRequest('GET', 'http://localhost:8057')
318+
->withProtocolVersion($httpVersion);
319+
320+
$responseWithoutOption = $clientWithoutOption->sendRequest($request);
321+
$bodyWithoutOption = json_decode((string) $responseWithoutOption->getBody(), true);
322+
if ('1.0' === $httpVersion) {
323+
$this->assertSame('HTTP/1.0', $bodyWithoutOption['SERVER_PROTOCOL']);
324+
} else {
325+
$this->assertSame('HTTP/', $bodyWithoutOption['SERVER_PROTOCOL']);
326+
}
327+
328+
$responseWithOptionFalse = $clientWithOptionFalse->sendRequest($request);
329+
$bodyWithOptionFalse = json_decode((string) $responseWithOptionFalse->getBody(), true);
330+
$this->assertSame('HTTP/'.$httpVersion, $bodyWithOptionFalse['SERVER_PROTOCOL']);
331+
}
332+
}
302333
}

src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,35 @@ public function testResponseReasonPhrase()
118118
$resultResponse = $client->sendRequest($request);
119119
$this->assertSame('Very Early Hints', $resultResponse->getReasonPhrase());
120120
}
121+
122+
public function testAutoUpgradeHttpVersion()
123+
{
124+
$clientWithoutOption = new Psr18Client(new MockHttpClient(function (string $method, string $url, array $options) {
125+
return new MockResponse(json_encode([
126+
'SERVER_PROTOCOL' => 'HTTP/'.$options['http_version'] ?? '',
127+
]), [
128+
'response_headers' => [
129+
'Content-Type' => 'application/json',
130+
],
131+
]);
132+
}));
133+
$clientWithOptionFalse = $clientWithoutOption->withOptions(['auto_upgrade_http_version' => false]);
134+
135+
foreach (['1.0', '1.1', '2.0', '3.0'] as $httpVersion) {
136+
$request = $clientWithoutOption->createRequest('GET', 'http://localhost:8057')
137+
->withProtocolVersion($httpVersion);
138+
139+
$responseWithoutOption = $clientWithoutOption->sendRequest($request);
140+
$bodyWithoutOption = json_decode((string) $responseWithoutOption->getBody(), true);
141+
if ('1.0' === $httpVersion) {
142+
$this->assertSame('HTTP/1.0', $bodyWithoutOption['SERVER_PROTOCOL']);
143+
} else {
144+
$this->assertSame('HTTP/', $bodyWithoutOption['SERVER_PROTOCOL']);
145+
}
146+
147+
$responseWithOptionFalse = $clientWithOptionFalse->sendRequest($request);
148+
$bodyWithOptionFalse = json_decode((string) $responseWithOptionFalse->getBody(), true);
149+
$this->assertSame('HTTP/'.$httpVersion, $bodyWithOptionFalse['SERVER_PROTOCOL']);
150+
}
151+
}
121152
}

0 commit comments

Comments
 (0)