Polish "Update TestRestTemplate's default cookie handling to match RestTemplate's"

See gh-49261

Signed-off-by: Andy Wilkinson <andy.wilkinson@broadcom.com>
This commit is contained in:
Andy Wilkinson
2026-03-12 13:05:21 +00:00
parent 085d522570
commit 5721f57acd
23 changed files with 225 additions and 98 deletions
@@ -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:
@@ -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);
}
/**
@@ -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
* <a href="https://hc.apache.org/httpcomponents-client-ga/">Apache HttpComponents</a>
* 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;
};
}
}
@@ -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);
@@ -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
@@ -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;
};
@@ -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();
};
}
@@ -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);
}
@@ -78,6 +78,10 @@ final class ReflectiveComponentsClientHttpRequestFactoryBuilder<T extends Client
Assert.state(settings.sslBundle() == null, "Unable to set SSL bundle using reflection");
Assert.state(settings.redirects() == null || settings.redirects() == HttpRedirects.FOLLOW_WHEN_POSSIBLE,
"Unable to set redirect follow using reflection");
Assert.state(
settings.cookieHandling() == null
|| settings.cookieHandling() == HttpCookieHandling.ENABLE_WHEN_POSSIBLE,
"Unable to set HTTP cookie handling using reflection");
ClientHttpRequestFactory unwrapped = unwrapRequestFactoryIfNecessary(requestFactory);
PropertyMapper map = PropertyMapper.get();
map.from(settings::connectTimeout).to((connectTimeout) -> setConnectTimeout(unwrapped, connectTimeout));
@@ -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(),
@@ -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;
}
@@ -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)
@@ -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);
@@ -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();
@@ -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");
@@ -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");
@@ -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");
@@ -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))
@@ -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;
@@ -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);
@@ -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);
}
/**
@@ -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,
@@ -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