From 8ca8ab14f66fde79a8f67f56c4ed291ec613b747 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 5 Dec 2024 18:01:09 -0800 Subject: [PATCH] Add `withRequestFactorySettings` method and restore previous defaults Update `TestRestTemplate` with a `withRequestFactorySettings` method that can be used to change defaults such as `Redirects`. This commit also restores the previous redirect defaults for HTTP components where redirects would only be followed when the `HttpClientOption.ENABLE_REDIRECTS` was specified. Closes gh-43258 --- .../test/web/client/TestRestTemplate.java | 62 ++++++++++++++----- .../web/client/TestRestTemplateTests.java | 24 ++++++- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java index f0629aa3db..46c9067f8d 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java @@ -22,8 +22,6 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.time.Duration; -import java.util.Arrays; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -60,6 +58,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.web.client.NoOpResponseErrorHandler; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResponseExtractor; @@ -100,8 +99,6 @@ public class TestRestTemplate { private final RestTemplateBuilder builder; - private final HttpClientOption[] httpClientOptions; - private final RestTemplate restTemplate; /** @@ -142,12 +139,26 @@ public class TestRestTemplate { */ public TestRestTemplate(RestTemplateBuilder builder, String username, String password, HttpClientOption... httpClientOptions) { - Assert.notNull(builder, "Builder must not be null"); + this(createInitialBuilder(builder, username, password, httpClientOptions), null); + } + + private TestRestTemplate(RestTemplateBuilder builder, UriTemplateHandler uriTemplateHandler) { this.builder = builder; - this.httpClientOptions = httpClientOptions; + this.restTemplate = builder.build(); + if (uriTemplateHandler != null) { + this.restTemplate.setUriTemplateHandler(uriTemplateHandler); + } + this.restTemplate.setErrorHandler(new NoOpResponseErrorHandler()); + } + + private static RestTemplateBuilder createInitialBuilder(RestTemplateBuilder builder, String username, + String password, HttpClientOption... httpClientOptions) { + Assert.notNull(builder, "Builder must not be null"); if (httpClientOptions != null) { ClientHttpRequestFactory requestFactory = builder.buildRequestFactory(); if (requestFactory instanceof HttpComponentsClientHttpRequestFactory) { + builder = builder.redirects(HttpClientOption.ENABLE_REDIRECTS.isPresent(httpClientOptions) + ? Redirects.FOLLOW : Redirects.DONT_FOLLOW); builder = builder.requestFactoryBuilder( (settings) -> new CustomHttpComponentsClientHttpRequestFactory(httpClientOptions, settings)); } @@ -155,8 +166,7 @@ public class TestRestTemplate { if (username != null || password != null) { builder = builder.basicAuthentication(username, password); } - this.restTemplate = builder.build(); - this.restTemplate.setErrorHandler(new NoOpResponseErrorHandler()); + return builder; } /** @@ -943,9 +953,25 @@ public class TestRestTemplate { * @since 1.4.1 */ public TestRestTemplate withBasicAuth(String username, String password) { - TestRestTemplate template = new TestRestTemplate(this.builder, username, password, this.httpClientOptions); - template.setUriTemplateHandler(getRestTemplate().getUriTemplateHandler()); - return template; + if (username == null && password == null) { + return this; + } + return new TestRestTemplate(this.builder.basicAuthentication(username, password), + this.restTemplate.getUriTemplateHandler()); + } + + /** + * Creates a new {@code TestRestTemplate} with the same configuration as this one, + * except that it will apply the given {@link ClientHttpRequestFactorySettings}. The + * request factory used is a new instance of the underlying {@link RestTemplate}'s + * request factory type (when possible). + * @param requestFactorySettings the new request factory settings + * @return the new template + * @since 3.4.1 + */ + public TestRestTemplate withRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) { + return new TestRestTemplate(this.builder.requestFactorySettings(requestFactorySettings), + this.restTemplate.getUriTemplateHandler()); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -996,7 +1022,11 @@ public class TestRestTemplate { /** * Use a {@link TlsSocketStrategy} that trusts self-signed certificates. */ - SSL + SSL; + + boolean isPresent(HttpClientOption[] options) { + return ObjectUtils.containsElement(options, this); + } } @@ -1031,12 +1061,10 @@ public class TestRestTemplate { */ public CustomHttpComponentsClientHttpRequestFactory(HttpClientOption[] httpClientOptions, ClientHttpRequestFactorySettings settings) { - Set options = new HashSet<>(Arrays.asList(httpClientOptions)); - this.cookieSpec = (options.contains(HttpClientOption.ENABLE_COOKIES) ? StandardCookieSpec.STRICT + this.cookieSpec = (HttpClientOption.ENABLE_COOKIES.isPresent(httpClientOptions) ? StandardCookieSpec.STRICT : StandardCookieSpec.IGNORE); - this.enableRedirects = options.contains(HttpClientOption.ENABLE_REDIRECTS) - || settings.redirects() != Redirects.DONT_FOLLOW; - boolean ssl = options.contains(HttpClientOption.SSL); + this.enableRedirects = settings.redirects() != Redirects.DONT_FOLLOW; + boolean ssl = HttpClientOption.SSL.isPresent(httpClientOptions); if (settings.readTimeout() != null || ssl) { setHttpClient(createHttpClient(settings.readTimeout(), ssl)); } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java index 1d86660888..dc99a59174 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java @@ -29,6 +29,7 @@ import org.apache.hc.client5.http.config.RequestConfig; import org.junit.jupiter.api.Test; 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.test.web.client.TestRestTemplate.CustomHttpComponentsClientHttpRequestFactory; import org.springframework.boot.test.web.client.TestRestTemplate.HttpClientOption; @@ -157,15 +158,34 @@ class TestRestTemplateTests { void httpComponentsAreBuildConsideringSettingsInRestTemplateBuilder() { RestTemplateBuilder builder = new RestTemplateBuilder() .requestFactoryBuilder(ClientHttpRequestFactoryBuilder.httpComponents()); - assertThat(getRequestConfig((RestTemplateBuilder) null).isRedirectsEnabled()).isTrue(); + assertThat(getRequestConfig((RestTemplateBuilder) null).isRedirectsEnabled()).isFalse(); assertThat(getRequestConfig(null, HttpClientOption.ENABLE_REDIRECTS).isRedirectsEnabled()).isTrue(); - assertThat(getRequestConfig(builder).isRedirectsEnabled()).isTrue(); + assertThat(getRequestConfig(builder).isRedirectsEnabled()).isFalse(); assertThat(getRequestConfig(builder, HttpClientOption.ENABLE_REDIRECTS).isRedirectsEnabled()).isTrue(); assertThat(getRequestConfig(builder.redirects(Redirects.DONT_FOLLOW)).isRedirectsEnabled()).isFalse(); assertThat(getRequestConfig(builder.redirects(Redirects.DONT_FOLLOW), HttpClientOption.ENABLE_REDIRECTS) .isRedirectsEnabled()).isTrue(); } + @Test + void withSettingsUpdatesRedirectsForHttpComponents() { + TestRestTemplate template = new TestRestTemplate(); + assertThat(getRequestConfig(template).isRedirectsEnabled()).isFalse(); + assertThat(getRequestConfig(template + .withRequestFactorySettings(ClientHttpRequestFactorySettings.defaults().withRedirects(Redirects.FOLLOW))) + .isRedirectsEnabled()).isTrue(); + } + + @Test + void withSettingsUpdatesRedirectsForJdk() { + TestRestTemplate template = new TestRestTemplate( + new RestTemplateBuilder().requestFactoryBuilder(ClientHttpRequestFactoryBuilder.jdk())); + assertThat(getJdkHttpClient(template).followRedirects()).isEqualTo(Redirect.NORMAL); + assertThat(getJdkHttpClient(template.withRequestFactorySettings( + ClientHttpRequestFactorySettings.defaults().withRedirects(Redirects.DONT_FOLLOW))) + .followRedirects()).isEqualTo(Redirect.NEVER); + } + private RequestConfig getRequestConfig(RestTemplateBuilder builder, HttpClientOption... httpClientOptions) { builder = (builder != null) ? builder : new RestTemplateBuilder(); TestRestTemplate template = new TestRestTemplate(builder, null, null, httpClientOptions);