Browse Source

Merge pull request #44979 from nosan

* pr/44979:
  Polish 'Update RestClientSsl to support ClientHttpRequestFactorySettings'
  Update RestClientSsl to support ClientHttpRequestFactorySettings

Closes gh-44979
pull/45203/head
Phillip Webb 10 months ago
parent
commit
2f30c52269
  1. 22
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.java
  2. 7
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java
  3. 6
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientSsl.java
  4. 95
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSslTests.java
  5. 41
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java

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

@ -1,5 +1,5 @@ @@ -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; @@ -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 { @@ -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));
}
}

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

@ -68,9 +68,12 @@ public class RestClientAutoConfiguration { @@ -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

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

@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.web.client; @@ -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; @@ -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}.
*

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

@ -0,0 +1,95 @@ @@ -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();
}
}

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

@ -16,6 +16,7 @@ @@ -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 @@ -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; @@ -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 { @@ -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

Loading…
Cancel
Save