From 085d5225700e67855287c627993ad46b41bbfd58 Mon Sep 17 00:00:00 2001 From: Apoorv Darshan Date: Wed, 25 Feb 2026 21:29:34 +0530 Subject: [PATCH 1/2] Update TestRestTemplate's default cookie handling to match RestTemplate's See gh-49261 Signed-off-by: Apoorv Darshan --- .../boot/http/client/HttpClientSettings.java | 47 +++++++++++++++---- .../http/client/HttpComponentsCookieSpec.java | 44 +++++++++++++++++ .../HttpComponentsHttpClientBuilder.java | 8 +++- .../boot/http/client/HttpCookies.java | 42 +++++++++++++++++ .../http/client/JdkHttpClientBuilder.java | 10 ++++ .../http/client/JettyHttpClientBuilder.java | 9 ++++ .../http/client/HttpClientSettingsTests.java | 19 ++++++-- .../HttpClientAutoConfigurationTests.java | 4 +- ...HttpClientSettingsPropertyMapperTests.java | 2 +- .../boot/restclient/RestTemplateBuilder.java | 15 ++++++ .../boot/resttestclient/TestRestTemplate.java | 27 +++++++++-- .../resttestclient/TestRestTemplateTests.java | 16 +++++++ 12 files changed, 222 insertions(+), 21 deletions(-) create mode 100644 module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java create mode 100644 module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookies.java diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java index 5f35ff04e43..1722ec01884 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java @@ -25,6 +25,8 @@ import org.springframework.boot.ssl.SslBundle; /** * Settings that can be applied when creating an imperative or reactive HTTP client. * + * @param cookies the cookie handling strategy to use or null to use the underlying + * library's default * @param redirects the follow redirect strategy to use or null to redirect whenever the * underlying library allows it * @param connectTimeout the connect timeout @@ -33,10 +35,36 @@ import org.springframework.boot.ssl.SslBundle; * @author Phillip Webb * @since 3.5.0 */ -public record HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Duration connectTimeout, - @Nullable Duration readTimeout, @Nullable SslBundle sslBundle) { +public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRedirects redirects, + @Nullable Duration connectTimeout, @Nullable Duration readTimeout, @Nullable SslBundle sslBundle) { - private static final HttpClientSettings defaults = new HttpClientSettings(null, null, null, null); + /** + * Create a new {@link HttpClientSettings} instance. + * @param redirects the follow redirect strategy to use + * @param connectTimeout the connect timeout + * @param readTimeout the read timeout + * @param sslBundle the SSL bundle providing SSL configuration + * @deprecated since 4.1.0 for removal in 4.3.0 in favor of + * {@link HttpClientSettings#HttpClientSettings(HttpCookies, HttpRedirects, Duration, Duration, SslBundle)} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Duration connectTimeout, + @Nullable Duration readTimeout, @Nullable SslBundle sslBundle) { + this(null, redirects, connectTimeout, readTimeout, sslBundle); + } + + private static final HttpClientSettings defaults = new HttpClientSettings(null, null, null, null, null); + + /** + * Return a new {@link HttpClientSettings} instance with an updated cookie handling + * setting. + * @param cookies the new cookie handling setting + * @return a new {@link HttpClientSettings} instance + * @since 4.1.0 + */ + public HttpClientSettings withCookies(@Nullable HttpCookies cookies) { + return new HttpClientSettings(cookies, this.redirects, this.connectTimeout, this.readTimeout, this.sslBundle); + } /** * Return a new {@link HttpClientSettings} instance with an updated connect timeout @@ -46,7 +74,7 @@ public record HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Du * @since 4.0.0 */ public HttpClientSettings withConnectTimeout(@Nullable Duration connectTimeout) { - return new HttpClientSettings(this.redirects, connectTimeout, this.readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookies, this.redirects, connectTimeout, this.readTimeout, this.sslBundle); } /** @@ -57,7 +85,7 @@ public record HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Du * @since 4.0.0 */ public HttpClientSettings withReadTimeout(@Nullable Duration readTimeout) { - return new HttpClientSettings(this.redirects, this.connectTimeout, readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookies, this.redirects, this.connectTimeout, readTimeout, this.sslBundle); } /** @@ -69,7 +97,7 @@ public record HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Du * @since 4.0.0 */ public HttpClientSettings withTimeouts(@Nullable Duration connectTimeout, @Nullable Duration readTimeout) { - return new HttpClientSettings(this.redirects, connectTimeout, readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookies, this.redirects, connectTimeout, readTimeout, this.sslBundle); } /** @@ -80,7 +108,7 @@ public record HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Du * @since 4.0.0 */ public HttpClientSettings withSslBundle(@Nullable SslBundle sslBundle) { - return new HttpClientSettings(this.redirects, this.connectTimeout, this.readTimeout, sslBundle); + return new HttpClientSettings(this.cookies, this.redirects, this.connectTimeout, this.readTimeout, sslBundle); } /** @@ -90,7 +118,7 @@ public record HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Du * @since 4.0.0 */ public HttpClientSettings withRedirects(@Nullable HttpRedirects redirects) { - return new HttpClientSettings(redirects, this.connectTimeout, this.readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookies, redirects, this.connectTimeout, this.readTimeout, this.sslBundle); } /** @@ -104,11 +132,12 @@ public record HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Du if (other == null) { return this; } + HttpCookies cookies = (cookies() != null) ? cookies() : other.cookies(); HttpRedirects redirects = (redirects() != null) ? redirects() : other.redirects(); Duration connectTimeout = (connectTimeout() != null) ? connectTimeout() : other.connectTimeout(); Duration readTimeout = (readTimeout() != null) ? readTimeout() : other.readTimeout(); SslBundle sslBundle = (sslBundle() != null) ? sslBundle() : other.sslBundle(); - return new HttpClientSettings(redirects, connectTimeout, readTimeout, sslBundle); + return new HttpClientSettings(cookies, redirects, connectTimeout, readTimeout, sslBundle); } /** diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java new file mode 100644 index 00000000000..a597f36c52c --- /dev/null +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-present 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.http.client; + +import org.apache.hc.client5.http.cookie.StandardCookieSpec; +import org.jspecify.annotations.Nullable; + +/** + * Adapts {@link HttpCookies} to an + * Apache HttpComponents + * cookie spec identifier. + * + * @author Apoorv Darshan + */ +final class HttpComponentsCookieSpec { + + private HttpComponentsCookieSpec() { + } + + static @Nullable String get(@Nullable HttpCookies cookies) { + if (cookies == null) { + return null; + } + return switch (cookies) { + case ENABLE_WHEN_POSSIBLE, ENABLE -> StandardCookieSpec.STRICT; + case DISABLE -> StandardCookieSpec.IGNORE; + }; + } + +} diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java index 5e3f8b5c228..cb1653568cd 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java @@ -180,7 +180,7 @@ public final class HttpComponentsHttpClientBuilder { .useSystemProperties() .setRedirectStrategy(HttpComponentsRedirectStrategy.get(settings.redirects())) .setConnectionManager(createConnectionManager(settings)) - .setDefaultRequestConfig(createDefaultRequestConfig()); + .setDefaultRequestConfig(createDefaultRequestConfig(settings)); this.customizer.accept(builder); return builder.build(); } @@ -218,8 +218,12 @@ public final class HttpComponentsHttpClientBuilder { return builder.build(); } - private RequestConfig createDefaultRequestConfig() { + private RequestConfig createDefaultRequestConfig(HttpClientSettings settings) { RequestConfig.Builder builder = RequestConfig.custom(); + String cookieSpec = HttpComponentsCookieSpec.get(settings.cookies()); + if (cookieSpec != null) { + builder.setCookieSpec(cookieSpec); + } this.defaultRequestConfigCustomizer.accept(builder); return builder.build(); } diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookies.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookies.java new file mode 100644 index 00000000000..0f2ba8162db --- /dev/null +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookies.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-present 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.http.client; + +/** + * Cookie handling strategies supported by HTTP clients. + * + * @author Apoorv Darshan + * @since 4.1.0 + */ +public enum HttpCookies { + + /** + * Enable cookies (if the underlying library has support). + */ + ENABLE_WHEN_POSSIBLE, + + /** + * Enable cookies (fail if the underlying library has no support). + */ + ENABLE, + + /** + * Disable cookies (fail if the underlying library has no support). + */ + DISABLE + +} diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java index 2eb67a5afc9..58c27fe2092 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java @@ -16,6 +16,8 @@ package org.springframework.boot.http.client; +import java.net.CookieHandler; +import java.net.CookieManager; import java.net.http.HttpClient; import java.net.http.HttpClient.Redirect; import java.util.concurrent.Executor; @@ -83,6 +85,7 @@ public final class JdkHttpClientBuilder { Assert.isTrue(settings.readTimeout() == null, "'settings' must not have a 'readTimeout'"); HttpClient.Builder builder = HttpClient.newBuilder(); PropertyMapper map = PropertyMapper.get(); + map.from(settings::cookies).as(this::asCookieHandler).to(builder::cookieHandler); map.from(settings::redirects).always().as(this::asHttpClientRedirect).to(builder::followRedirects); map.from(settings::connectTimeout).to(builder::connectTimeout); map.from(settings::sslBundle).as(SslBundle::createSslContext).to(builder::sslContext); @@ -99,6 +102,13 @@ public final class JdkHttpClientBuilder { return parameters; } + private @Nullable CookieHandler asCookieHandler(HttpCookies cookies) { + return switch (cookies) { + case ENABLE_WHEN_POSSIBLE, ENABLE -> new CookieManager(); + case DISABLE -> null; + }; + } + private Redirect asHttpClientRedirect(@Nullable HttpRedirects redirects) { if (redirects == null) { return Redirect.NORMAL; diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java index 157635cd87c..6ff16265526 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; +import org.eclipse.jetty.http.HttpCookieStore; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.jspecify.annotations.Nullable; @@ -140,6 +141,7 @@ public final class JettyHttpClientBuilder { HttpClient httpClient = createHttpClient(settings.readTimeout(), transport); PropertyMapper map = PropertyMapper.get(); map.from(settings::connectTimeout).as(Duration::toMillis).to(httpClient::setConnectTimeout); + map.from(settings::cookies).as(this::asCookieStore).to(httpClient::setHttpCookieStore); map.from(settings::redirects).always().as(this::followRedirects).to(httpClient::setFollowRedirects); this.customizer.accept(httpClient); return httpClient; @@ -182,6 +184,13 @@ public final class JettyHttpClientBuilder { return factory; } + private @Nullable HttpCookieStore asCookieStore(HttpCookies cookies) { + return switch (cookies) { + case ENABLE_WHEN_POSSIBLE, ENABLE -> null; + case DISABLE -> new HttpCookieStore.Empty(); + }; + } + private boolean followRedirects(@Nullable HttpRedirects redirects) { if (redirects == null) { return true; diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java index d37f796e772..c7b4d1af170 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java @@ -47,7 +47,8 @@ class HttpClientSettingsTests { @Test void createWithNulls() { - HttpClientSettings settings = new HttpClientSettings(null, null, null, null); + HttpClientSettings settings = new HttpClientSettings(null, null, null, null, null); + assertThat(settings.cookies()).isNull(); assertThat(settings.redirects()).isNull(); assertThat(settings.connectTimeout()).isNull(); assertThat(settings.readTimeout()).isNull(); @@ -82,6 +83,16 @@ class HttpClientSettingsTests { assertThat(settings.sslBundle()).isSameAs(sslBundle); } + @Test + void withCookiesReturnsInstanceWithUpdatedCookies() { + HttpClientSettings settings = HttpClientSettings.defaults().withCookies(HttpCookies.DISABLE); + assertThat(settings.cookies()).isEqualTo(HttpCookies.DISABLE); + assertThat(settings.redirects()).isNull(); + assertThat(settings.connectTimeout()).isNull(); + assertThat(settings.readTimeout()).isNull(); + assertThat(settings.sslBundle()).isNull(); + } + @Test void withRedirectsReturnsInstanceWithUpdatedRedirect() { HttpClientSettings settings = HttpClientSettings.defaults().withRedirects(HttpRedirects.DONT_FOLLOW); @@ -94,8 +105,10 @@ class HttpClientSettingsTests { @Test void orElseReturnsNewInstanceWithUpdatedValues() { SslBundle sslBundle = mock(SslBundle.class); - HttpClientSettings settings = new HttpClientSettings(null, ONE_SECOND, null, null) - .orElse(new HttpClientSettings(HttpRedirects.FOLLOW_WHEN_POSSIBLE, TWO_SECONDS, TWO_SECONDS, sslBundle)); + HttpClientSettings settings = new HttpClientSettings(null, null, ONE_SECOND, null, null) + .orElse(new HttpClientSettings(HttpCookies.ENABLE, HttpRedirects.FOLLOW_WHEN_POSSIBLE, TWO_SECONDS, + TWO_SECONDS, sslBundle)); + assertThat(settings.cookies()).isEqualTo(HttpCookies.ENABLE); assertThat(settings.redirects()).isEqualTo(HttpRedirects.FOLLOW_WHEN_POSSIBLE); assertThat(settings.connectTimeout()).isEqualTo(ONE_SECOND); assertThat(settings.readTimeout()).isEqualTo(TWO_SECONDS); diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientAutoConfigurationTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientAutoConfigurationTests.java index f3ebb359d0e..9bc5844f677 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientAutoConfigurationTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientAutoConfigurationTests.java @@ -52,14 +52,14 @@ class HttpClientAutoConfigurationTests { .withPropertyValues("spring.http.clients.redirects=dont-follow", "spring.http.clients.connect-timeout=1s", "spring.http.clients.read-timeout=2s") .run((context) -> assertThat(context.getBean(HttpClientSettings.class)).isEqualTo(new HttpClientSettings( - HttpRedirects.DONT_FOLLOW, Duration.ofSeconds(1), Duration.ofSeconds(2), null))); + null, HttpRedirects.DONT_FOLLOW, Duration.ofSeconds(1), Duration.ofSeconds(2), null))); } @Test void doesNotReplaceUserProvidedHttpClientSettings() { this.contextRunner.withUserConfiguration(TestHttpClientConfiguration.class) .run((context) -> assertThat(context.getBean(HttpClientSettings.class)) - .isEqualTo(new HttpClientSettings(null, Duration.ofSeconds(1), Duration.ofSeconds(2), null))); + .isEqualTo(new HttpClientSettings(null, null, Duration.ofSeconds(1), Duration.ofSeconds(2), null))); } @Configuration(proxyBeanMethods = false) diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java index 2a49cce16f4..e6d0b8b7782 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java @@ -93,7 +93,7 @@ class HttpClientSettingsPropertyMapperTests { @Test void mapUsesBaseSettingsForMissingProperties() { - HttpClientSettings baseSettings = new HttpClientSettings(HttpRedirects.FOLLOW_WHEN_POSSIBLE, + HttpClientSettings baseSettings = new HttpClientSettings(null, HttpRedirects.FOLLOW_WHEN_POSSIBLE, Duration.ofSeconds(15), Duration.ofSeconds(25), null); HttpClientSettingsPropertyMapper mapper = new HttpClientSettingsPropertyMapper(null, baseSettings); TestHttpClientSettingsProperties properties = new TestHttpClientSettingsProperties(); diff --git a/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java b/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java index 37848d57e6f..508e349dca8 100644 --- a/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java +++ b/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java @@ -35,6 +35,7 @@ import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; +import org.springframework.boot.http.client.HttpCookies; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.ssl.SslBundle; import org.springframework.http.client.ClientHttpRequest; @@ -481,6 +482,20 @@ public class RestTemplateBuilder { this.customizers, this.requestCustomizers); } + /** + * Sets the cookie handling strategy on the underlying + * {@link ClientHttpRequestFactory}. + * @param cookies the cookie handling strategy + * @return a new builder instance. + * @since 4.1.0 + */ + public RestTemplateBuilder cookies(HttpCookies cookies) { + return new RestTemplateBuilder(this.clientSettings.withCookies(cookies), this.detectRequestFactory, + this.rootUri, this.messageConverters, this.interceptors, this.requestFactoryBuilder, + this.uriTemplateHandler, this.errorHandler, this.basicAuthentication, this.defaultHeaders, + this.customizers, this.requestCustomizers); + } + /** * Sets the redirect strategy on the underlying {@link ClientHttpRequestFactory}. * @param redirects the redirect strategy diff --git a/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java b/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java index b9988d6d48b..ef63db42d43 100644 --- a/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java +++ b/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java @@ -41,6 +41,7 @@ import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.HttpComponentsClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpComponentsHttpClientBuilder.TlsSocketStrategyFactory; +import org.springframework.boot.http.client.HttpCookies; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; @@ -69,8 +70,7 @@ import org.springframework.web.util.UriTemplateHandler; * status code}. *

* A {@code TestRestTemplate} can optionally carry Basic authentication headers. If Apache - * Http Client 4.3.2 or better is available (recommended) it will be used as the client, - * and by default configured to ignore cookies. + * Http Client 4.3.2 or better is available (recommended) it will be used as the client. *

* Note: To prevent injection problems this class intentionally does not extend * {@link RestTemplate}. If you need access to the underlying {@link RestTemplate} use @@ -160,10 +160,13 @@ public class TestRestTemplate { return builder; } + @SuppressWarnings("deprecation") private static HttpComponentsClientHttpRequestFactoryBuilder applyHttpClientOptions( HttpComponentsClientHttpRequestFactoryBuilder builder, HttpClientOption[] httpClientOptions) { - builder = builder.withDefaultRequestConfigCustomizer( - new CookieSpecCustomizer(HttpClientOption.ENABLE_COOKIES.isPresent(httpClientOptions))); + if (HttpClientOption.ENABLE_COOKIES.isPresent(httpClientOptions)) { + builder = builder.withDefaultRequestConfigCustomizer( + new CookieSpecCustomizer(true)); + } if (HttpClientOption.SSL.isPresent(httpClientOptions)) { builder = builder.withTlsSocketStrategyFactory(new SelfSignedTlsSocketStrategyFactory()); } @@ -974,6 +977,19 @@ public class TestRestTemplate { return withClientSettings((settings) -> settings.withRedirects(redirects)); } + /** + * Creates a new {@code TestRestTemplate} with the same configuration as this one, + * except that it will apply the given {@link HttpCookies}. The request factory used is + * a new instance of the underlying {@link RestTemplate}'s request factory type (when + * possible). + * @param cookies the new cookie settings + * @return the new template + * @since 4.1.0 + */ + public TestRestTemplate withCookies(HttpCookies cookies) { + return withClientSettings((settings) -> settings.withCookies(cookies)); + } + /** * Creates a new {@code TestRestTemplate} with the same configuration as this one, * except that it will apply the given {@link HttpClientSettings}. The request factory @@ -1036,7 +1052,10 @@ public class TestRestTemplate { /** * Enable cookies. + * @deprecated since 4.1.0 for removal in 4.3.0 in favor of + * {@link TestRestTemplate#withCookies(HttpCookies)} */ + @Deprecated(since = "4.1.0", forRemoval = true) ENABLE_COOKIES, /** diff --git a/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java b/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java index 932376a8fcf..f835ec95951 100644 --- a/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java +++ b/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; +import org.springframework.boot.http.client.HttpCookies; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.boot.resttestclient.TestRestTemplate.HttpClientOption; @@ -140,11 +141,26 @@ class TestRestTemplateTests { } @Test + @SuppressWarnings("removal") void options() { RequestConfig config = getRequestConfig(new TestRestTemplate(HttpClientOption.ENABLE_COOKIES)); assertThat(config.getCookieSpec()).isEqualTo("strict"); } + @Test + void defaultCookieSpecMatchesRestTemplate() { + RequestConfig config = getRequestConfig(new TestRestTemplate()); + assertThat(config.getCookieSpec()).isNull(); + } + + @Test + void withCookies() { + TestRestTemplate template = new TestRestTemplate(); + assertThat(getRequestConfig(template).getCookieSpec()).isNull(); + assertThat(getRequestConfig(template.withCookies(HttpCookies.ENABLE)).getCookieSpec()).isEqualTo("strict"); + assertThat(getRequestConfig(template.withCookies(HttpCookies.DISABLE)).getCookieSpec()).isEqualTo("ignoreCookies"); + } + @Test void jdkBuilderCanBeSpecifiedWithSpecificRedirects() { RestTemplateBuilder builder = new RestTemplateBuilder() From 5721f57acd007a98aac1a8c321b387b93381c6d0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 12 Mar 2026 12:13:15 +0000 Subject: [PATCH 2/2] Polish "Update TestRestTemplate's default cookie handling to match RestTemplate's" See gh-49261 Signed-off-by: Andy Wilkinson --- .../pages/testing/test-utilities.adoc | 1 - .../boot/http/client/HttpClientSettings.java | 31 +++++++------ .../http/client/HttpComponentsCookieSpec.java | 44 ------------------- .../HttpComponentsHttpClientBuilder.java | 8 +++- ...tpCookies.java => HttpCookieHandling.java} | 12 +++-- .../http/client/JdkHttpClientBuilder.java | 6 +-- .../http/client/JettyHttpClientBuilder.java | 8 ++-- .../http/client/ReactorHttpClientBuilder.java | 3 ++ ...onentsClientHttpRequestFactoryBuilder.java | 4 ++ ...SimpleClientHttpRequestFactoryBuilder.java | 3 ++ .../HttpClientSettingsProperties.java | 14 ++++++ .../HttpClientSettingsPropertyMapper.java | 1 + .../http/client/HttpClientSettingsTests.java | 12 ++--- ...sClientHttpRequestFactoryBuilderTests.java | 25 +++++++++++ ...kClientHttpRequestFactoryBuilderTests.java | 30 +++++++++++++ ...yClientHttpRequestFactoryBuilderTests.java | 25 +++++++++++ ...rClientHttpRequestFactoryBuilderTests.java | 17 +++++++ ...sClientHttpRequestFactoryBuilderTests.java | 14 ++++++ ...eClientHttpRequestFactoryBuilderTests.java | 8 ++++ ...HttpClientSettingsPropertyMapperTests.java | 10 +++++ .../boot/restclient/RestTemplateBuilder.java | 14 +++--- .../boot/resttestclient/TestRestTemplate.java | 19 ++++---- .../resttestclient/TestRestTemplateTests.java | 14 ++++-- 23 files changed, 225 insertions(+), 98 deletions(-) delete mode 100644 module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java rename module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/{HttpCookies.java => HttpCookieHandling.java} (63%) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/test-utilities.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/test-utilities.adoc index 9b60836f8aa..0143f15733b 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/test-utilities.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/test-utilities.adoc @@ -59,7 +59,6 @@ If you are using Spring WebFlux, consider the javadoc:org.springframework.test.w It is recommended, but not mandatory, to use the Apache HTTP Client (version 5.1 or better). If you have that on your classpath, the javadoc:org.springframework.boot.resttestclient.TestRestTemplate[] responds by configuring the client appropriately. -If you do use Apache's HTTP client it is configured to ignore cookies (so the template is stateless). javadoc:org.springframework.boot.resttestclient.TestRestTemplate[] can be instantiated directly in your integration tests, as shown in the following example: diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java index 1722ec01884..b6966fd9bee 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpClientSettings.java @@ -25,7 +25,7 @@ import org.springframework.boot.ssl.SslBundle; /** * Settings that can be applied when creating an imperative or reactive HTTP client. * - * @param cookies the cookie handling strategy to use or null to use the underlying + * @param cookieHandling the cookie handling strategy to use or null to use the underlying * library's default * @param redirects the follow redirect strategy to use or null to redirect whenever the * underlying library allows it @@ -35,7 +35,7 @@ import org.springframework.boot.ssl.SslBundle; * @author Phillip Webb * @since 3.5.0 */ -public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRedirects redirects, +public record HttpClientSettings(@Nullable HttpCookieHandling cookieHandling, @Nullable HttpRedirects redirects, @Nullable Duration connectTimeout, @Nullable Duration readTimeout, @Nullable SslBundle sslBundle) { /** @@ -45,7 +45,7 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe * @param readTimeout the read timeout * @param sslBundle the SSL bundle providing SSL configuration * @deprecated since 4.1.0 for removal in 4.3.0 in favor of - * {@link HttpClientSettings#HttpClientSettings(HttpCookies, HttpRedirects, Duration, Duration, SslBundle)} + * {@link HttpClientSettings#HttpClientSettings(HttpCookieHandling, HttpRedirects, Duration, Duration, SslBundle)} */ @Deprecated(since = "4.1.0", forRemoval = true) public HttpClientSettings(@Nullable HttpRedirects redirects, @Nullable Duration connectTimeout, @@ -58,12 +58,13 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe /** * Return a new {@link HttpClientSettings} instance with an updated cookie handling * setting. - * @param cookies the new cookie handling setting + * @param cookieHandling the new cookie handling setting * @return a new {@link HttpClientSettings} instance * @since 4.1.0 */ - public HttpClientSettings withCookies(@Nullable HttpCookies cookies) { - return new HttpClientSettings(cookies, this.redirects, this.connectTimeout, this.readTimeout, this.sslBundle); + public HttpClientSettings withCookieHandling(@Nullable HttpCookieHandling cookieHandling) { + return new HttpClientSettings(cookieHandling, this.redirects, this.connectTimeout, this.readTimeout, + this.sslBundle); } /** @@ -74,7 +75,8 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe * @since 4.0.0 */ public HttpClientSettings withConnectTimeout(@Nullable Duration connectTimeout) { - return new HttpClientSettings(this.cookies, this.redirects, connectTimeout, this.readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookieHandling, this.redirects, connectTimeout, this.readTimeout, + this.sslBundle); } /** @@ -85,7 +87,8 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe * @since 4.0.0 */ public HttpClientSettings withReadTimeout(@Nullable Duration readTimeout) { - return new HttpClientSettings(this.cookies, this.redirects, this.connectTimeout, readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookieHandling, this.redirects, this.connectTimeout, readTimeout, + this.sslBundle); } /** @@ -97,7 +100,7 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe * @since 4.0.0 */ public HttpClientSettings withTimeouts(@Nullable Duration connectTimeout, @Nullable Duration readTimeout) { - return new HttpClientSettings(this.cookies, this.redirects, connectTimeout, readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookieHandling, this.redirects, connectTimeout, readTimeout, this.sslBundle); } /** @@ -108,7 +111,8 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe * @since 4.0.0 */ public HttpClientSettings withSslBundle(@Nullable SslBundle sslBundle) { - return new HttpClientSettings(this.cookies, this.redirects, this.connectTimeout, this.readTimeout, sslBundle); + return new HttpClientSettings(this.cookieHandling, this.redirects, this.connectTimeout, this.readTimeout, + sslBundle); } /** @@ -118,7 +122,8 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe * @since 4.0.0 */ public HttpClientSettings withRedirects(@Nullable HttpRedirects redirects) { - return new HttpClientSettings(this.cookies, redirects, this.connectTimeout, this.readTimeout, this.sslBundle); + return new HttpClientSettings(this.cookieHandling, redirects, this.connectTimeout, this.readTimeout, + this.sslBundle); } /** @@ -132,12 +137,12 @@ public record HttpClientSettings(@Nullable HttpCookies cookies, @Nullable HttpRe if (other == null) { return this; } - HttpCookies cookies = (cookies() != null) ? cookies() : other.cookies(); + HttpCookieHandling cookieHandling = (cookieHandling() != null) ? cookieHandling() : other.cookieHandling(); HttpRedirects redirects = (redirects() != null) ? redirects() : other.redirects(); Duration connectTimeout = (connectTimeout() != null) ? connectTimeout() : other.connectTimeout(); Duration readTimeout = (readTimeout() != null) ? readTimeout() : other.readTimeout(); SslBundle sslBundle = (sslBundle() != null) ? sslBundle() : other.sslBundle(); - return new HttpClientSettings(cookies, redirects, connectTimeout, readTimeout, sslBundle); + return new HttpClientSettings(cookieHandling, redirects, connectTimeout, readTimeout, sslBundle); } /** diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java deleted file mode 100644 index a597f36c52c..00000000000 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsCookieSpec.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012-present 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.http.client; - -import org.apache.hc.client5.http.cookie.StandardCookieSpec; -import org.jspecify.annotations.Nullable; - -/** - * Adapts {@link HttpCookies} to an - * Apache HttpComponents - * cookie spec identifier. - * - * @author Apoorv Darshan - */ -final class HttpComponentsCookieSpec { - - private HttpComponentsCookieSpec() { - } - - static @Nullable String get(@Nullable HttpCookies cookies) { - if (cookies == null) { - return null; - } - return switch (cookies) { - case ENABLE_WHEN_POSSIBLE, ENABLE -> StandardCookieSpec.STRICT; - case DISABLE -> StandardCookieSpec.IGNORE; - }; - } - -} diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java index cb1653568cd..c14ecf3a3d9 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpComponentsHttpClientBuilder.java @@ -23,6 +23,7 @@ import java.util.function.Consumer; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; @@ -220,8 +221,11 @@ public final class HttpComponentsHttpClientBuilder { private RequestConfig createDefaultRequestConfig(HttpClientSettings settings) { RequestConfig.Builder builder = RequestConfig.custom(); - String cookieSpec = HttpComponentsCookieSpec.get(settings.cookies()); - if (cookieSpec != null) { + if (settings.cookieHandling() != null) { + String cookieSpec = switch (settings.cookieHandling()) { + case ENABLE_WHEN_POSSIBLE, ENABLE -> StandardCookieSpec.STRICT; + case DISABLE -> StandardCookieSpec.IGNORE; + }; builder.setCookieSpec(cookieSpec); } this.defaultRequestConfigCustomizer.accept(builder); diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookies.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookieHandling.java similarity index 63% rename from module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookies.java rename to module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookieHandling.java index 0f2ba8162db..e43402bd7bd 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookies.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/HttpCookieHandling.java @@ -22,20 +22,24 @@ package org.springframework.boot.http.client; * @author Apoorv Darshan * @since 4.1.0 */ -public enum HttpCookies { +public enum HttpCookieHandling { /** - * Enable cookies (if the underlying library has support). + * Enable cookie handling (if the underlying library has support). When enabled, + * cookies received in a response are stored and automatically included in subsequent + * matching requests. */ ENABLE_WHEN_POSSIBLE, /** - * Enable cookies (fail if the underlying library has no support). + * Enable cookie handling (fail if the underlying library has no support). When + * enabled, cookies received in a response are stored and automatically included in + * subsequent matching requests. */ ENABLE, /** - * Disable cookies (fail if the underlying library has no support). + * Disable cookie handling (fail if the underlying library has no support). */ DISABLE diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java index 58c27fe2092..13e1f6d8970 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JdkHttpClientBuilder.java @@ -85,7 +85,7 @@ public final class JdkHttpClientBuilder { Assert.isTrue(settings.readTimeout() == null, "'settings' must not have a 'readTimeout'"); HttpClient.Builder builder = HttpClient.newBuilder(); PropertyMapper map = PropertyMapper.get(); - map.from(settings::cookies).as(this::asCookieHandler).to(builder::cookieHandler); + map.from(settings::cookieHandling).as(this::asCookieHandler).to(builder::cookieHandler); map.from(settings::redirects).always().as(this::asHttpClientRedirect).to(builder::followRedirects); map.from(settings::connectTimeout).to(builder::connectTimeout); map.from(settings::sslBundle).as(SslBundle::createSslContext).to(builder::sslContext); @@ -102,8 +102,8 @@ public final class JdkHttpClientBuilder { return parameters; } - private @Nullable CookieHandler asCookieHandler(HttpCookies cookies) { - return switch (cookies) { + private @Nullable CookieHandler asCookieHandler(HttpCookieHandling cookieHandling) { + return switch (cookieHandling) { case ENABLE_WHEN_POSSIBLE, ENABLE -> new CookieManager(); case DISABLE -> null; }; diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java index 6ff16265526..e01647be3d3 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java @@ -141,7 +141,7 @@ public final class JettyHttpClientBuilder { HttpClient httpClient = createHttpClient(settings.readTimeout(), transport); PropertyMapper map = PropertyMapper.get(); map.from(settings::connectTimeout).as(Duration::toMillis).to(httpClient::setConnectTimeout); - map.from(settings::cookies).as(this::asCookieStore).to(httpClient::setHttpCookieStore); + map.from(settings::cookieHandling).as(this::asCookieStore).to(httpClient::setHttpCookieStore); map.from(settings::redirects).always().as(this::followRedirects).to(httpClient::setFollowRedirects); this.customizer.accept(httpClient); return httpClient; @@ -184,9 +184,9 @@ public final class JettyHttpClientBuilder { return factory; } - private @Nullable HttpCookieStore asCookieStore(HttpCookies cookies) { - return switch (cookies) { - case ENABLE_WHEN_POSSIBLE, ENABLE -> null; + private @Nullable HttpCookieStore asCookieStore(HttpCookieHandling cookieHandling) { + return switch (cookieHandling) { + case ENABLE_WHEN_POSSIBLE, ENABLE -> new HttpCookieStore.Default(); case DISABLE -> new HttpCookieStore.Empty(); }; } diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java index e6287613460..c004e316210 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java @@ -109,6 +109,9 @@ public final class ReactorHttpClientBuilder { .orFrom(() -> HttpRedirects.FOLLOW_WHEN_POSSIBLE) .as(this::followRedirects) .to(httpClient, HttpClient::followRedirect); + if (HttpCookieHandling.ENABLE.equals(settings.cookieHandling())) { + throw new IllegalArgumentException("Reactor Netty HTTP client does not support cookie handling"); + } httpClient = map.from(settings::sslBundle).to(httpClient, this::secure); return this.customizer.apply(httpClient); } diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilder.java index c0d57900704..25ae283d228 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilder.java @@ -78,6 +78,10 @@ final class ReflectiveComponentsClientHttpRequestFactoryBuilder setConnectTimeout(unwrapped, connectTimeout)); diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilder.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilder.java index 80764d2ea76..fb9040a46ac 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilder.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilder.java @@ -78,6 +78,9 @@ public final class SimpleClientHttpRequestFactoryBuilder @Override protected SimpleClientHttpRequestFactory createClientHttpRequestFactory(HttpClientSettings settings) { + if (settings.cookieHandling() == HttpCookieHandling.ENABLE) { + throw new IllegalArgumentException("Simple HTTP request factory does not support HTTP cookie handling"); + } SslBundle sslBundle = settings.sslBundle(); SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpsRequestFactory(settings); Assert.state(sslBundle == null || !sslBundle.getOptions().isSpecified(), diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsProperties.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsProperties.java index 888feef654f..99760946155 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsProperties.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsProperties.java @@ -21,6 +21,7 @@ import java.time.Duration; import org.jspecify.annotations.Nullable; import org.springframework.boot.http.client.HttpClientSettings; +import org.springframework.boot.http.client.HttpCookieHandling; import org.springframework.boot.http.client.HttpRedirects; /** @@ -47,6 +48,11 @@ public abstract class HttpClientSettingsProperties { */ private @Nullable Duration readTimeout; + /** + * Handling for HTTP cookies. + */ + private @Nullable HttpCookieHandling cookieHandling; + /** * Default SSL configuration for a client HTTP request. */ @@ -76,6 +82,14 @@ public abstract class HttpClientSettingsProperties { this.readTimeout = readTimeout; } + public @Nullable HttpCookieHandling getCookieHandling() { + return this.cookieHandling; + } + + public void setCookieHandling(@Nullable HttpCookieHandling cookieHandling) { + this.cookieHandling = cookieHandling; + } + public Ssl getSsl() { return this.ssl; } diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapper.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapper.java index 0a0e8feeb08..86802c47258 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapper.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapper.java @@ -49,6 +49,7 @@ public class HttpClientSettingsPropertyMapper { settings = map.from(properties::getRedirects).to(settings, HttpClientSettings::withRedirects); settings = map.from(properties::getConnectTimeout).to(settings, HttpClientSettings::withConnectTimeout); settings = map.from(properties::getReadTimeout).to(settings, HttpClientSettings::withReadTimeout); + settings = map.from(properties::getCookieHandling).to(settings, HttpClientSettings::withCookieHandling); settings = map.from(properties::getSsl) .as(HttpClientSettingsProperties.Ssl::getBundle) .as(this::getSslBundle) diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java index c7b4d1af170..58d55443466 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpClientSettingsTests.java @@ -48,7 +48,7 @@ class HttpClientSettingsTests { @Test void createWithNulls() { HttpClientSettings settings = new HttpClientSettings(null, null, null, null, null); - assertThat(settings.cookies()).isNull(); + assertThat(settings.cookieHandling()).isNull(); assertThat(settings.redirects()).isNull(); assertThat(settings.connectTimeout()).isNull(); assertThat(settings.readTimeout()).isNull(); @@ -84,9 +84,9 @@ class HttpClientSettingsTests { } @Test - void withCookiesReturnsInstanceWithUpdatedCookies() { - HttpClientSettings settings = HttpClientSettings.defaults().withCookies(HttpCookies.DISABLE); - assertThat(settings.cookies()).isEqualTo(HttpCookies.DISABLE); + void withCookieHandlingReturnsInstanceWithUpdatedCookieHandling() { + HttpClientSettings settings = HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.DISABLE); + assertThat(settings.cookieHandling()).isEqualTo(HttpCookieHandling.DISABLE); assertThat(settings.redirects()).isNull(); assertThat(settings.connectTimeout()).isNull(); assertThat(settings.readTimeout()).isNull(); @@ -106,9 +106,9 @@ class HttpClientSettingsTests { void orElseReturnsNewInstanceWithUpdatedValues() { SslBundle sslBundle = mock(SslBundle.class); HttpClientSettings settings = new HttpClientSettings(null, null, ONE_SECOND, null, null) - .orElse(new HttpClientSettings(HttpCookies.ENABLE, HttpRedirects.FOLLOW_WHEN_POSSIBLE, TWO_SECONDS, + .orElse(new HttpClientSettings(HttpCookieHandling.ENABLE, HttpRedirects.FOLLOW_WHEN_POSSIBLE, TWO_SECONDS, TWO_SECONDS, sslBundle)); - assertThat(settings.cookies()).isEqualTo(HttpCookies.ENABLE); + assertThat(settings.cookieHandling()).isEqualTo(HttpCookieHandling.ENABLE); assertThat(settings.redirects()).isEqualTo(HttpRedirects.FOLLOW_WHEN_POSSIBLE); assertThat(settings.connectTimeout()).isEqualTo(ONE_SECOND); assertThat(settings.readTimeout()).isEqualTo(TWO_SECONDS); diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpComponentsClientHttpRequestFactoryBuilderTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpComponentsClientHttpRequestFactoryBuilderTests.java index 285a7b1a9bd..ba2ef777ebf 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpComponentsClientHttpRequestFactoryBuilderTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/HttpComponentsClientHttpRequestFactoryBuilderTests.java @@ -22,12 +22,15 @@ import java.util.List; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.io.SocketConfig; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.springframework.boot.http.client.HttpComponentsHttpClientBuilder.TlsSocketStrategyFactory; import org.springframework.boot.ssl.SslBundle; @@ -102,6 +105,28 @@ class HttpComponentsClientHttpRequestFactoryBuilderTests customizer.assertCalled(); } + @Test + void defaultCookieHandling() { + HttpComponentsClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.httpComponents() + .build(HttpClientSettings.defaults()); + assertThat(factory).extracting("httpClient.defaultConfig.cookieSpec").isNull(); + } + + @Test + void cookieHandlingDisabled() { + HttpComponentsClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.httpComponents() + .build(HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.DISABLE)); + assertThat(factory).extracting("httpClient.defaultConfig.cookieSpec").isEqualTo(StandardCookieSpec.IGNORE); + } + + @ParameterizedTest + @EnumSource(names = { "ENABLE", "ENABLE_WHEN_POSSIBLE" }) + void cookieHandlingEnabled(HttpCookieHandling cookieHandling) { + HttpComponentsClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.httpComponents() + .build(HttpClientSettings.defaults().withCookieHandling(cookieHandling)); + assertThat(factory).extracting("httpClient.defaultConfig.cookieSpec").isEqualTo(StandardCookieSpec.STRICT); + } + @Override protected long connectTimeout(HttpComponentsClientHttpRequestFactory requestFactory) { return getConnectorConfig(requestFactory).getConnectTimeout().toMilliseconds(); diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JdkClientHttpRequestFactoryBuilderTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JdkClientHttpRequestFactoryBuilderTests.java index 41c7b9f7720..2f240492cc2 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JdkClientHttpRequestFactoryBuilderTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JdkClientHttpRequestFactoryBuilderTests.java @@ -21,6 +21,8 @@ import java.time.Duration; import java.util.concurrent.Executor; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.http.client.JdkClientHttpRequestFactory; @@ -68,6 +70,34 @@ class JdkClientHttpRequestFactoryBuilderTests customizer.assertCalled(); } + @Test + void defaultCookieHandling() { + JdkClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.jdk() + .build(HttpClientSettings.defaults()); + HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(factory, "httpClient"); + assertThat(httpClient).isNotNull(); + assertThat(httpClient.cookieHandler()).isEmpty(); + } + + @Test + void cookieHandlingDisabled() { + JdkClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.jdk() + .build(HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.DISABLE)); + HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(factory, "httpClient"); + assertThat(httpClient).isNotNull(); + assertThat(httpClient.cookieHandler()).isEmpty(); + } + + @ParameterizedTest + @EnumSource(names = { "ENABLE", "ENABLE_WHEN_POSSIBLE" }) + void cookieHandlingEnabled(HttpCookieHandling cookieHandling) { + JdkClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.jdk() + .build(HttpClientSettings.defaults().withCookieHandling(cookieHandling)); + HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(factory, "httpClient"); + assertThat(httpClient).isNotNull(); + assertThat(httpClient.cookieHandler()).isNotEmpty(); + } + @Override protected long connectTimeout(JdkClientHttpRequestFactory requestFactory) { HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient"); diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JettyClientHttpRequestFactoryBuilderTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JettyClientHttpRequestFactoryBuilderTests.java index bc29518ef7e..66b8636b551 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JettyClientHttpRequestFactoryBuilderTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/JettyClientHttpRequestFactoryBuilderTests.java @@ -19,8 +19,11 @@ package org.springframework.boot.http.client; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; +import org.eclipse.jetty.http.HttpCookieStore; import org.eclipse.jetty.io.ClientConnector; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.springframework.http.client.JettyClientHttpRequestFactory; import org.springframework.test.util.ReflectionTestUtils; @@ -75,6 +78,28 @@ class JettyClientHttpRequestFactoryBuilderTests .isInstanceOf(TestHttpClientTransport.class); } + @Test + void defaultCookieHandling() { + JettyClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.jetty() + .build(HttpClientSettings.defaults()); + assertThat(factory).extracting("httpClient.cookieStore").isNull(); + } + + @Test + void cookieHandlingDisabled() { + JettyClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.jetty() + .build(HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.DISABLE)); + assertThat(factory).extracting("httpClient.cookieStore").isInstanceOf(HttpCookieStore.Empty.class); + } + + @ParameterizedTest + @EnumSource(names = { "ENABLE", "ENABLE_WHEN_POSSIBLE" }) + void cookieHandlingEnabled(HttpCookieHandling cookieHandling) { + JettyClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.jetty() + .build(HttpClientSettings.defaults().withCookieHandling(cookieHandling)); + assertThat(factory).extracting("httpClient.cookieStore").isInstanceOf(HttpCookieStore.Default.class); + } + @Override protected long connectTimeout(JettyClientHttpRequestFactory requestFactory) { HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient"); diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReactorClientHttpRequestFactoryBuilderTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReactorClientHttpRequestFactoryBuilderTests.java index 6ae0e4676d6..2a60d2cd6b8 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReactorClientHttpRequestFactoryBuilderTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReactorClientHttpRequestFactoryBuilderTests.java @@ -24,6 +24,8 @@ import java.util.function.UnaryOperator; import io.netty.channel.ChannelOption; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import reactor.netty.http.client.HttpClient; import org.springframework.http.client.ReactorClientHttpRequestFactory; @@ -31,6 +33,8 @@ import org.springframework.http.client.ReactorResourceFactory; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.spy; @@ -98,6 +102,19 @@ class ReactorClientHttpRequestFactoryBuilderTests assertThat(called).containsExactly(true); } + @ParameterizedTest + @EnumSource(names = { "DISABLE", "ENABLE_WHEN_POSSIBLE" }) + void doesNotThrowWhenCookieHandlingNotEnabled(HttpCookieHandling cookieHandling) { + assertThatNoException().isThrownBy(() -> ClientHttpRequestFactoryBuilder.reactor() + .build(HttpClientSettings.defaults().withCookieHandling(cookieHandling))); + } + + @Test + void throwsWhenCookieHandlingEnabled() { + assertThatIllegalArgumentException().isThrownBy(() -> ClientHttpRequestFactoryBuilder.reactor() + .build(HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.ENABLE))); + } + @Override protected long connectTimeout(ReactorClientHttpRequestFactory requestFactory) { HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient"); diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilderTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilderTests.java index a90897c5758..f328d0af04d 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilderTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/ReflectiveComponentsClientHttpRequestFactoryBuilderTests.java @@ -67,6 +67,20 @@ class ReflectiveComponentsClientHttpRequestFactoryBuilderTests .withMessage("Unable to set redirect follow using reflection"); } + @Test + void cookieHandlingEnable() { + HttpClientSettings settings = HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.ENABLE); + assertThatIllegalStateException().isThrownBy(() -> ofTestRequestFactory().build(settings)) + .withMessage("Unable to set HTTP cookie handling using reflection"); + } + + @Test + void cookieHandlingDisable() { + HttpClientSettings settings = HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.DISABLE); + assertThatIllegalStateException().isThrownBy(() -> ofTestRequestFactory().build(settings)) + .withMessage("Unable to set HTTP cookie handling using reflection"); + } + @Override void connectWithSslBundleAndOptionsMismatch(String httpMethod) throws Exception { assertThatIllegalStateException().isThrownBy(() -> super.connectWithSslBundleAndOptionsMismatch(httpMethod)) diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilderTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilderTests.java index 1b134981adf..f23d1f91fcc 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilderTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/SimpleClientHttpRequestFactoryBuilderTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.http.client; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -25,6 +26,7 @@ import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** @@ -81,6 +83,12 @@ class SimpleClientHttpRequestFactoryBuilderTests super.redirectDontFollow(httpMethod); } + @Test + void throwsWhenCookieHandlingEnabled() { + assertThatIllegalArgumentException().isThrownBy(() -> ClientHttpRequestFactoryBuilder.simple() + .build(HttpClientSettings.defaults().withCookieHandling(HttpCookieHandling.ENABLE))); + } + @Override protected HttpStatus getExpectedRedirect(HttpMethod httpMethod) { return (httpMethod != HttpMethod.GET) ? HttpStatus.FOUND : HttpStatus.OK; diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java index e6d0b8b7782..54f5f059a2e 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/HttpClientSettingsPropertyMapperTests.java @@ -21,6 +21,7 @@ import java.time.Duration; import org.junit.jupiter.api.Test; import org.springframework.boot.http.client.HttpClientSettings; +import org.springframework.boot.http.client.HttpCookieHandling; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; @@ -79,6 +80,15 @@ class HttpClientSettingsPropertyMapperTests { assertThat(result.readTimeout()).isEqualTo(Duration.ofSeconds(30)); } + @Test + void mapMapsCookieHandling() { + HttpClientSettingsPropertyMapper mapper = new HttpClientSettingsPropertyMapper(null, null); + TestHttpClientSettingsProperties properties = new TestHttpClientSettingsProperties(); + properties.setCookieHandling(HttpCookieHandling.DISABLE); + HttpClientSettings result = mapper.map(properties); + assertThat(result.cookieHandling()).isEqualTo(HttpCookieHandling.DISABLE); + } + @Test void mapMapsSslBundle() { SslBundle sslBundle = mock(SslBundle.class); diff --git a/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java b/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java index 508e349dca8..27abdf5450a 100644 --- a/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java +++ b/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/RestTemplateBuilder.java @@ -35,7 +35,7 @@ import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; -import org.springframework.boot.http.client.HttpCookies; +import org.springframework.boot.http.client.HttpCookieHandling; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.ssl.SslBundle; import org.springframework.http.client.ClientHttpRequest; @@ -485,15 +485,15 @@ public class RestTemplateBuilder { /** * Sets the cookie handling strategy on the underlying * {@link ClientHttpRequestFactory}. - * @param cookies the cookie handling strategy + * @param cookieHandling the cookie handling strategy * @return a new builder instance. * @since 4.1.0 */ - public RestTemplateBuilder cookies(HttpCookies cookies) { - return new RestTemplateBuilder(this.clientSettings.withCookies(cookies), this.detectRequestFactory, - this.rootUri, this.messageConverters, this.interceptors, this.requestFactoryBuilder, - this.uriTemplateHandler, this.errorHandler, this.basicAuthentication, this.defaultHeaders, - this.customizers, this.requestCustomizers); + public RestTemplateBuilder cookieHandling(HttpCookieHandling cookieHandling) { + return new RestTemplateBuilder(this.clientSettings.withCookieHandling(cookieHandling), + this.detectRequestFactory, this.rootUri, this.messageConverters, this.interceptors, + this.requestFactoryBuilder, this.uriTemplateHandler, this.errorHandler, this.basicAuthentication, + this.defaultHeaders, this.customizers, this.requestCustomizers); } /** diff --git a/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java b/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java index ef63db42d43..52ebd28f409 100644 --- a/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java +++ b/module/spring-boot-resttestclient/src/main/java/org/springframework/boot/resttestclient/TestRestTemplate.java @@ -41,7 +41,7 @@ import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.HttpComponentsClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpComponentsHttpClientBuilder.TlsSocketStrategyFactory; -import org.springframework.boot.http.client.HttpCookies; +import org.springframework.boot.http.client.HttpCookieHandling; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; @@ -164,8 +164,7 @@ public class TestRestTemplate { private static HttpComponentsClientHttpRequestFactoryBuilder applyHttpClientOptions( HttpComponentsClientHttpRequestFactoryBuilder builder, HttpClientOption[] httpClientOptions) { if (HttpClientOption.ENABLE_COOKIES.isPresent(httpClientOptions)) { - builder = builder.withDefaultRequestConfigCustomizer( - new CookieSpecCustomizer(true)); + builder = builder.withDefaultRequestConfigCustomizer(new CookieSpecCustomizer(true)); } if (HttpClientOption.SSL.isPresent(httpClientOptions)) { builder = builder.withTlsSocketStrategyFactory(new SelfSignedTlsSocketStrategyFactory()); @@ -979,15 +978,15 @@ public class TestRestTemplate { /** * Creates a new {@code TestRestTemplate} with the same configuration as this one, - * except that it will apply the given {@link HttpCookies}. The request factory used is - * a new instance of the underlying {@link RestTemplate}'s request factory type (when - * possible). - * @param cookies the new cookie settings + * except that it will apply the given {@link HttpCookieHandling}. The request factory + * used is a new instance of the underlying {@link RestTemplate}'s request factory + * type (when possible). + * @param cookieHandling the new cookie handling * @return the new template * @since 4.1.0 */ - public TestRestTemplate withCookies(HttpCookies cookies) { - return withClientSettings((settings) -> settings.withCookies(cookies)); + public TestRestTemplate withCookieHandling(HttpCookieHandling cookieHandling) { + return withClientSettings((settings) -> settings.withCookieHandling(cookieHandling)); } /** @@ -1053,7 +1052,7 @@ public class TestRestTemplate { /** * Enable cookies. * @deprecated since 4.1.0 for removal in 4.3.0 in favor of - * {@link TestRestTemplate#withCookies(HttpCookies)} + * {@link TestRestTemplate#withCookieHandling(HttpCookieHandling)} */ @Deprecated(since = "4.1.0", forRemoval = true) ENABLE_COOKIES, diff --git a/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java b/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java index f835ec95951..b621b1e018f 100644 --- a/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java +++ b/module/spring-boot-resttestclient/src/test/java/org/springframework/boot/resttestclient/TestRestTemplateTests.java @@ -26,6 +26,7 @@ import java.util.Base64; import java.util.stream.Stream; import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.classic.RedirectExec; import org.apache.hc.client5.http.protocol.RedirectStrategy; @@ -35,7 +36,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; -import org.springframework.boot.http.client.HttpCookies; +import org.springframework.boot.http.client.HttpCookieHandling; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.boot.resttestclient.TestRestTemplate.HttpClientOption; @@ -154,11 +155,16 @@ class TestRestTemplateTests { } @Test - void withCookies() { + void withCookieHandling() { TestRestTemplate template = new TestRestTemplate(); assertThat(getRequestConfig(template).getCookieSpec()).isNull(); - assertThat(getRequestConfig(template.withCookies(HttpCookies.ENABLE)).getCookieSpec()).isEqualTo("strict"); - assertThat(getRequestConfig(template.withCookies(HttpCookies.DISABLE)).getCookieSpec()).isEqualTo("ignoreCookies"); + assertThat(getRequestConfig(template.withCookieHandling(HttpCookieHandling.ENABLE)).getCookieSpec()) + .isEqualTo(StandardCookieSpec.STRICT); + assertThat( + getRequestConfig(template.withCookieHandling(HttpCookieHandling.ENABLE_WHEN_POSSIBLE)).getCookieSpec()) + .isEqualTo(StandardCookieSpec.STRICT); + assertThat(getRequestConfig(template.withCookieHandling(HttpCookieHandling.DISABLE)).getCookieSpec()) + .isEqualTo(StandardCookieSpec.IGNORE); } @Test