10000 [FrameworkBundle] change the way http clients are configured by lever… · symfony/symfony@f1a26b9 · GitHub
[go: up one dir, main page]

Skip to content

Commit f1a26b9

Browse files
[FrameworkBundle] change the way http clients are configured by leveraging ScopingHttpClient
1 parent 755f411 commit f1a26b9

17 files changed

+306
-219
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 216 additions & 127 deletions
Large diffs are not rendered by default.

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
use Symfony\Component\Form\FormTypeGuesserInterface;
6262
use Symfony\Component\Form\FormTypeInterface;
6363
use Symfony\Component\HttpClient\HttpClient;
64-
use Symfony\Component\HttpClient\HttpClientTrait;
6564
use Symfony\Component\HttpClient\Psr18Client;
65+
use Symfony\Component\HttpClient\ScopingHttpClient;
6666
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
6767
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
6868
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
@@ -117,7 +117,6 @@
117117
use Symfony\Component\Yaml\Command\LintCommand as BaseYamlLintCommand;
118118
use Symfony\Component\Yaml\Yaml;
119119
use Symfony\Contracts\Cache\CacheInterface;
120-
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
121120
use Symfony\Contracts\HttpClient\HttpClientInterface;
122121
use Symfony\Contracts\Service\ResetInterface;
123122
use Symfony\Contracts\Service\ServiceSubscriberInterface;
@@ -1803,42 +1802,23 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
18031802

18041803
$loader->load('http_client.xml');
18051804

1806-
$merger = new class() {
1807-
use HttpClientTrait;
1808-
1809-
public function merge(array $options, array $defaultOptions)
1810-
{
1811-
try {
1812-
[, $mergedOptions] = $this->prepareRequest(null, null, $options, $defaultOptions);
1813-
1814-
foreach ($mergedOptions as $k => $v) {
1815-
if (!isset($options[$k]) && !isset($defaultOptions[$k])) {
1816-
// Remove options added by prepareRequest()
1817-
unset($mergedOptions[$k]);
1818-
}
1819-
}
1820-
1821-
return $mergedOptions;
1822-
} catch (TransportExceptionInterface $e) {
1823-
throw new InvalidArgumentException($e->getMessage(), 0, $e);
1824-
}
1825-
}
1826-
};
1827-
1828-
$defaultOptions = $merger->merge($config['default_options'] ?? [], []);
1829-
$container->getDefinition('http_client')->setArguments([$defaultOptions, $config['max_host_connections'] ?? 6]);
1805+
$container->getDefinition('http_client')->setArguments([$config['default_options'] ?? [], $config['max_host_connections'] ?? 6]);
18301806

18311807
if (!$hasPsr18 = interface_exists(ClientInterface::class)) {
18321808
$container->removeDefinition('psr18.http_client');
18331809
$container->removeAlias(ClientInterface::class);
18341810
}
18351811

1836-
foreach ($config['clients'] as $name => $clientConfig) {
1837-
$options = $merger->merge($clientConfig['default_options'] ?? [], $defaultOptions);
1812+
foreach ($config['scoped_clients'] as $name => $scopeConfig) {
1813+
if ('http_client' === $name) {
1814+
throw new InvalidArgumentException(sprintf('Invalid scope name: "%s" is reserved.', $name));
1815+
}
1816+
1817+
$scope = $scopeConfig['scope'];
1818+
unset($scopeConfig['scope']);
18381819

1839-
$container->register($name, HttpClientInterface::class)
1840-
->setFactory([HttpClient::class, 'create'])
1841-
->setArguments([$options, $clientConfig['max_host_connections'] ?? $config['max_host_connections'] ?? 6]);
1820+
$container->register($name, ScopingHttpClient::class)
1821+
->setArguments([new Reference('http_client'), [$scope => $scopeConfig], $scope]);
18421822

18431823
$container->registerAliasForArgument($name, HttpClientInterface::class);
18441824

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -458,30 +458,55 @@
458458

459459
<xsd:complexType name="http_client">
460460
<xsd:sequence>
461-
<xsd:element name="default-options" type="http_client_options" minOccurs="0" />
462-
<xsd:element name="client" type="http_client_client" minOccurs="0" maxOccurs="unbounded" />
461+
<xsd:element name="default-options" type="http_client_default_options" minOccurs="0" />
462+
<xsd:element name="scoped-client" type="http_client_scope_options" minOccurs="0" maxOccurs="unbounded" />
463463
</xsd:sequence>
464464
<xsd:attribute name="enabled" type="xsd:boolean" />
465465
<xsd:attribute name="max-host-connections" type="xsd:integer" />
466466
</xsd:complexType>
467467

468-
<xsd:complexType name="http_client_options" mixed="true">
468+
<xsd:complexType name="http_client_default_options" mixed="true">
469469
<xsd:choice maxOccurs="unbounded">
470-
<xsd:element name="query" type="http_query" minOccurs="0" maxOccurs="unbounded" />
471470
<xsd:element name="resolve" type="http_resolve" minOccurs="0" maxOccurs="unbounded" />
472471
<xsd:element name="header" type="http_header" minOccurs="0" maxOccurs="unbounded" />
473472
<xsd:element name="peer-fingerprint" type="fingerprint" minOccurs="0" maxOccurs="unbounded" />
474473
</xsd:choice>
474+
<xsd:attribute name="max-redirects" type="xsd:integer" />
475+
<xsd:attribute name="http-version" type="xsd:string" />
475476
<xsd:attribute name="proxy" type="xsd:string" />
477+
<xsd:attribute name="no-proxy" type="xsd:string" />
476478
<xsd:attribute name="timeout" type="xsd:float" />
477479
<xsd:attribute name="bindto" type="xsd:string" />
478480
<xsd:attribute name="verify-peer" type="xsd:boolean" />
481+
<xsd:attribute name="verify-host" type="xsd:boolean" />
482+
<xsd:attribute name="cafile" type="xsd:string" />
483+
<xsd:attribute name="capath" type="xsd:string" />
484+
<xsd:attribute name="local-cert" type="xsd:string" />
485+
<xsd:attribute name="local-pk" type="xsd:string" />
486+
<xsd:attribute name="passphrase" type="xsd:string" />
487+
<xsd:attribute name="ciphers" type="xsd:string" />
488+
489+
</xsd:complexType>
490+
491+
<xsd:complexType name="http_client_scope_options" mixed="true">
492+
<xsd:choice maxOccurs="unbounded">
493+
<xsd:element name="query" type="http_query" minOccurs="0" maxOccurs="unbounded" />
494+
<xsd:element name="resolve" type="http_resolve" minOccurs="0" maxOccurs="unbounded" />
495+
<xsd:element name="header" type="http_header" minOccurs="0" maxOccurs="unbounded" />
496+
<xsd:element name="peer-fingerprint" type="fingerprint" minOccurs="0" maxOccurs="unbounded" />
497+
</xsd:choice>
498+
<xsd:attribute name="name" type="xsd:string" />
499+
<xsd:attribute name="scope" type="xsd:string" />
500+
<xsd:attribute name="base-uri" type="xsd:string" />
479501
<xsd:attribute name="auth-basic" type="xsd:string" />
480502
<xsd:attribute name="auth-bearer" type="xsd:string" />
481503
<xsd:attribute name="max-redirects" type="xsd:integer" />
482504
<xsd:attribute name="http-version" type="xsd:string" />
483-
<xsd:attribute name="base-uri" type="xsd:string" />
505+
<xsd:attribute name="proxy" type="xsd:string" />
484506
<xsd:attribute name="no-proxy" type="xsd:string" />
507+
<xsd:attribute name="timeout" type="xsd:float" />
508+
<xsd:attribute name="bindto" type="xsd:string" />
509+
<xsd:attribute name="verify-peer" type="xsd:boolean" />
485510
<xsd:attribute name="verify-host" type="xsd:boolean" />
486511
<xsd:attribute name="cafile" type="xsd:string" />
487512
<xsd:attribute name="capath" type="xsd:string" />
@@ -491,14 +516,6 @@
491516
<xsd:attribute name="ciphers" type="xsd:string" />
492517
</xsd:complexType>
493518

494-
<xsd:complexType name="http_client_client">
495-
<xsd:sequence>
496-
<xsd:element name="default-options" type="http_client_options" minOccurs="0" />
497-
</xsd:sequence>
498-
<xsd:attribute name="name" type="xsd:string" />
499-
<xsd:attribute name="max-host-connections" type="xsd:integer" />
500-
</xsd:complexType>
501-
502519
<xsd:complexType name="fingerprint">
503520
<xsd:choice maxOccurs="unbounded">
504521
<xsd:element name="pin-sha256" type="xsd:string" minOccurs="0" />

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
336336
'disallow_search_engine_index' => true,
337337
'http_client' => [
338338
'enabled' => !class_exists(FullStack::class) && class_exists(HttpClient::class),
339-
'clients' => [],
339+
'scoped_clients' => [],
340340
],
341341
'mailer' => [
342342
'dsn' => 'smtp://null',

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_default_options.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
'http_client' => [
55
'max_host_connections' => 4,
66
'default_options' => null,
7-
'clients' => [
7+
'scoped_clients' => [
88
'foo' => [
9-
'default_options' => null,
9+
'base_uri' => 'http://example.com',
1010
],
1111
],
1212
],

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_full_default_options.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
$container->loadFromExtension('framework', [
44
'http_client' => [
55
'default_options' => [
6-
'auth_basic' => 'foo:bar',
7-
'query' => ['foo' => 'bar', 'bar' => 'baz'],
86
'headers' => ['X-powered' => 'PHP'],
97
'max_redirects' => 2,
108
'http_version' => '2.0',
11-
'base_uri' => 'http://example.com',
129
'resolve' => ['localhost' => '127.0.0.1'],
1310
'proxy' => 'proxy.org',
1411
'timeout' => 3.5,

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_override_default_options.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
'default_options' => [
77
'headers' => ['foo' => 'bar'],
88
],
9-
'clients' => [
9+
'scoped_clients' => [
1010
'foo' => [
11-
'max_host_connections' => 5,
12-
'default_options' => [
13-
'headers' => ['bar' => 'baz'],
14-
],
11+
'base_uri' => 'http://example.com',
12+
'headers' => ['bar' => 'baz'],
1513
],
1614
],
1715
],

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_default_options.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
<framework:config>
99
<framework:http-client max-host-connections="4">
1010
<framework:default-options />
11-
<framework:client name="foo">
12-
<framework:default-options />
13-
</framework:client>
11+
<framework:scoped-client
12+
name="foo"
13+
base-uri="http://example.com"
14+
/>
1415
</framework:http-client>
1516
</framework:config>
1617
</container>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_full_default_options.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212
bindto="127.0.0.1"
1313
timeout="3.5"
1414
verify-peer="true"
15-
auth-basic="foo:bar"
1615
max-redirects="2"
1716
http-version="2.0"
18-
base-uri="http://example.com"
1917
verify-host="true"
2018
cafile="/etc/ssl/cafile"
2119
capath="/etc/ssl"
@@ -24,8 +22,6 @@
2422
passphrase="password123456"
2523
ciphers="RC4-SHA:TLS13-AES-128-GCM-SHA256"
2624
>
27-
<framework:query key="foo">bar</framework:query>
28-
<framework:query key="bar">baz</framework:query>
2925
<framework:header name="X-powered">PHP</framework:header>
3026
<framework:resolve host="localhost">127.0.0.1</framework:resolve>
3127
<framework:peer-fingerprint>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_override_default_options.xml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
<framework:default-options>
1111
<framework:header name="foo">bar</framework:header>
1212
</framework:default-options>
13-
<framework:client name="foo" max-host-connections="5">
14-
<framework:default-options>
15-
<framework:header name="bar">baz</framework:header>
16-
</framework:default-options>
17-
</framework:client>
13+
<framework:scoped-client name="foo" base-uri="http://example.com">
14+
<framework:header name="bar">baz</framework:header>
15+
</framework:scoped-client>
1816
</framework:http-client>
1917
</framework:config>
2018
</container>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_default_options.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ framework:
22
http_client:
33
max_host_connections: 4
44
default_options: ~
5-
clients:
5+
scoped_clients:
66
foo:
7-
default_options: ~
7+
base_uri: http://example.com

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_full_default_options.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
framework:
22
http_client:
33
default_options:
4-
auth_basic: foo:bar
5-
query: {'foo': 'bar', 'bar': 'baz'}
64
headers:
75
X-powered: PHP
86
max_redirects: 2
97
http_version: 2.0
10-
base_uri: 'http://example.com'
118
resolve: {'localhost': '127.0.0.1'}
129
proxy: proxy.org
1310
timeout: 3.5

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_override_default_options.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ framework:
33
max_host_connections: 4
44
default_options:
55
headers: {'foo': 'bar'}
6-
clients:
6+
scoped_clients:
77
foo:
8-
max_host_connections: 5
9-
default_options:
10-
headers: {'bar': 'baz'}
8+
base_uri: http://example.com
9+
headers: {'bar': 'baz'}

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
3636
use Symfony\Component\DependencyInjection\Reference;
3737
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
38+
use Symfony\Component\HttpClient\ScopingHttpClient;
3839
use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass;
3940
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
4041
use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage;
@@ -53,7 +54,6 @@
5354
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
5455
use Symfony\Component\Validator\Validation;
5556
use Symfony\Component\Workflow;
56-
use Symfony\Contracts\HttpClient\HttpClientInterface;
5757

5858
abstract class FrameworkExtensionTest extends TestCase
5959
{
@@ -1406,25 +1406,34 @@ public function testHttpClientDefaultOptions()
14061406
$this->assertTrue($container->hasDefinition('http_client'), '->registerHttpClientConfiguration() loads http_client.xml');
14071407

14081408
$defaultOptions = [
1409-
'query' => [],
14101409
'headers' => [],
14111410
'resolve' => [],
14121411
];
14131412
$this->assertSame([$defaultOptions, 4], $container->getDefinition('http_client')->getArguments());
14141413

14151414
$this->assertTrue($container->hasDefinition('foo'), 'should have the "foo" service.');
1416-
$this->assertSame(HttpClientInterface::class, $container->getDefinition('foo')->getClass());
1417-
$this->assertSame([$defaultOptions, 4], $container->getDefinition('foo')->getArguments());
1415+
$this->assertSame(ScopingHttpClient::class, $container->getDefinition('foo')->getClass());
14181416
}
14191417

14201418
public function testHttpClientOverrideDefaultOptions()
14211419
{
14221420
$container = $this->createContainerFromFile('http_client_override_default_options');
14231421

1424-
$this->assertSame(['foo' => ['bar']], $container->getDefinition('http_client')->getArgument(0)['headers']);
1422+
$this->assertSame(['foo' => 'bar'], $container->getDefinition('http_client')->getArgument(0)['headers']);
14251423
$this->assertSame(4, $container->getDefinition('http_client')->getArgument(1));
1426-
$this->assertSame(['bar' => ['baz'], 'foo' => ['bar']], $container->getDefinition('foo')->getArgument(0)['headers']);
1427-
$this->assertSame(5, $container->getDefinition('foo')->getArgument(1));
1424+
1425+
$expected = [
1426+
'http\://example\.com/' => [
1427+
'base_uri' => 'http://example.com',
1428+
'headers' => [
1429+
'bar' => 'baz',
1430+
],
1431+
'query' => [],
1432+
'resolve' => [],
1433+
],
1434+
];
1435+
1436+
$this->assertSame($expected, $container->getDefinition('foo')->getArgument(1));
14281437
}
14291438

14301439
public function testHttpClientFullDefaultOptions()
@@ -1433,12 +1442,9 @@ public function testHttpClientFullDefaultOptions()
14331442

14341443
$defaultOptions = $container->getDefinition('http_client')->getArgument(0);
14351444

1436-
$this->assertSame('foo:bar', $defaultOptions['auth_basic']);
1437-
$this->assertSame(['foo' => 'bar', 'bar' => 'baz'], $defaultOptions['query']);
1438-
$this->assertSame(['x-powered' => ['PHP']], $defaultOptions['headers']);
1445+
$this->assertSame(['X-powered' => 'PHP'], $defaultOptions['headers']);
14391446
$this->assertSame(2, $defaultOptions['max_redirects']);
14401447
$this->assertSame(2.0, (float) $defaultOptions['http_version']);
1441-
$this->assertSame('http://example.com', $defaultOptions['base_uri']);
14421448
$this->assertSame(['localhost' => '127.0.0.1'], $defaultOptions['resolve']);
14431449
$this->assertSame('proxy.org', $defaultOptions['proxy']);
14441450
$this->assertSame(3.5, $defaultOptions['timeout']);

src/Symfony/Component/HttpClient/Response/MockResponse.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ private static function readResponse(self $response, array $options, ResponseInt
240240
$info = $mock->getInfo() ?: [];
241241
$response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode(false) ?: 200;
242242
$response->addResponseHeaders($info['response_headers'] ?? [], $response->info, $response->headers);
243-
$dlSize = (int) ($response->headers['content-length'][0] ?? 0);
243+
$dlSize = isset($response->headers['content-encoding']) ? 0 : (int) ($response->headers['content-length'][0] ?? 0);
244244

245245
$response->info = [
246246
'start_time' => $response->info['start_time'],
@@ -282,7 +282,7 @@ private static function readResponse(self $response, array $options, ResponseInt
282282
// "notify" completion
283283
$onProgress($offset, $dlSize, $response->info);
284284

285-
if (isset($response->headers['content-length']) && $offset !== $dlSize) {
285+
if ($dlSize && $offset !== $dlSize) {
286286
throw new TransportException(sprintf('Transfer closed with %s bytes remaining to read.', $dlSize - $offset));
287287
}
288288
}

src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ public function set($key, $values, $replace = true)
122122
parent::set($key, $values, $replace);
123123

124124
// ensure the cache-control header has sensible defaults
125-
if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true)) {
126-
$computed = $this->computeCacheControlValue();
125+
if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true) && '' !== $computed = $this->computeCacheControlValue()) {
127126
$this->headers['cache-control'] = [$computed];
128127
$this->headerNames['cache-control'] = 'Cache-Control';
129128
$this->computedCacheControl = $this->parseCacheControl($computed);

0 commit comments

Comments
 (0)
0