diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpClientProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpClientProperties.java index ea0c41791ba..389fddc651d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpClientProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpClientProperties.java @@ -18,12 +18,8 @@ package org.springframework.boot.autoconfigure.http.client; import java.time.Duration; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.HttpRedirects; -import org.springframework.boot.ssl.SslBundle; -import org.springframework.boot.ssl.SslBundles; -import org.springframework.util.StringUtils; /** * Abstract base class for properties that directly or indirectly make use of a blocking @@ -38,7 +34,7 @@ public abstract class AbstractHttpClientProperties { /** * Handling for HTTP redirects. */ - private HttpRedirects redirects = HttpRedirects.FOLLOW_WHEN_POSSIBLE; + private HttpRedirects redirects; /** * Default connect timeout for a client HTTP request. @@ -83,20 +79,6 @@ public abstract class AbstractHttpClientProperties { return this.ssl; } - /** - * Return {@link HttpClientSettings} based on these properties. - * @param sslBundles a {@link SslBundles} provider - * @return the {@link HttpClientSettings} - */ - protected HttpClientSettings httpClientSettings(ObjectProvider sslBundles) { - return new HttpClientSettings(this.redirects, this.connectTimeout, this.readTimeout, sslBundle(sslBundles)); - } - - private SslBundle sslBundle(ObjectProvider sslBundles) { - String name = getSsl().getBundle(); - return (StringUtils.hasLength(name)) ? sslBundles.getObject().getBundle(name) : null; - } - /** * SSL configuration. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpRequestFactoryProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpRequestFactoryProperties.java index c00519acb70..88ad8e06e69 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpRequestFactoryProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/AbstractHttpRequestFactoryProperties.java @@ -46,16 +46,6 @@ public abstract class AbstractHttpRequestFactoryProperties extends AbstractHttpC this.factory = factory; } - /** - * Return a {@link ClientHttpRequestFactoryBuilder} based on the properties. - * @param classLoader the class loader to use for detection - * @return a {@link ClientHttpRequestFactoryBuilder} - */ - protected final ClientHttpRequestFactoryBuilder factoryBuilder(ClassLoader classLoader) { - Factory factory = getFactory(); - return (factory != null) ? factory.builder() : ClientHttpRequestFactoryBuilder.detect(classLoader); - } - /** * Supported factory types. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/ClientHttpRequestFactories.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/ClientHttpRequestFactories.java new file mode 100644 index 00000000000..340f6ff1311 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/ClientHttpRequestFactories.java @@ -0,0 +1,85 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.http.client; + +import java.time.Duration; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.http.client.AbstractHttpClientProperties.Ssl; +import org.springframework.boot.autoconfigure.http.client.AbstractHttpRequestFactoryProperties.Factory; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects; +import org.springframework.boot.http.client.HttpRedirects; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundles; +import org.springframework.util.StringUtils; + +/** + * Helper class to create {@link ClientHttpRequestFactoryBuilder} and + * {@link ClientHttpRequestFactorySettings}. + * + * @author Phillip Webb + */ +class ClientHttpRequestFactories { + + private final ObjectProvider sslBundles; + + private final AbstractHttpRequestFactoryProperties[] orderedProperties; + + ClientHttpRequestFactories(ObjectProvider sslBundles, + AbstractHttpRequestFactoryProperties... orderedProperties) { + this.sslBundles = sslBundles; + this.orderedProperties = orderedProperties; + } + + ClientHttpRequestFactoryBuilder builder(ClassLoader classLoader) { + Factory factory = getProperty(AbstractHttpRequestFactoryProperties::getFactory); + return (factory != null) ? factory.builder() : ClientHttpRequestFactoryBuilder.detect(classLoader); + } + + ClientHttpRequestFactorySettings settings() { + HttpRedirects redirects = getProperty(AbstractHttpRequestFactoryProperties::getRedirects); + Duration connectTimeout = getProperty(AbstractHttpRequestFactoryProperties::getConnectTimeout); + Duration readTimeout = getProperty(AbstractHttpRequestFactoryProperties::getReadTimeout); + String sslBundleName = getProperty(AbstractHttpRequestFactoryProperties::getSsl, Ssl::getBundle, + StringUtils::hasLength); + SslBundle sslBundle = (StringUtils.hasLength(sslBundleName)) + ? this.sslBundles.getObject().getBundle(sslBundleName) : null; + return new ClientHttpRequestFactorySettings(Redirects.of(redirects), connectTimeout, readTimeout, sslBundle); + } + + private T getProperty(Function accessor) { + return getProperty(accessor, Function.identity(), Objects::nonNull); + } + + private T getProperty(Function accessor, Function extractor, + Predicate predicate) { + for (AbstractHttpRequestFactoryProperties properties : this.orderedProperties) { + P value = accessor.apply(properties); + T extracted = (value != null) ? extractor.apply(value) : null; + if (predicate.test(extracted)) { + return extracted; + } + } + return null; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java index 2b0a55b74b0..e4c55c0bf7f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java @@ -28,9 +28,6 @@ import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; -import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects; -import org.springframework.boot.http.client.HttpClientSettings; -import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.util.LambdaSafe; import org.springframework.context.annotation.Bean; @@ -44,14 +41,22 @@ import org.springframework.http.client.ClientHttpRequestFactory; * @author Phillip Webb * @since 3.4.0 */ +@SuppressWarnings("removal") @AutoConfiguration(after = SslAutoConfiguration.class) @ConditionalOnClass(ClientHttpRequestFactory.class) @Conditional(NotReactiveWebApplicationCondition.class) -@EnableConfigurationProperties(HttpClientProperties.class) +@EnableConfigurationProperties({ HttpClientSettingsProperties.class, HttpClientProperties.class }) public class HttpClientAutoConfiguration implements BeanClassLoaderAware { + private final ClientHttpRequestFactories factories; + private ClassLoader beanClassLoader; + HttpClientAutoConfiguration(ObjectProvider sslBundles, HttpClientSettingsProperties properties, + HttpClientProperties deprecatedProperties) { + this.factories = new ClientHttpRequestFactories(sslBundles, properties, deprecatedProperties); + } + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; @@ -59,9 +64,9 @@ public class HttpClientAutoConfiguration implements BeanClassLoaderAware { @Bean @ConditionalOnMissingBean - ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties, + ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder( ObjectProvider> clientHttpRequestFactoryBuilderCustomizers) { - ClientHttpRequestFactoryBuilder builder = httpClientProperties.factoryBuilder(this.beanClassLoader); + ClientHttpRequestFactoryBuilder builder = this.factories.builder(this.beanClassLoader); return customize(builder, clientHttpRequestFactoryBuilderCustomizers.orderedStream().toList()); } @@ -76,19 +81,8 @@ public class HttpClientAutoConfiguration implements BeanClassLoaderAware { @Bean @ConditionalOnMissingBean - ClientHttpRequestFactorySettings clientHttpRequestFactorySettings(HttpClientProperties httpClientProperties, - ObjectProvider sslBundles) { - HttpClientSettings settings = httpClientProperties.httpClientSettings(sslBundles); - return new ClientHttpRequestFactorySettings(asRequestFactoryRedirects(settings.redirects()), - settings.connectTimeout(), settings.readTimeout(), settings.sslBundle()); - } - - private Redirects asRequestFactoryRedirects(HttpRedirects redirects) { - return switch (redirects) { - case FOLLOW_WHEN_POSSIBLE -> Redirects.FOLLOW_WHEN_POSSIBLE; - case FOLLOW -> Redirects.FOLLOW; - case DONT_FOLLOW -> Redirects.DONT_FOLLOW; - }; + ClientHttpRequestFactorySettings clientHttpRequestFactorySettings() { + return this.factories.settings(); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientProperties.java index 7542c9ba93b..a605330f998 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientProperties.java @@ -16,8 +16,12 @@ package org.springframework.boot.autoconfigure.http.client; +import java.time.Duration; + import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.http.client.HttpRedirects; /** * {@link ConfigurationProperties @ConfigurationProperties} for a Spring's blocking HTTP @@ -26,8 +30,41 @@ import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; * @author Phillip Webb * @since 3.4.0 * @see ClientHttpRequestFactorySettings + * @deprecated since 3.5.0 for removal in 4.0.0 in favor of + * {@link HttpClientSettingsProperties} */ @ConfigurationProperties("spring.http.client") +@Deprecated(since = "3.5.0", forRemoval = true) public class HttpClientProperties extends AbstractHttpRequestFactoryProperties { + @Override + @DeprecatedConfigurationProperty(since = "3.5.0", replacement = "spring.http.client.settings.factory") + public Factory getFactory() { + return super.getFactory(); + } + + @Override + @DeprecatedConfigurationProperty(since = "3.5.0", replacement = "spring.http.client.settings.redirects") + public HttpRedirects getRedirects() { + return super.getRedirects(); + } + + @Override + @DeprecatedConfigurationProperty(since = "3.5.0", replacement = "spring.http.client.settings.connect-timeout") + public Duration getConnectTimeout() { + return super.getConnectTimeout(); + } + + @Override + @DeprecatedConfigurationProperty(since = "3.5.0", replacement = "spring.http.client.settings.read-timeout") + public Duration getReadTimeout() { + return super.getReadTimeout(); + } + + @Override + @DeprecatedConfigurationProperty(since = "3.5.0", replacement = "spring.http.client.settings.ssl") + public Ssl getSsl() { + return super.getSsl(); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientSettingsProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientSettingsProperties.java new file mode 100644 index 00000000000..95832e11ee8 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientSettingsProperties.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.http.client; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings; + +/** + * {@link ConfigurationProperties @ConfigurationProperties} to configure settings that + * apply to Spring's reactive client HTTP connectors. + * + * @author Phillip Webb + * @since 3.5.0 + * @see ClientHttpConnectorSettings + */ +@ConfigurationProperties("spring.http.client.settings") +public class HttpClientSettingsProperties extends AbstractHttpRequestFactoryProperties { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/AbstractClientHttpConnectorProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/AbstractClientHttpConnectorProperties.java index c28815f0636..0bfb5b5ebc5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/AbstractClientHttpConnectorProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/AbstractClientHttpConnectorProperties.java @@ -18,13 +18,10 @@ package org.springframework.boot.autoconfigure.http.client.reactive; import java.util.function.Supplier; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.http.client.AbstractHttpClientProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder; import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings; -import org.springframework.boot.ssl.SslBundles; import org.springframework.http.client.reactive.ClientHttpConnector; /** @@ -50,21 +47,6 @@ public abstract class AbstractClientHttpConnectorProperties extends AbstractHttp this.connector = connector; } - @Override - protected HttpClientSettings httpClientSettings(ObjectProvider sslBundles) { - return super.httpClientSettings(sslBundles); - } - - /** - * Return a {@link ClientHttpConnectorBuilder} based on the properties. - * @param classLoader the class loader to use for detection - * @return a {@link ClientHttpConnectorBuilder} - */ - protected final ClientHttpConnectorBuilder connectorBuilder(ClassLoader classLoader) { - Connector connector = getConnector(); - return (connector != null) ? connector.builder() : ClientHttpConnectorBuilder.detect(classLoader); - } - /** * Supported factory types. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectorAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectorAutoConfiguration.java index ce797e03ec1..e416beed8ee 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectorAutoConfiguration.java @@ -29,7 +29,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.reactor.netty.ReactorNettyConfigurations; import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder; import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings; import org.springframework.boot.ssl.SslBundles; @@ -52,8 +51,15 @@ import org.springframework.http.client.reactive.ClientHttpConnector; @EnableConfigurationProperties(HttpReactiveClientSettingsProperties.class) public class ClientHttpConnectorAutoConfiguration implements BeanClassLoaderAware { + private final ClientHttpConnectors connectors; + private ClassLoader beanClassLoader; + ClientHttpConnectorAutoConfiguration(ObjectProvider sslBundles, + HttpReactiveClientSettingsProperties properties) { + this.connectors = new ClientHttpConnectors(sslBundles, properties); + } + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; @@ -62,10 +68,8 @@ public class ClientHttpConnectorAutoConfiguration implements BeanClassLoaderAwar @Bean @ConditionalOnMissingBean ClientHttpConnectorBuilder clientHttpConnectorBuilder( - HttpReactiveClientSettingsProperties httpReactiveClientSettingsProperties, ObjectProvider> clientHttpConnectorBuilderCustomizers) { - ClientHttpConnectorBuilder builder = httpReactiveClientSettingsProperties - .connectorBuilder(this.beanClassLoader); + ClientHttpConnectorBuilder builder = this.connectors.builder(this.beanClassLoader); return customize(builder, clientHttpConnectorBuilderCustomizers.orderedStream().toList()); } @@ -80,12 +84,8 @@ public class ClientHttpConnectorAutoConfiguration implements BeanClassLoaderAwar @Bean @ConditionalOnMissingBean - ClientHttpConnectorSettings clientHttpConnectorSettings( - HttpReactiveClientSettingsProperties httpReactiveClientSettingsProperties, - ObjectProvider sslBundles) { - HttpClientSettings settings = httpReactiveClientSettingsProperties.httpClientSettings(sslBundles); - return new ClientHttpConnectorSettings(settings.redirects(), settings.connectTimeout(), settings.readTimeout(), - settings.sslBundle()); + ClientHttpConnectorSettings clientHttpConnectorSettings() { + return this.connectors.settings(); } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectors.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectors.java new file mode 100644 index 00000000000..6eaeffe9ab1 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectors.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.http.client.reactive; + +import java.time.Duration; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.http.client.AbstractHttpClientProperties.Ssl; +import org.springframework.boot.autoconfigure.http.client.reactive.AbstractClientHttpConnectorProperties.Connector; +import org.springframework.boot.http.client.HttpRedirects; +import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder; +import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundles; +import org.springframework.util.StringUtils; + +/** + * Helper class to create {@link ClientHttpConnectorBuilder} and + * {@link ClientHttpConnectorSettings}. + * + * @author Phillip Webb + */ +class ClientHttpConnectors { + + private final ObjectProvider sslBundles; + + private final AbstractClientHttpConnectorProperties[] orderedProperties; + + ClientHttpConnectors(ObjectProvider sslBundles, + AbstractClientHttpConnectorProperties... orderedProperties) { + this.sslBundles = sslBundles; + this.orderedProperties = orderedProperties; + } + + ClientHttpConnectorBuilder builder(ClassLoader classLoader) { + Connector connector = getProperty(AbstractClientHttpConnectorProperties::getConnector); + return (connector != null) ? connector.builder() : ClientHttpConnectorBuilder.detect(classLoader); + } + + ClientHttpConnectorSettings settings() { + HttpRedirects redirects = getProperty(AbstractClientHttpConnectorProperties::getRedirects); + Duration connectTimeout = getProperty(AbstractClientHttpConnectorProperties::getConnectTimeout); + Duration readTimeout = getProperty(AbstractClientHttpConnectorProperties::getReadTimeout); + String sslBundleName = getProperty(AbstractClientHttpConnectorProperties::getSsl, Ssl::getBundle, + StringUtils::hasText); + SslBundle sslBundle = (StringUtils.hasLength(sslBundleName)) + ? this.sslBundles.getObject().getBundle(sslBundleName) : null; + return new ClientHttpConnectorSettings(redirects, connectTimeout, readTimeout, sslBundle); + } + + private T getProperty(Function accessor) { + return getProperty(accessor, Function.identity(), Objects::nonNull); + } + + private T getProperty(Function accessor, Function extractor, + Predicate predicate) { + for (AbstractClientHttpConnectorProperties properties : this.orderedProperties) { + P value = accessor.apply(properties); + T extracted = (value != null) ? extractor.apply(value) : null; + if (predicate.test(extracted)) { + return extracted; + } + } + return null; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java index a90d31949e1..1c4d1210826 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java @@ -68,6 +68,21 @@ class HttpClientAutoConfigurationTests { @Test void configuresClientHttpRequestFactorySettings() { + this.contextRunner.withPropertyValues(sslPropertyValues().toArray(String[]::new)) + .withPropertyValues("spring.http.client.settings.redirects=dont-follow", + "spring.http.client.settings.connect-timeout=10s", "spring.http.client.settings.read-timeout=20s", + "spring.http.client.settings.ssl.bundle=test") + .run((context) -> { + ClientHttpRequestFactorySettings settings = context.getBean(ClientHttpRequestFactorySettings.class); + assertThat(settings.redirects()).isEqualTo(Redirects.DONT_FOLLOW); + assertThat(settings.connectTimeout()).isEqualTo(Duration.ofSeconds(10)); + assertThat(settings.readTimeout()).isEqualTo(Duration.ofSeconds(20)); + assertThat(settings.sslBundle().getKey().getAlias()).isEqualTo("alias1"); + }); + } + + @Test + void configuresClientHttpRequestFactorySettingsUsingDeprecatedProperties() { this.contextRunner.withPropertyValues(sslPropertyValues().toArray(String[]::new)) .withPropertyValues("spring.http.client.redirects=dont-follow", "spring.http.client.connect-timeout=10s", "spring.http.client.read-timeout=20s", "spring.http.client.ssl.bundle=test") diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc index f9264f33433..b20cb7f37d8 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc @@ -248,7 +248,7 @@ If multiple clients are available on the classpath, and not global configuration [[io.rest-client.clienthttprequestfactory.configuration]] === Global HTTP Client Configuration -If the auto-detected HTTP client does not meet your needs, you can use the configprop:spring.http.client.factory[] property to pick a specific factory. +If the auto-detected HTTP client does not meet your needs, you can use the configprop:spring.http.client.settings.factory[] property to pick a specific factory. For example, if you have Apache HttpClient on your classpath, but you prefer Jetty's javadoc:org.eclipse.jetty.client.HttpClient[] you can add the following: [configprops,yaml] @@ -256,7 +256,8 @@ For example, if you have Apache HttpClient on your classpath, but you prefer Jet spring: http: client: - factory: jetty + settings: + factory: jetty ---- You can also set properties to change defaults that will be applied to all clients. @@ -267,9 +268,10 @@ For example, you may want to change timeouts and if redirects are followed: spring: http: client: - connect-timeout: 2s - read-timeout: 1s - redirects: dont-follow + settings: + connect-timeout: 2s + read-timeout: 1s + redirects: dont-follow ---- For more complex customizations, you can use javadoc:org.springframework.boot.autoconfigure.http.client.ClientHttpRequestFactoryBuilderCustomizer[] or declare your own javadoc:org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder[] bean which will cause auto-configuration to back off. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ClientHttpRequestFactorySettings.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ClientHttpRequestFactorySettings.java index a7a38b74269..50d8816c3e2 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ClientHttpRequestFactorySettings.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ClientHttpRequestFactorySettings.java @@ -145,6 +145,20 @@ public record ClientHttpRequestFactorySettings(Redirects redirects, Duration con return this.httpClientRedirects; } + /** + * Return the related {@link Redirects} for the given {@link HttpRedirects}. + * @param httpRedirects the HTTP redirects + * @return the related redirects + * @since 3.5.0 + */ + public static Redirects of(HttpRedirects httpRedirects) { + return (httpRedirects != null) ? switch (httpRedirects) { + case FOLLOW_WHEN_POSSIBLE -> FOLLOW_WHEN_POSSIBLE; + case FOLLOW -> FOLLOW; + case DONT_FOLLOW -> DONT_FOLLOW; + } : null; + } + } }