Merge pull request #44979 from nosan
* pr/44979: Polish 'Update RestClientSsl to support ClientHttpRequestFactorySettings' Update RestClientSsl to support ClientHttpRequestFactorySettings Closes gh-44979
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* 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.
|
||||
@@ -29,16 +29,20 @@ import org.springframework.web.client.RestClient;
|
||||
* An auto-configured {@link RestClientSsl} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
class AutoConfiguredRestClientSsl implements RestClientSsl {
|
||||
|
||||
private final ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder;
|
||||
private final ClientHttpRequestFactoryBuilder<?> builder;
|
||||
|
||||
private final ClientHttpRequestFactorySettings settings;
|
||||
|
||||
private final SslBundles sslBundles;
|
||||
|
||||
AutoConfiguredRestClientSsl(ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder,
|
||||
SslBundles sslBundles) {
|
||||
this.clientHttpRequestFactoryBuilder = clientHttpRequestFactoryBuilder;
|
||||
ClientHttpRequestFactorySettings clientHttpRequestFactorySettings, SslBundles sslBundles) {
|
||||
this.builder = clientHttpRequestFactoryBuilder;
|
||||
this.settings = clientHttpRequestFactorySettings;
|
||||
this.sslBundles = sslBundles;
|
||||
}
|
||||
|
||||
@@ -49,11 +53,11 @@ class AutoConfiguredRestClientSsl implements RestClientSsl {
|
||||
|
||||
@Override
|
||||
public Consumer<RestClient.Builder> fromBundle(SslBundle bundle) {
|
||||
return (builder) -> {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(bundle);
|
||||
ClientHttpRequestFactory requestFactory = this.clientHttpRequestFactoryBuilder.build(settings);
|
||||
builder.requestFactory(requestFactory);
|
||||
};
|
||||
return (builder) -> builder.requestFactory(requestFactory(bundle));
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactory requestFactory(SslBundle bundle) {
|
||||
return this.builder.build(this.settings.withSslBundle(bundle));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,9 +68,12 @@ public class RestClientAutoConfiguration {
|
||||
@ConditionalOnMissingBean(RestClientSsl.class)
|
||||
@ConditionalOnBean(SslBundles.class)
|
||||
AutoConfiguredRestClientSsl restClientSsl(
|
||||
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder, SslBundles sslBundles) {
|
||||
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
|
||||
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings, SslBundles sslBundles) {
|
||||
return new AutoConfiguredRestClientSsl(
|
||||
clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect), sslBundles);
|
||||
clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect),
|
||||
clientHttpRequestFactorySettings.getIfAvailable(ClientHttpRequestFactorySettings::defaults),
|
||||
sslBundles);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.web.client;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
|
||||
import org.springframework.boot.ssl.NoSuchSslBundleException;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
@@ -34,8 +35,11 @@ import org.springframework.web.client.RestClient;
|
||||
* RestClient restClient = restClientBuilder.apply(ssl.fromBundle("mybundle")).build();
|
||||
* return new MyBean(restClient);
|
||||
* }
|
||||
* </pre> NOTE: Apply SSL configuration will replace any previously
|
||||
* </pre> NOTE: Applying SSL configuration will replace any previously
|
||||
* {@link RestClient.Builder#requestFactory configured} {@link ClientHttpRequestFactory}.
|
||||
* The replacement {@link ClientHttpRequestFactory} will apply only configured
|
||||
* {@link ClientHttpRequestFactorySettings} and the appropriate {@link SslBundle}.
|
||||
* <p>
|
||||
* If you need to configure {@link ClientHttpRequestFactory} with more than just SSL
|
||||
* consider using a {@link ClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.web.client;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
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.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.RestClient.Builder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link AutoConfiguredRestClientSsl}.
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AutoConfiguredRestClientSslTests {
|
||||
|
||||
private final ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings
|
||||
.ofSslBundle(mock(SslBundle.class, "Default SslBundle"))
|
||||
.withRedirects(Redirects.DONT_FOLLOW)
|
||||
.withReadTimeout(Duration.ofSeconds(10))
|
||||
.withConnectTimeout(Duration.ofSeconds(30));
|
||||
|
||||
@Mock
|
||||
private SslBundles sslBundles;
|
||||
|
||||
@Mock
|
||||
private ClientHttpRequestFactoryBuilder<ClientHttpRequestFactory> factoryBuilder;
|
||||
|
||||
@Mock
|
||||
private ClientHttpRequestFactory factory;
|
||||
|
||||
private AutoConfiguredRestClientSsl restClientSsl;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
this.restClientSsl = new AutoConfiguredRestClientSsl(this.factoryBuilder, this.settings, this.sslBundles);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureRestClientUsingBundleName() {
|
||||
String bundleName = "test";
|
||||
SslBundle sslBundle = mock(SslBundle.class, "SslBundle named '%s'".formatted(bundleName));
|
||||
given(this.sslBundles.getBundle(bundleName)).willReturn(sslBundle);
|
||||
given(this.factoryBuilder.build(this.settings.withSslBundle(sslBundle))).willReturn(this.factory);
|
||||
RestClient restClient = build(this.restClientSsl.fromBundle(bundleName));
|
||||
assertThat(restClient).hasFieldOrPropertyWithValue("clientRequestFactory", this.factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureRestClientUsingBundle() {
|
||||
SslBundle sslBundle = mock(SslBundle.class, "Custom SslBundle");
|
||||
given(this.factoryBuilder.build(this.settings.withSslBundle(sslBundle))).willReturn(this.factory);
|
||||
RestClient restClient = build(this.restClientSsl.fromBundle(sslBundle));
|
||||
assertThat(restClient).hasFieldOrPropertyWithValue("clientRequestFactory", this.factory);
|
||||
}
|
||||
|
||||
private RestClient build(Consumer<RestClient.Builder> customizer) {
|
||||
Builder builder = RestClient.builder();
|
||||
customizer.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.web.client;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfigur
|
||||
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.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.web.client.RestClientCustomizer;
|
||||
@@ -50,6 +52,7 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Moritz Halbritter
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
class RestClientAutoConfigurationTests {
|
||||
|
||||
@@ -66,9 +69,41 @@ class RestClientAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupplyRestClientSslIfSslBundlesIsThere() {
|
||||
this.contextRunner.withBean(SslBundles.class, () -> mock(SslBundles.class))
|
||||
.run((context) -> assertThat(context).hasSingleBean(RestClientSsl.class));
|
||||
void shouldSupplyRestClientSslIfSslBundlesIsThereWithCustomHttpSettingsAndBuilder() {
|
||||
SslBundles sslBundles = mock(SslBundles.class);
|
||||
ClientHttpRequestFactorySettings clientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withRedirects(Redirects.DONT_FOLLOW)
|
||||
.withConnectTimeout(Duration.ofHours(1))
|
||||
.withReadTimeout(Duration.ofDays(1))
|
||||
.withSslBundle(mock(SslBundle.class));
|
||||
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder = mock(
|
||||
ClientHttpRequestFactoryBuilder.class);
|
||||
this.contextRunner.withBean(SslBundles.class, () -> sslBundles)
|
||||
.withBean(ClientHttpRequestFactorySettings.class, () -> clientHttpRequestFactorySettings)
|
||||
.withBean(ClientHttpRequestFactoryBuilder.class, () -> clientHttpRequestFactoryBuilder)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(RestClientSsl.class);
|
||||
RestClientSsl restClientSsl = context.getBean(RestClientSsl.class);
|
||||
assertThat(restClientSsl).hasFieldOrPropertyWithValue("sslBundles", sslBundles);
|
||||
assertThat(restClientSsl).hasFieldOrPropertyWithValue("builder", clientHttpRequestFactoryBuilder);
|
||||
assertThat(restClientSsl).hasFieldOrPropertyWithValue("settings", clientHttpRequestFactorySettings);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupplyRestClientSslIfSslBundlesIsThereWithAutoConfiguredHttpSettingsAndBuilder() {
|
||||
SslBundles sslBundles = mock(SslBundles.class);
|
||||
this.contextRunner.withBean(SslBundles.class, () -> sslBundles).run((context) -> {
|
||||
assertThat(context).hasSingleBean(RestClientSsl.class)
|
||||
.hasSingleBean(ClientHttpRequestFactorySettings.class)
|
||||
.hasSingleBean(ClientHttpRequestFactoryBuilder.class);
|
||||
RestClientSsl restClientSsl = context.getBean(RestClientSsl.class);
|
||||
assertThat(restClientSsl).hasFieldOrPropertyWithValue("sslBundles", sslBundles);
|
||||
assertThat(restClientSsl).hasFieldOrPropertyWithValue("builder",
|
||||
context.getBean(ClientHttpRequestFactoryBuilder.class));
|
||||
assertThat(restClientSsl).hasFieldOrPropertyWithValue("settings",
|
||||
context.getBean(ClientHttpRequestFactorySettings.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user