Browse Source

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
pull/46927/head
Phillip Webb 4 months ago
parent
commit
d307d57617
  1. 51
      module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/ClientHttpRequestFactories.java
  2. 50
      module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/reactive/ClientHttpConnectors.java
  3. 9
      module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/RestClientHttpServiceClientConfiguration.java
  4. 15
      module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/RestClientPropertiesHttpServiceGroupConfigurer.java
  5. 38
      module/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfigurationTests.java
  6. 9
      module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/WebClientHttpServiceClientConfiguration.java
  7. 15
      module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/WebClientPropertiesHttpServiceGroupConfigurer.java
  8. 43
      module/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfigurationTests.java

51
module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/ClientHttpRequestFactories.java

@ -46,37 +46,64 @@ public final class ClientHttpRequestFactories { @@ -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);
}
private @Nullable SslBundle fallbackSslBundle() {
return (this.fallbackSettings != null) ? this.fallbackSettings.sslBundle() : null;
}
@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 <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);
}
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 <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 { @@ -86,7 +113,7 @@ public final class ClientHttpRequestFactories {
}
}
}
return null;
return (fallback != null) ? fallbackAccessor.apply(fallback) : null;
}
}

50
module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/reactive/ClientHttpConnectors.java

@ -46,37 +46,63 @@ public final class ClientHttpConnectors { @@ -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);
}
private @Nullable SslBundle fallbackSslBundle() {
return (this.fallbackSettings != null) ? this.fallbackSettings.sslBundle() : null;
}
@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 <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);
}
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 <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 { @@ -86,7 +112,7 @@ public final class ClientHttpConnectors {
}
}
}
return null;
return (fallback != null) ? fallbackAccessor.apply(fallback) : null;
}
}

9
module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/RestClientHttpServiceClientConfiguration.java

@ -21,7 +21,6 @@ import org.springframework.beans.factory.ObjectProvider; @@ -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 { @@ -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

15
module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/RestClientPropertiesHttpServiceGroupConfigurer.java

@ -46,8 +46,6 @@ class RestClientPropertiesHttpServiceGroupConfigurer implements RestClientHttpSe @@ -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 @@ -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 @@ -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
module/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfigurationTests.java

@ -25,6 +25,7 @@ import java.util.Map; @@ -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 @@ -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 { @@ -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")

9
module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/WebClientHttpServiceClientConfiguration.java

@ -19,7 +19,6 @@ package org.springframework.boot.webclient.autoconfigure.service; @@ -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 @@ -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

15
module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/WebClientPropertiesHttpServiceGroupConfigurer.java

@ -48,8 +48,6 @@ class WebClientPropertiesHttpServiceGroupConfigurer implements WebClientHttpServ @@ -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 @@ -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 @@ -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
module/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfigurationTests.java

@ -20,11 +20,13 @@ import java.lang.reflect.InvocationHandler; @@ -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 @@ -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 { @@ -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")

Loading…
Cancel
Save