Skip to content

Commit 2f30c52

Browse files
committed
Merge pull request #44979 from nosan
* pr/44979: Polish 'Update RestClientSsl to support ClientHttpRequestFactorySettings' Update RestClientSsl to support ClientHttpRequestFactorySettings Closes gh-44979
2 parents 6746cac + 567353a commit 2f30c52

File tree

5 files changed

+156
-15
lines changed

5 files changed

+156
-15
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.java

+13-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,16 +29,20 @@
2929
* An auto-configured {@link RestClientSsl} implementation.
3030
*
3131
* @author Phillip Webb
32+
* @author Dmytro Nosan
3233
*/
3334
class AutoConfiguredRestClientSsl implements RestClientSsl {
3435

35-
private final ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder;
36+
private final ClientHttpRequestFactoryBuilder<?> builder;
37+
38+
private final ClientHttpRequestFactorySettings settings;
3639

3740
private final SslBundles sslBundles;
3841

3942
AutoConfiguredRestClientSsl(ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder,
40-
SslBundles sslBundles) {
41-
this.clientHttpRequestFactoryBuilder = clientHttpRequestFactoryBuilder;
43+
ClientHttpRequestFactorySettings clientHttpRequestFactorySettings, SslBundles sslBundles) {
44+
this.builder = clientHttpRequestFactoryBuilder;
45+
this.settings = clientHttpRequestFactorySettings;
4246
this.sslBundles = sslBundles;
4347
}
4448

@@ -49,11 +53,11 @@ public Consumer<RestClient.Builder> fromBundle(String bundleName) {
4953

5054
@Override
5155
public Consumer<RestClient.Builder> fromBundle(SslBundle bundle) {
52-
return (builder) -> {
53-
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(bundle);
54-
ClientHttpRequestFactory requestFactory = this.clientHttpRequestFactoryBuilder.build(settings);
55-
builder.requestFactory(requestFactory);
56-
};
56+
return (builder) -> builder.requestFactory(requestFactory(bundle));
57+
}
58+
59+
private ClientHttpRequestFactory requestFactory(SslBundle bundle) {
60+
return this.builder.build(this.settings.withSslBundle(bundle));
5761
}
5862

5963
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,12 @@ HttpMessageConvertersRestClientCustomizer httpMessageConvertersRestClientCustomi
6868
@ConditionalOnMissingBean(RestClientSsl.class)
6969
@ConditionalOnBean(SslBundles.class)
7070
AutoConfiguredRestClientSsl restClientSsl(
71-
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder, SslBundles sslBundles) {
71+
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
72+
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings, SslBundles sslBundles) {
7273
return new AutoConfiguredRestClientSsl(
73-
clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect), sslBundles);
74+
clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect),
75+
clientHttpRequestFactorySettings.getIfAvailable(ClientHttpRequestFactorySettings::defaults),
76+
sslBundles);
7477
}
7578

7679
@Bean

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientSsl.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.function.Consumer;
2020

2121
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
22+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
2223
import org.springframework.boot.ssl.NoSuchSslBundleException;
2324
import org.springframework.boot.ssl.SslBundle;
2425
import org.springframework.http.client.ClientHttpRequestFactory;
@@ -34,8 +35,11 @@
3435
* RestClient restClient = restClientBuilder.apply(ssl.fromBundle("mybundle")).build();
3536
* return new MyBean(restClient);
3637
* }
37-
* </pre> NOTE: Apply SSL configuration will replace any previously
38+
* </pre> NOTE: Applying SSL configuration will replace any previously
3839
* {@link RestClient.Builder#requestFactory configured} {@link ClientHttpRequestFactory}.
40+
* The replacement {@link ClientHttpRequestFactory} will apply only configured
41+
* {@link ClientHttpRequestFactorySettings} and the appropriate {@link SslBundle}.
42+
* <p>
3943
* If you need to configure {@link ClientHttpRequestFactory} with more than just SSL
4044
* consider using a {@link ClientHttpRequestFactoryBuilder}.
4145
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.web.client;
18+
19+
import java.time.Duration;
20+
import java.util.function.Consumer;
21+
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
import org.mockito.Mock;
25+
import org.mockito.MockitoAnnotations;
26+
27+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
28+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
29+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
30+
import org.springframework.boot.ssl.SslBundle;
31+
import org.springframework.boot.ssl.SslBundles;
32+
import org.springframework.http.client.ClientHttpRequestFactory;
33+
import org.springframework.web.client.RestClient;
34+
import org.springframework.web.client.RestClient.Builder;
35+
36+
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.mockito.BDDMockito.given;
38+
import static org.mockito.Mockito.mock;
39+
40+
/**
41+
* Tests for {@link AutoConfiguredRestClientSsl}.
42+
*
43+
* @author Dmytro Nosan
44+
* @author Phillip Webb
45+
*/
46+
class AutoConfiguredRestClientSslTests {
47+
48+
private final ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings
49+
.ofSslBundle(mock(SslBundle.class, "Default SslBundle"))
50+
.withRedirects(Redirects.DONT_FOLLOW)
51+
.withReadTimeout(Duration.ofSeconds(10))
52+
.withConnectTimeout(Duration.ofSeconds(30));
53+
54+
@Mock
55+
private SslBundles sslBundles;
56+
57+
@Mock
58+
private ClientHttpRequestFactoryBuilder<ClientHttpRequestFactory> factoryBuilder;
59+
60+
@Mock
61+
private ClientHttpRequestFactory factory;
62+
63+
private AutoConfiguredRestClientSsl restClientSsl;
64+
65+
@BeforeEach
66+
void setup() {
67+
MockitoAnnotations.openMocks(this);
68+
this.restClientSsl = new AutoConfiguredRestClientSsl(this.factoryBuilder, this.settings, this.sslBundles);
69+
}
70+
71+
@Test
72+
void shouldConfigureRestClientUsingBundleName() {
73+
String bundleName = "test";
74+
SslBundle sslBundle = mock(SslBundle.class, "SslBundle named '%s'".formatted(bundleName));
75+
given(this.sslBundles.getBundle(bundleName)).willReturn(sslBundle);
76+
given(this.factoryBuilder.build(this.settings.withSslBundle(sslBundle))).willReturn(this.factory);
77+
RestClient restClient = build(this.restClientSsl.fromBundle(bundleName));
78+
assertThat(restClient).hasFieldOrPropertyWithValue("clientRequestFactory", this.factory);
79+
}
80+
81+
@Test
82+
void shouldConfigureRestClientUsingBundle() {
83+
SslBundle sslBundle = mock(SslBundle.class, "Custom SslBundle");
84+
given(this.factoryBuilder.build(this.settings.withSslBundle(sslBundle))).willReturn(this.factory);
85+
RestClient restClient = build(this.restClientSsl.fromBundle(sslBundle));
86+
assertThat(restClient).hasFieldOrPropertyWithValue("clientRequestFactory", this.factory);
87+
}
88+
89+
private RestClient build(Consumer<RestClient.Builder> customizer) {
90+
Builder builder = RestClient.builder();
91+
customizer.accept(builder);
92+
return builder.build();
93+
}
94+
95+
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java

+38-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.autoconfigure.web.client;
1818

19+
import java.time.Duration;
1920
import java.util.List;
2021

2122
import org.junit.jupiter.api.Test;
@@ -27,6 +28,7 @@
2728
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
2829
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
2930
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
31+
import org.springframework.boot.ssl.SslBundle;
3032
import org.springframework.boot.ssl.SslBundles;
3133
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3234
import org.springframework.boot.web.client.RestClientCustomizer;
@@ -50,6 +52,7 @@
5052
*
5153
* @author Arjen Poutsma
5254
* @author Moritz Halbritter
55+
* @author Dmytro Nosan
5356
*/
5457
class RestClientAutoConfigurationTests {
5558

@@ -66,9 +69,41 @@ void shouldSupplyBeans() {
6669
}
6770

6871
@Test
69-
void shouldSupplyRestClientSslIfSslBundlesIsThere() {
70-
this.contextRunner.withBean(SslBundles.class, () -> mock(SslBundles.class))
71-
.run((context) -> assertThat(context).hasSingleBean(RestClientSsl.class));
72+
void shouldSupplyRestClientSslIfSslBundlesIsThereWithCustomHttpSettingsAndBuilder() {
73+
SslBundles sslBundles = mock(SslBundles.class);
74+
ClientHttpRequestFactorySettings clientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.defaults()
75+
.withRedirects(Redirects.DONT_FOLLOW)
76+
.withConnectTimeout(Duration.ofHours(1))
77+
.withReadTimeout(Duration.ofDays(1))
78+
.withSslBundle(mock(SslBundle.class));
79+
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder = mock(
80+
ClientHttpRequestFactoryBuilder.class);
81+
this.contextRunner.withBean(SslBundles.class, () -> sslBundles)
82+
.withBean(ClientHttpRequestFactorySettings.class, () -> clientHttpRequestFactorySettings)
83+
.withBean(ClientHttpRequestFactoryBuilder.class, () -> clientHttpRequestFactoryBuilder)
84+
.run((context) -> {
85+
assertThat(context).hasSingleBean(RestClientSsl.class);
86+
RestClientSsl restClientSsl = context.getBean(RestClientSsl.class);
87+
assertThat(restClientSsl).hasFieldOrPropertyWithValue("sslBundles", sslBundles);
88+
assertThat(restClientSsl).hasFieldOrPropertyWithValue("builder", clientHttpRequestFactoryBuilder);
89+
assertThat(restClientSsl).hasFieldOrPropertyWithValue("settings", clientHttpRequestFactorySettings);
90+
});
91+
}
92+
93+
@Test
94+
void shouldSupplyRestClientSslIfSslBundlesIsThereWithAutoConfiguredHttpSettingsAndBuilder() {
95+
SslBundles sslBundles = mock(SslBundles.class);
96+
this.contextRunner.withBean(SslBundles.class, () -> sslBundles).run((context) -> {
97+
assertThat(context).hasSingleBean(RestClientSsl.class)
98+
.hasSingleBean(ClientHttpRequestFactorySettings.class)
99+
.hasSingleBean(ClientHttpRequestFactoryBuilder.class);
100+
RestClientSsl restClientSsl = context.getBean(RestClientSsl.class);
101+
assertThat(restClientSsl).hasFieldOrPropertyWithValue("sslBundles", sslBundles);
102+
assertThat(restClientSsl).hasFieldOrPropertyWithValue("builder",
103+
context.getBean(ClientHttpRequestFactoryBuilder.class));
104+
assertThat(restClientSsl).hasFieldOrPropertyWithValue("settings",
105+
context.getBean(ClientHttpRequestFactorySettings.class));
106+
});
72107
}
73108

74109
@Test

0 commit comments

Comments
 (0)