diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index ad352160822ae..0430945be5fd1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -1979,13 +1979,23 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e
->ifTrue(fn ($v) => !empty($v['query']) && !isset($v['base_uri']))
->thenInvalid('"query" applies to "base_uri" but no base URI is defined.')
->end()
+ ->validate()
+ ->ifTrue(fn ($v) => (
+ (isset($v['base_uri']) && \is_array($v['base_uri']))
+ && (
+ (!isset($v['retry_failed']) || false === $v['retry_failed']['enabled'])
+ || \count($v['base_uri']) !== \count(array_filter($v['base_uri'], 'is_string'))
+ )
+ ))
+ ->thenInvalid('"base_uri" can only be an array if "retry_failed" is defined.')
+ ->end()
->children()
->scalarNode('scope')
->info('The regular expression that the request URL must match before adding the other options. When none is provided, the base URI is used instead.')
->cannotBeEmpty()
->end()
- ->scalarNode('base_uri')
- ->info('The URI to resolve relative URLs, following rules in RFC 3985, section 2.')
+ ->variableNode('base_uri')
+ ->info('The URI(s) to resolve relative URLs, following rules in RFC 3985, section 2.')
->cannotBeEmpty()
->end()
->scalarNode('auth_basic')
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 039707722dee3..d529efc72d447 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -2546,12 +2546,12 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
unset($scopeConfig['retry_failed']);
if (null === $scope) {
- $baseUri = $scopeConfig['base_uri'];
- unset($scopeConfig['base_uri']);
+ $baseUri = \is_array($scopeConfig['base_uri']) ? $scopeConfig['base_uri'][0] : $scopeConfig['base_uri'];
+ $config = array_filter($scopeConfig, fn ($k) => 'base_uri' !== $k, \ARRAY_FILTER_USE_KEY);
$container->register($name, ScopingHttpClient::class)
->setFactory([ScopingHttpClient::class, 'forBaseUri'])
- ->setArguments([new Reference('http_client.transport'), $baseUri, $scopeConfig])
+ ->setArguments([new Reference('http_client.transport'), $baseUri, $config])
->addTag('http_client.client')
;
} else {
@@ -2562,7 +2562,12 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
}
if ($this->readConfigEnabled('http_client.scoped_clients.'.$name.'.retry_failed', $container, $retryOptions)) {
- $this->registerRetryableHttpClient($retryOptions, $name, $container);
+ $baseUris = [];
+ if (isset($scopeConfig['base_uri']) && \is_array($scopeConfig['base_uri'])) {
+ $baseUris = $scopeConfig['base_uri'];
+ unset($scopeConfig['base_uri']);
+ }
+ $this->registerRetryableHttpClient($retryOptions, $name, $container, $baseUris);
}
$container
@@ -2598,7 +2603,7 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
}
}
- private function registerRetryableHttpClient(array $options, string $name, ContainerBuilder $container): void
+ private function registerRetryableHttpClient(array $options, string $name, ContainerBuilder $container, array $baseUris = []): void
{
if (null !== $options['retry_strategy']) {
$retryStrategy = new Reference($options['retry_strategy']);
@@ -2628,7 +2633,8 @@ private function registerRetryableHttpClient(array $options, string $name, Conta
->register($name.'.retryable', RetryableHttpClient::class)
->setDecoratedService($name, null, 10) // higher priority than TraceableHttpClient (5)
->setArguments([new Reference($name.'.retryable.inner'), $retryStrategy, $options['max_retries'], new Reference('logger')])
- ->addTag('monolog.logger', ['channel' => 'http_client']);
+ ->addTag('monolog.logger', ['channel' => 'http_client'])
+ ->addMethodCall('withOptions', [['base_uri' => $baseUris]], true);
}
private function registerMailerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $webhookEnabled): void
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index aedd4a86fd113..7750db1f84637 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -668,6 +668,7 @@
+
@@ -694,6 +695,7 @@
+
@@ -747,6 +749,10 @@
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_retry.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_retry.php
index 28205f8e4ed8f..5ee718e762ce8 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_retry.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_retry.php
@@ -22,6 +22,10 @@
'base_uri' => 'http://example.com',
'retry_failed' => ['multiplier' => 4],
],
+ 'bar' => [
+ 'base_uri' => ['http://a.example.com', 'http://b.example.com'],
+ 'retry_failed' => ['max_retries' => 4],
+ ],
],
],
]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_retry.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_retry.xml
index 296d1b29cd7a6..889cdc040f722 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_retry.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_retry.xml
@@ -26,6 +26,11 @@
+
+ http://a.example.com
+ http://a.example.com
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_retry.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_retry.yml
index ea59b82d98e7a..dd98cbebb7007 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_retry.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_retry.yml
@@ -21,3 +21,9 @@ framework:
base_uri: http://example.com
retry_failed:
multiplier: 4
+ bar:
+ base_uri:
+ - http://a.example.com
+ - http://b.example.com
+ retry_failed:
+ max_retries: 3
diff --git a/src/Symfony/Component/HttpClient/RetryableHttpClient.php b/src/Symfony/Component/HttpClient/RetryableHttpClient.php
index d3b779420ffa9..c577a2d3ea3a4 100644
--- a/src/Symfony/Component/HttpClient/RetryableHttpClient.php
+++ b/src/Symfony/Component/HttpClient/RetryableHttpClient.php
@@ -39,12 +39,13 @@ class RetryableHttpClient implements HttpClientInterface, ResetInterface
/**
* @param int $maxRetries The maximum number of times to retry
*/
- public function __construct(HttpClientInterface $client, ?RetryStrategyInterface $strategy = null, int $maxRetries = 3, ?LoggerInterface $logger = null)
+ public function __construct(HttpClientInterface $client, ?RetryStrategyInterface $strategy = null, int $maxRetries = 3, ?LoggerInterface $logger = null, array $baseUris = [])
{
$this->client = $client;
$this->strategy = $strategy ?? new GenericRetryStrategy();
$this->maxRetries = $maxRetries;
$this->logger = $logger;
+ $this->baseUris = $baseUris;
}
public function withOptions(array $options): static