mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-05-02 19:30:23 +01:00
Apply HTTP Service Client properties and use fallback beans
Update service client configuration so that properties are always applied when present. Any settings and factory/connector beans that are present are now only used as fallbacks. Fixes gh-46915
This commit is contained in:
+40
-13
@@ -46,37 +46,64 @@ public final class ClientHttpRequestFactories {
|
||||
|
||||
private final @Nullable AbstractHttpRequestFactoryProperties[] orderedProperties;
|
||||
|
||||
private final @Nullable ClientHttpRequestFactoryBuilder<?> fallbackBuilder;
|
||||
|
||||
private final @Nullable ClientHttpRequestFactorySettings fallbackSettings;
|
||||
|
||||
public ClientHttpRequestFactories(ObjectFactory<SslBundles> sslBundles,
|
||||
@Nullable AbstractHttpRequestFactoryProperties... orderedProperties) {
|
||||
this(null, null, sslBundles, orderedProperties);
|
||||
}
|
||||
|
||||
public ClientHttpRequestFactories(@Nullable ClientHttpRequestFactoryBuilder<?> fallbackBuilder,
|
||||
@Nullable ClientHttpRequestFactorySettings fallbackSettings, ObjectFactory<SslBundles> sslBundles,
|
||||
@Nullable AbstractHttpRequestFactoryProperties... orderedProperties) {
|
||||
this.fallbackBuilder = fallbackBuilder;
|
||||
this.fallbackSettings = fallbackSettings;
|
||||
this.sslBundles = sslBundles;
|
||||
this.orderedProperties = orderedProperties;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
public ClientHttpRequestFactoryBuilder<?> builder(@Nullable ClassLoader classLoader) {
|
||||
Factory factory = getProperty(AbstractHttpRequestFactoryProperties::getFactory);
|
||||
return (factory != null) ? factory.builder() : ClientHttpRequestFactoryBuilder.detect(classLoader);
|
||||
Factory factory = getProperty(AbstractHttpRequestFactoryProperties::getFactory, Objects::nonNull, null,
|
||||
Function.identity());
|
||||
if (factory != null) {
|
||||
return factory.builder();
|
||||
}
|
||||
return (this.fallbackBuilder != null) ? this.fallbackBuilder
|
||||
: ClientHttpRequestFactoryBuilder.detect(classLoader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
public ClientHttpRequestFactorySettings settings() {
|
||||
HttpRedirects redirects = getProperty(AbstractHttpRequestFactoryProperties::getRedirects);
|
||||
Duration connectTimeout = getProperty(AbstractHttpRequestFactoryProperties::getConnectTimeout);
|
||||
Duration readTimeout = getProperty(AbstractHttpRequestFactoryProperties::getReadTimeout);
|
||||
HttpRedirects redirects = getProperty(AbstractHttpRequestFactoryProperties::getRedirects, Objects::nonNull,
|
||||
this.fallbackSettings, ClientHttpRequestFactorySettings::redirects);
|
||||
Duration connectTimeout = getProperty(AbstractHttpRequestFactoryProperties::getConnectTimeout, Objects::nonNull,
|
||||
this.fallbackSettings, ClientHttpRequestFactorySettings::connectTimeout);
|
||||
Duration readTimeout = getProperty(AbstractHttpRequestFactoryProperties::getReadTimeout, Objects::nonNull,
|
||||
this.fallbackSettings, ClientHttpRequestFactorySettings::readTimeout);
|
||||
String sslBundleName = getProperty(AbstractHttpRequestFactoryProperties::getSsl, Ssl::getBundle,
|
||||
StringUtils::hasLength);
|
||||
StringUtils::hasLength, null, Function.identity());
|
||||
SslBundle sslBundle = (StringUtils.hasLength(sslBundleName))
|
||||
? this.sslBundles.getObject().getBundle(sslBundleName) : null;
|
||||
? this.sslBundles.getObject().getBundle(sslBundleName) : fallbackSslBundle();
|
||||
return new ClientHttpRequestFactorySettings(redirects, connectTimeout, readTimeout, sslBundle);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
private <T> @Nullable T getProperty(Function<AbstractHttpRequestFactoryProperties, @Nullable T> accessor) {
|
||||
return getProperty(accessor, Function.identity(), Objects::nonNull);
|
||||
private @Nullable SslBundle fallbackSslBundle() {
|
||||
return (this.fallbackSettings != null) ? this.fallbackSettings.sslBundle() : null;
|
||||
}
|
||||
|
||||
private <P, T> @Nullable T getProperty(Function<AbstractHttpRequestFactoryProperties, @Nullable P> accessor,
|
||||
Function<P, T> extractor, Predicate<@Nullable T> predicate) {
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
private <T, F> @Nullable T getProperty(Function<AbstractHttpRequestFactoryProperties, @Nullable T> accessor,
|
||||
Predicate<@Nullable T> predicate, @Nullable F fallback, Function<F, @Nullable T> fallbackAccessor) {
|
||||
return getProperty(accessor, Function.identity(), predicate, fallback, fallbackAccessor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
private <P, T, F> @Nullable T getProperty(Function<AbstractHttpRequestFactoryProperties, @Nullable P> accessor,
|
||||
Function<@Nullable P, @Nullable T> extractor, Predicate<@Nullable T> predicate, @Nullable F fallback,
|
||||
Function<F, @Nullable T> fallbackAccessor) {
|
||||
for (AbstractHttpRequestFactoryProperties properties : this.orderedProperties) {
|
||||
if (properties != null) {
|
||||
P value = accessor.apply(properties);
|
||||
@@ -86,7 +113,7 @@ public final class ClientHttpRequestFactories {
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return (fallback != null) ? fallbackAccessor.apply(fallback) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+39
-13
@@ -46,37 +46,63 @@ public final class ClientHttpConnectors {
|
||||
|
||||
private final @Nullable AbstractClientHttpConnectorProperties[] orderedProperties;
|
||||
|
||||
private final @Nullable ClientHttpConnectorBuilder<?> fallbackBuilder;
|
||||
|
||||
private final @Nullable ClientHttpConnectorSettings fallbackSettings;
|
||||
|
||||
public ClientHttpConnectors(ObjectFactory<SslBundles> sslBundles,
|
||||
@Nullable AbstractClientHttpConnectorProperties... orderedProperties) {
|
||||
this(null, null, sslBundles, orderedProperties);
|
||||
}
|
||||
|
||||
public ClientHttpConnectors(@Nullable ClientHttpConnectorBuilder<?> fallbackBuilder,
|
||||
@Nullable ClientHttpConnectorSettings fallbackSettings, ObjectFactory<SslBundles> sslBundles,
|
||||
@Nullable AbstractClientHttpConnectorProperties... orderedProperties) {
|
||||
this.fallbackBuilder = fallbackBuilder;
|
||||
this.fallbackSettings = fallbackSettings;
|
||||
this.sslBundles = sslBundles;
|
||||
this.orderedProperties = orderedProperties;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
public ClientHttpConnectorBuilder<?> builder(@Nullable ClassLoader classLoader) {
|
||||
Connector connector = getProperty(AbstractClientHttpConnectorProperties::getConnector);
|
||||
return (connector != null) ? connector.builder() : ClientHttpConnectorBuilder.detect(classLoader);
|
||||
Connector connector = getProperty(AbstractClientHttpConnectorProperties::getConnector, Objects::nonNull, null,
|
||||
Function.identity());
|
||||
if (connector != null) {
|
||||
return connector.builder();
|
||||
}
|
||||
return (this.fallbackBuilder != null) ? this.fallbackBuilder : ClientHttpConnectorBuilder.detect(classLoader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
public ClientHttpConnectorSettings settings() {
|
||||
HttpRedirects redirects = getProperty(AbstractClientHttpConnectorProperties::getRedirects);
|
||||
Duration connectTimeout = getProperty(AbstractClientHttpConnectorProperties::getConnectTimeout);
|
||||
Duration readTimeout = getProperty(AbstractClientHttpConnectorProperties::getReadTimeout);
|
||||
HttpRedirects redirects = getProperty(AbstractClientHttpConnectorProperties::getRedirects, Objects::nonNull,
|
||||
this.fallbackSettings, ClientHttpConnectorSettings::redirects);
|
||||
Duration connectTimeout = getProperty(AbstractClientHttpConnectorProperties::getConnectTimeout,
|
||||
Objects::nonNull, this.fallbackSettings, ClientHttpConnectorSettings::connectTimeout);
|
||||
Duration readTimeout = getProperty(AbstractClientHttpConnectorProperties::getReadTimeout, Objects::nonNull,
|
||||
this.fallbackSettings, ClientHttpConnectorSettings::readTimeout);
|
||||
String sslBundleName = getProperty(AbstractClientHttpConnectorProperties::getSsl, Ssl::getBundle,
|
||||
StringUtils::hasText);
|
||||
StringUtils::hasText, null, Function.identity());
|
||||
SslBundle sslBundle = (StringUtils.hasLength(sslBundleName))
|
||||
? this.sslBundles.getObject().getBundle(sslBundleName) : null;
|
||||
? this.sslBundles.getObject().getBundle(sslBundleName) : fallbackSslBundle();
|
||||
return new ClientHttpConnectorSettings(redirects, connectTimeout, readTimeout, sslBundle);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
private <T> @Nullable T getProperty(Function<AbstractClientHttpConnectorProperties, @Nullable T> accessor) {
|
||||
return getProperty(accessor, Function.identity(), Objects::nonNull);
|
||||
private @Nullable SslBundle fallbackSslBundle() {
|
||||
return (this.fallbackSettings != null) ? this.fallbackSettings.sslBundle() : null;
|
||||
}
|
||||
|
||||
private <P, T> @Nullable T getProperty(Function<AbstractClientHttpConnectorProperties, @Nullable P> accessor,
|
||||
Function<P, @Nullable T> extractor, Predicate<@Nullable T> predicate) {
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
private <T, F> @Nullable T getProperty(Function<AbstractClientHttpConnectorProperties, @Nullable T> accessor,
|
||||
Predicate<@Nullable T> predicate, @Nullable F fallback, Function<F, @Nullable T> fallbackAccessor) {
|
||||
return getProperty(accessor, Function.identity(), predicate, fallback, fallbackAccessor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability
|
||||
private <P, T, F> @Nullable T getProperty(Function<AbstractClientHttpConnectorProperties, @Nullable P> accessor,
|
||||
Function<@Nullable P, @Nullable T> extractor, Predicate<@Nullable T> predicate, @Nullable F fallback,
|
||||
Function<F, @Nullable T> fallbackAccessor) {
|
||||
for (AbstractClientHttpConnectorProperties properties : this.orderedProperties) {
|
||||
if (properties != null) {
|
||||
P value = accessor.apply(properties);
|
||||
@@ -86,7 +112,7 @@ public final class ClientHttpConnectors {
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return (fallback != null) ? fallbackAccessor.apply(fallback) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-6
@@ -21,7 +21,6 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
|
||||
import org.springframework.boot.http.client.autoconfigure.HttpClientProperties;
|
||||
import org.springframework.boot.restclient.RestClientCustomizer;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -53,15 +52,13 @@ class RestClientHttpServiceClientConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@Bean
|
||||
RestClientPropertiesHttpServiceGroupConfigurer restClientPropertiesHttpServiceGroupConfigurer(
|
||||
ObjectProvider<SslBundles> sslBundles, ObjectProvider<HttpClientProperties> httpClientProperties,
|
||||
HttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<SslBundles> sslBundles, HttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientFactoryBuilder,
|
||||
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
return new RestClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles,
|
||||
httpClientProperties.getIfAvailable(), serviceProperties, clientFactoryBuilder,
|
||||
clientHttpRequestFactorySettings, apiVersionInserter, apiVersionFormatter);
|
||||
return new RestClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles, serviceProperties,
|
||||
clientFactoryBuilder, clientHttpRequestFactorySettings, apiVersionInserter, apiVersionFormatter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+6
-9
@@ -46,8 +46,6 @@ class RestClientPropertiesHttpServiceGroupConfigurer implements RestClientHttpSe
|
||||
|
||||
private final ObjectProvider<SslBundles> sslBundles;
|
||||
|
||||
private final @Nullable HttpClientProperties clientProperties;
|
||||
|
||||
private final HttpClientServiceProperties serviceProperties;
|
||||
|
||||
private final ObjectProvider<ClientHttpRequestFactoryBuilder<?>> requestFactoryBuilder;
|
||||
@@ -59,14 +57,13 @@ class RestClientPropertiesHttpServiceGroupConfigurer implements RestClientHttpSe
|
||||
private final @Nullable ApiVersionFormatter apiVersionFormatter;
|
||||
|
||||
RestClientPropertiesHttpServiceGroupConfigurer(ClassLoader classLoader, ObjectProvider<SslBundles> sslBundles,
|
||||
@Nullable HttpClientProperties clientProperties, HttpClientServiceProperties serviceProperties,
|
||||
HttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> requestFactoryBuilder,
|
||||
ObjectProvider<ClientHttpRequestFactorySettings> requestFactorySettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
this.classLoader = classLoader;
|
||||
this.sslBundles = sslBundles;
|
||||
this.clientProperties = clientProperties;
|
||||
this.serviceProperties = serviceProperties;
|
||||
this.requestFactoryBuilder = requestFactoryBuilder;
|
||||
this.requestFactorySettings = requestFactorySettings;
|
||||
@@ -97,11 +94,11 @@ class RestClientPropertiesHttpServiceGroupConfigurer implements RestClientHttpSe
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactory getRequestFactory(HttpClientServiceProperties.@Nullable Group groupProperties) {
|
||||
ClientHttpRequestFactories factories = new ClientHttpRequestFactories(this.sslBundles, groupProperties,
|
||||
this.serviceProperties, this.clientProperties);
|
||||
ClientHttpRequestFactoryBuilder<?> builder = this.requestFactoryBuilder
|
||||
.getIfAvailable(() -> factories.builder(this.classLoader));
|
||||
ClientHttpRequestFactorySettings settings = this.requestFactorySettings.getIfAvailable(factories::settings);
|
||||
ClientHttpRequestFactories factories = new ClientHttpRequestFactories(
|
||||
this.requestFactoryBuilder.getIfAvailable(), this.requestFactorySettings.getIfAvailable(),
|
||||
this.sslBundles, groupProperties, this.serviceProperties);
|
||||
ClientHttpRequestFactoryBuilder<?> builder = factories.builder(this.classLoader);
|
||||
ClientHttpRequestFactorySettings settings = factories.settings();
|
||||
return builder.build(settings);
|
||||
}
|
||||
|
||||
|
||||
+38
@@ -25,6 +25,7 @@ import java.util.Map;
|
||||
|
||||
import org.assertj.core.extractor.Extractors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
|
||||
@@ -39,16 +40,22 @@ import org.springframework.boot.restclient.autoconfigure.service.scan.TestHttpSe
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.RestClient.Builder;
|
||||
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.service.annotation.GetExchange;
|
||||
import org.springframework.web.service.registry.HttpServiceGroup;
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer.ClientCallback;
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer.Groups;
|
||||
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
@@ -95,6 +102,37 @@ class HttpServiceClientAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // gh-46915
|
||||
void configuresClientFromPropertiesWhenHasHttpClientAutoConfiguration() {
|
||||
this.contextRunner.withConfiguration(AutoConfigurations.of(HttpClientAutoConfiguration.class))
|
||||
.withPropertyValues("spring.http.client.service.connect-timeout=10s",
|
||||
"spring.http.client.service.group.one.connect-timeout=5s")
|
||||
.withUserConfiguration(HttpClientConfiguration.class, MockRestServiceServerConfiguration.class)
|
||||
.run((context) -> {
|
||||
RestClientPropertiesHttpServiceGroupConfigurer configurer = context
|
||||
.getBean(RestClientPropertiesHttpServiceGroupConfigurer.class);
|
||||
Groups<RestClient.Builder> groups = mock();
|
||||
configurer.configureGroups(groups);
|
||||
ArgumentCaptor<ClientCallback<RestClient.Builder>> callbackCaptor = ArgumentCaptor.captor();
|
||||
then(groups).should().forEachClient(callbackCaptor.capture());
|
||||
ClientCallback<RestClient.Builder> callback = callbackCaptor.getValue();
|
||||
assertConnectTimeout(callback, "one", 5000);
|
||||
assertConnectTimeout(callback, "two", 10000);
|
||||
});
|
||||
}
|
||||
|
||||
private void assertConnectTimeout(ClientCallback<RestClient.Builder> callback, String name,
|
||||
long expectedReadTimeout) {
|
||||
HttpServiceGroup group = mock();
|
||||
given(group.name()).willReturn(name);
|
||||
RestClient.Builder builder = mock();
|
||||
callback.withClient(group, builder);
|
||||
ArgumentCaptor<ClientHttpRequestFactory> requestFactoryCaptor = ArgumentCaptor.captor();
|
||||
then(builder).should().requestFactory(requestFactoryCaptor.capture());
|
||||
ClientHttpRequestFactory client = requestFactoryCaptor.getValue();
|
||||
assertThat(client).extracting("connectTimeout").isEqualTo(expectedReadTimeout);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasUserDefinedRequestFactoryBuilder() {
|
||||
this.contextRunner.withPropertyValues("spring.http.client.service.base-url=https://example.com")
|
||||
|
||||
+3
-6
@@ -19,7 +19,6 @@ package org.springframework.boot.webclient.autoconfigure.service;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.http.client.autoconfigure.reactive.HttpReactiveClientProperties;
|
||||
import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder;
|
||||
import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
@@ -53,15 +52,13 @@ final class WebClientHttpServiceClientConfiguration implements BeanClassLoaderAw
|
||||
|
||||
@Bean
|
||||
WebClientPropertiesHttpServiceGroupConfigurer webClientPropertiesHttpServiceGroupConfigurer(
|
||||
ObjectProvider<SslBundles> sslBundles, HttpReactiveClientProperties httpReactiveClientProperties,
|
||||
ReactiveHttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<SslBundles> sslBundles, ReactiveHttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpConnectorBuilder<?>> clientConnectorBuilder,
|
||||
ObjectProvider<ClientHttpConnectorSettings> clientConnectorSettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
return new WebClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles,
|
||||
httpReactiveClientProperties, serviceProperties, clientConnectorBuilder, clientConnectorSettings,
|
||||
apiVersionInserter, apiVersionFormatter);
|
||||
return new WebClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles, serviceProperties,
|
||||
clientConnectorBuilder, clientConnectorSettings, apiVersionInserter, apiVersionFormatter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+6
-9
@@ -48,8 +48,6 @@ class WebClientPropertiesHttpServiceGroupConfigurer implements WebClientHttpServ
|
||||
|
||||
private final ObjectProvider<SslBundles> sslBundles;
|
||||
|
||||
private final HttpReactiveClientProperties clientProperties;
|
||||
|
||||
private final ReactiveHttpClientServiceProperties serviceProperties;
|
||||
|
||||
private final ObjectProvider<ClientHttpConnectorBuilder<?>> clientConnectorBuilder;
|
||||
@@ -61,14 +59,13 @@ class WebClientPropertiesHttpServiceGroupConfigurer implements WebClientHttpServ
|
||||
private final @Nullable ApiVersionFormatter apiVersionFormatter;
|
||||
|
||||
WebClientPropertiesHttpServiceGroupConfigurer(ClassLoader classLoader, ObjectProvider<SslBundles> sslBundles,
|
||||
HttpReactiveClientProperties clientProperties, ReactiveHttpClientServiceProperties serviceProperties,
|
||||
ReactiveHttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpConnectorBuilder<?>> clientConnectorBuilder,
|
||||
ObjectProvider<ClientHttpConnectorSettings> clientConnectorSettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
this.classLoader = classLoader;
|
||||
this.sslBundles = sslBundles;
|
||||
this.clientProperties = clientProperties;
|
||||
this.serviceProperties = serviceProperties;
|
||||
this.clientConnectorBuilder = clientConnectorBuilder;
|
||||
this.clientConnectorSettings = clientConnectorSettings;
|
||||
@@ -100,11 +97,11 @@ class WebClientPropertiesHttpServiceGroupConfigurer implements WebClientHttpServ
|
||||
|
||||
private ClientHttpConnector getClientConnector(
|
||||
ReactiveHttpClientServiceProperties.@Nullable Group groupProperties) {
|
||||
ClientHttpConnectors connectors = new ClientHttpConnectors(this.sslBundles, groupProperties,
|
||||
this.serviceProperties, this.clientProperties);
|
||||
ClientHttpConnectorBuilder<?> builder = this.clientConnectorBuilder
|
||||
.getIfAvailable(() -> connectors.builder(this.classLoader));
|
||||
ClientHttpConnectorSettings settings = this.clientConnectorSettings.getIfAvailable(connectors::settings);
|
||||
ClientHttpConnectors connectors = new ClientHttpConnectors(this.clientConnectorBuilder.getIfAvailable(),
|
||||
this.clientConnectorSettings.getIfAvailable(), this.sslBundles, groupProperties,
|
||||
this.serviceProperties);
|
||||
ClientHttpConnectorBuilder<?> builder = connectors.builder(this.classLoader);
|
||||
ClientHttpConnectorSettings settings = connectors.settings();
|
||||
return builder.build(settings);
|
||||
}
|
||||
|
||||
|
||||
+43
@@ -20,11 +20,13 @@ import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpClient.Redirect;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.extractor.Extractors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
|
||||
@@ -40,15 +42,23 @@ import org.springframework.boot.webclient.autoconfigure.service.scan.TestHttpSer
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.service.annotation.GetExchange;
|
||||
import org.springframework.web.service.registry.HttpServiceGroup;
|
||||
import org.springframework.web.service.registry.HttpServiceGroup.ClientType;
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer.ClientCallback;
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer.Groups;
|
||||
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveHttpServiceClientAutoConfiguration},
|
||||
@@ -87,6 +97,39 @@ class ReactiveHttpServiceClientAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // gh-46915
|
||||
void configuresClientFromPropertiesWhenHasHttpConnectorAutoConfiguration() {
|
||||
this.contextRunner.withConfiguration(AutoConfigurations.of(ClientHttpConnectorAutoConfiguration.class))
|
||||
.withPropertyValues("spring.http.reactiveclient.service.connect-timeout=10s",
|
||||
"spring.http.reactiveclient.service.group.one.connect-timeout=5s",
|
||||
"spring.http.reactiveclient.connector=jdk")
|
||||
.withUserConfiguration(HttpClientConfiguration.class)
|
||||
.run((context) -> {
|
||||
WebClientPropertiesHttpServiceGroupConfigurer configurer = context
|
||||
.getBean(WebClientPropertiesHttpServiceGroupConfigurer.class);
|
||||
Groups<WebClient.Builder> groups = mock();
|
||||
configurer.configureGroups(groups);
|
||||
ArgumentCaptor<ClientCallback<WebClient.Builder>> callbackCaptor = ArgumentCaptor.captor();
|
||||
then(groups).should().forEachClient(callbackCaptor.capture());
|
||||
ClientCallback<WebClient.Builder> callback = callbackCaptor.getValue();
|
||||
assertConnectTimeout(callback, "one", Duration.ofSeconds(5));
|
||||
assertConnectTimeout(callback, "two", Duration.ofSeconds(10));
|
||||
});
|
||||
}
|
||||
|
||||
private void assertConnectTimeout(ClientCallback<WebClient.Builder> callback, String name,
|
||||
Duration expectedReadTimeout) {
|
||||
HttpServiceGroup group = mock();
|
||||
given(group.name()).willReturn(name);
|
||||
WebClient.Builder builder = mock();
|
||||
callback.withClient(group, builder);
|
||||
ArgumentCaptor<ClientHttpConnector> connectorCaptor = ArgumentCaptor.captor();
|
||||
then(builder).should().clientConnector(connectorCaptor.capture());
|
||||
ClientHttpConnector client = connectorCaptor.getValue();
|
||||
HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(client, "httpClient");
|
||||
assertThat(httpClient.connectTimeout()).contains(expectedReadTimeout);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasUserDefinedHttpConnectorBuilder() {
|
||||
this.contextRunner.withPropertyValues("spring.http.reactiveclient.service.base-url=https://example.com")
|
||||
|
||||
Reference in New Issue
Block a user