Deprecate support for OkHttp

Closes gh-36632
This commit is contained in:
Stephane Nicoll 2023-08-01 16:16:13 +02:00
parent 19859a9023
commit 191ac10009
11 changed files with 76 additions and 49 deletions

View File

@ -181,8 +181,8 @@ Spring Boot will auto-detect which HTTP client to use with `RestClient` and `Res
In order of preference, the following clients are supported: In order of preference, the following clients are supported:
. Apache HttpClient . Apache HttpClient
. OkHttp
. Jetty HttpClient . Jetty HttpClient
. OkHttp (deprecated)
. Simple JDK client (`HttpURLConnection`) . Simple JDK client (`HttpURLConnection`)
If multiple clients are available on the classpath, the most preferred client will be used. If multiple clients are available on the classpath, the most preferred client will be used.

View File

@ -38,7 +38,7 @@ import org.springframework.http.RequestEntity;
import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.client.JettyClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.http.client.MockClientHttpRequest; import org.springframework.mock.http.client.MockClientHttpRequest;
@ -86,15 +86,16 @@ class TestRestTemplateTests {
@Test @Test
void doNotReplaceCustomRequestFactory() { void doNotReplaceCustomRequestFactory() {
RestTemplateBuilder builder = new RestTemplateBuilder().requestFactory(OkHttp3ClientHttpRequestFactory.class); RestTemplateBuilder builder = new RestTemplateBuilder()
.requestFactory(HttpComponentsClientHttpRequestFactory.class);
TestRestTemplate testRestTemplate = new TestRestTemplate(builder); TestRestTemplate testRestTemplate = new TestRestTemplate(builder);
assertThat(testRestTemplate.getRestTemplate().getRequestFactory()) assertThat(testRestTemplate.getRestTemplate().getRequestFactory())
.isInstanceOf(OkHttp3ClientHttpRequestFactory.class); .isInstanceOf(HttpComponentsClientHttpRequestFactory.class);
} }
@Test @Test
void useTheSameRequestFactoryClassWithBasicAuth() { void useTheSameRequestFactoryClassWithBasicAuth() {
OkHttp3ClientHttpRequestFactory customFactory = new OkHttp3ClientHttpRequestFactory(); JettyClientHttpRequestFactory customFactory = new JettyClientHttpRequestFactory();
RestTemplateBuilder builder = new RestTemplateBuilder().requestFactory(() -> customFactory); RestTemplateBuilder builder = new RestTemplateBuilder().requestFactory(() -> customFactory);
TestRestTemplate testRestTemplate = new TestRestTemplate(builder).withBasicAuth("test", "test"); TestRestTemplate testRestTemplate = new TestRestTemplate(builder).withBasicAuth("test", "test");
RestTemplate restTemplate = testRestTemplate.getRestTemplate(); RestTemplate restTemplate = testRestTemplate.getRestTemplate();

View File

@ -89,24 +89,25 @@ public final class ClientHttpRequestFactories {
* dependencies {@link ClassUtils#isPresent are available} is returned: * dependencies {@link ClassUtils#isPresent are available} is returned:
* <ol> * <ol>
* <li>{@link HttpComponentsClientHttpRequestFactory}</li> * <li>{@link HttpComponentsClientHttpRequestFactory}</li>
* <li>{@link OkHttp3ClientHttpRequestFactory}</li>
* <li>{@link JettyClientHttpRequestFactory}</li> * <li>{@link JettyClientHttpRequestFactory}</li>
* <li>{@link OkHttp3ClientHttpRequestFactory} (deprecated)</li>
* <li>{@link SimpleClientHttpRequestFactory}</li> * <li>{@link SimpleClientHttpRequestFactory}</li>
* </ol> * </ol>
* @param settings the settings to apply * @param settings the settings to apply
* @return a new {@link ClientHttpRequestFactory} * @return a new {@link ClientHttpRequestFactory}
*/ */
@SuppressWarnings("removal")
public static ClientHttpRequestFactory get(ClientHttpRequestFactorySettings settings) { public static ClientHttpRequestFactory get(ClientHttpRequestFactorySettings settings) {
Assert.notNull(settings, "Settings must not be null"); Assert.notNull(settings, "Settings must not be null");
if (APACHE_HTTP_CLIENT_PRESENT) { if (APACHE_HTTP_CLIENT_PRESENT) {
return HttpComponents.get(settings); return HttpComponents.get(settings);
} }
if (OKHTTP_CLIENT_PRESENT) {
return OkHttp.get(settings);
}
if (JETTY_CLIENT_PRESENT) { if (JETTY_CLIENT_PRESENT) {
return Jetty.get(settings); return Jetty.get(settings);
} }
if (OKHTTP_CLIENT_PRESENT) {
return OkHttp.get(settings);
}
return Simple.get(settings); return Simple.get(settings);
} }
@ -119,7 +120,7 @@ public final class ClientHttpRequestFactories {
* <li>{@link HttpComponentsClientHttpRequestFactory}</li> * <li>{@link HttpComponentsClientHttpRequestFactory}</li>
* <li>{@link JdkClientHttpRequestFactory}</li> * <li>{@link JdkClientHttpRequestFactory}</li>
* <li>{@link JettyClientHttpRequestFactory}</li> * <li>{@link JettyClientHttpRequestFactory}</li>
* <li>{@link OkHttp3ClientHttpRequestFactory}</li> * <li>{@link OkHttp3ClientHttpRequestFactory} (deprecated)</li>
* <li>{@link SimpleClientHttpRequestFactory}</li> * <li>{@link SimpleClientHttpRequestFactory}</li>
* </ul> * </ul>
* A {@code requestFactoryType} of {@link ClientHttpRequestFactory} is equivalent to * A {@code requestFactoryType} of {@link ClientHttpRequestFactory} is equivalent to
@ -129,7 +130,7 @@ public final class ClientHttpRequestFactories {
* @param settings the settings to apply * @param settings the settings to apply
* @return a new {@link ClientHttpRequestFactory} instance * @return a new {@link ClientHttpRequestFactory} instance
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings({ "unchecked", "removal" })
public static <T extends ClientHttpRequestFactory> T get(Class<T> requestFactoryType, public static <T extends ClientHttpRequestFactory> T get(Class<T> requestFactoryType,
ClientHttpRequestFactorySettings settings) { ClientHttpRequestFactorySettings settings) {
Assert.notNull(settings, "Settings must not be null"); Assert.notNull(settings, "Settings must not be null");
@ -139,9 +140,6 @@ public final class ClientHttpRequestFactories {
if (requestFactoryType == HttpComponentsClientHttpRequestFactory.class) { if (requestFactoryType == HttpComponentsClientHttpRequestFactory.class) {
return (T) HttpComponents.get(settings); return (T) HttpComponents.get(settings);
} }
if (requestFactoryType == OkHttp3ClientHttpRequestFactory.class) {
return (T) OkHttp.get(settings);
}
if (requestFactoryType == JettyClientHttpRequestFactory.class) { if (requestFactoryType == JettyClientHttpRequestFactory.class) {
return (T) Jetty.get(settings); return (T) Jetty.get(settings);
} }
@ -151,6 +149,9 @@ public final class ClientHttpRequestFactories {
if (requestFactoryType == SimpleClientHttpRequestFactory.class) { if (requestFactoryType == SimpleClientHttpRequestFactory.class) {
return (T) Simple.get(settings); return (T) Simple.get(settings);
} }
if (requestFactoryType == OkHttp3ClientHttpRequestFactory.class) {
return (T) OkHttp.get(settings);
}
return get(() -> createRequestFactory(requestFactoryType), settings); return get(() -> createRequestFactory(requestFactoryType), settings);
} }
@ -220,6 +221,8 @@ public final class ClientHttpRequestFactories {
/** /**
* Support for {@link OkHttp3ClientHttpRequestFactory}. * Support for {@link OkHttp3ClientHttpRequestFactory}.
*/ */
@Deprecated(since = "3.2.0", forRemoval = true)
@SuppressWarnings("removal")
static class OkHttp { static class OkHttp {
static OkHttp3ClientHttpRequestFactory get(ClientHttpRequestFactorySettings settings) { static OkHttp3ClientHttpRequestFactory get(ClientHttpRequestFactorySettings settings) {

View File

@ -56,10 +56,6 @@ class ClientHttpRequestFactoriesRuntimeHints implements RuntimeHintsRegistrar {
typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.APACHE_HTTP_CLIENT_CLASS)); typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.APACHE_HTTP_CLIENT_CLASS));
registerReflectionHints(hints, HttpComponentsClientHttpRequestFactory.class); registerReflectionHints(hints, HttpComponentsClientHttpRequestFactory.class);
}); });
hints.registerTypeIfPresent(classLoader, ClientHttpRequestFactories.OKHTTP_CLIENT_CLASS, (typeHint) -> {
typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.OKHTTP_CLIENT_CLASS));
registerReflectionHints(hints, OkHttp3ClientHttpRequestFactory.class);
});
hints.registerTypeIfPresent(classLoader, ClientHttpRequestFactories.JETTY_CLIENT_CLASS, (typeHint) -> { hints.registerTypeIfPresent(classLoader, ClientHttpRequestFactories.JETTY_CLIENT_CLASS, (typeHint) -> {
typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.JETTY_CLIENT_CLASS)); typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.JETTY_CLIENT_CLASS));
registerReflectionHints(hints, JettyClientHttpRequestFactory.class, long.class); registerReflectionHints(hints, JettyClientHttpRequestFactory.class, long.class);
@ -68,6 +64,17 @@ class ClientHttpRequestFactoriesRuntimeHints implements RuntimeHintsRegistrar {
typeHint.onReachableType(HttpURLConnection.class); typeHint.onReachableType(HttpURLConnection.class);
registerReflectionHints(hints, SimpleClientHttpRequestFactory.class); registerReflectionHints(hints, SimpleClientHttpRequestFactory.class);
}); });
registerOkHttpHints(hints, classLoader);
}
@SuppressWarnings("removal")
@Deprecated(since = "3.2.0", forRemoval = true)
private void registerOkHttpHints(ReflectionHints hints, ClassLoader classLoader) {
hints.registerTypeIfPresent(classLoader, ClientHttpRequestFactories.OKHTTP_CLIENT_CLASS, (typeHint) -> {
typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.OKHTTP_CLIENT_CLASS));
registerReflectionHints(hints, OkHttp3ClientHttpRequestFactory.class);
});
} }
private void registerReflectionHints(ReflectionHints hints, private void registerReflectionHints(ReflectionHints hints,

View File

@ -35,7 +35,9 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
@ClassPathOverrides("com.squareup.okhttp3:okhttp:3.14.9") @ClassPathOverrides("com.squareup.okhttp3:okhttp:3.14.9")
@ClassPathExclusions("httpclient5-*.jar") @ClassPathExclusions({ "httpclient5-*.jar", "jetty-client-*.jar" })
@Deprecated(since = "3.2.0")
@SuppressWarnings("removal")
class ClientHttpRequestFactoriesOkHttp3Tests class ClientHttpRequestFactoriesOkHttp3Tests
extends AbstractClientHttpRequestFactoriesTests<OkHttp3ClientHttpRequestFactory> { extends AbstractClientHttpRequestFactoriesTests<OkHttp3ClientHttpRequestFactory> {

View File

@ -33,7 +33,9 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
@ClassPathExclusions("httpclient5-*.jar") @ClassPathExclusions({ "httpclient5-*.jar", "jetty-client-*.jar" })
@Deprecated(since = "3.2.0")
@SuppressWarnings("removal")
class ClientHttpRequestFactoriesOkHttp4Tests class ClientHttpRequestFactoriesOkHttp4Tests
extends AbstractClientHttpRequestFactoriesTests<OkHttp3ClientHttpRequestFactory> { extends AbstractClientHttpRequestFactoriesTests<OkHttp3ClientHttpRequestFactory> {

View File

@ -63,6 +63,8 @@ class ClientHttpRequestFactoriesRuntimeHintsTests {
} }
@Test @Test
@Deprecated(since = "3.2.0")
@SuppressWarnings("removal")
void shouldRegisterOkHttpHints() { void shouldRegisterOkHttpHints() {
RuntimeHints hints = new RuntimeHints(); RuntimeHints hints = new RuntimeHints();
new ClientHttpRequestFactoriesRuntimeHints().registerHints(hints, getClass().getClassLoader()); new ClientHttpRequestFactoriesRuntimeHints().registerHints(hints, getClass().getClassLoader());

View File

@ -70,6 +70,8 @@ class ClientHttpRequestFactoriesTests {
} }
@Test @Test
@Deprecated(since = "3.2.0")
@SuppressWarnings("removal")
void getOfOkHttpFactoryReturnsOkHttpFactory() { void getOfOkHttpFactoryReturnsOkHttpFactory() {
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(OkHttp3ClientHttpRequestFactory.class, ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(OkHttp3ClientHttpRequestFactory.class,
ClientHttpRequestFactorySettings.DEFAULTS); ClientHttpRequestFactorySettings.DEFAULTS);

View File

@ -18,12 +18,12 @@ package org.springframework.boot.webservices.client;
import java.time.Duration; import java.time.Duration;
import okhttp3.OkHttpClient; import org.eclipse.jetty.client.HttpClient;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.client.JettyClientHttpRequestFactory;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.ws.transport.WebServiceMessageSender; import org.springframework.ws.transport.WebServiceMessageSender;
import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; import org.springframework.ws.transport.http.ClientHttpRequestMessageSender;
@ -42,9 +42,9 @@ class HttpWebServiceMessageSenderBuilderOkHttp3IntegrationTests {
private final HttpWebServiceMessageSenderBuilder builder = new HttpWebServiceMessageSenderBuilder(); private final HttpWebServiceMessageSenderBuilder builder = new HttpWebServiceMessageSenderBuilder();
@Test @Test
void buildUseOkHttp3ByDefault() { void buildUseJettyClientIfHttpComponentsIsNotAvailable() {
WebServiceMessageSender messageSender = this.builder.build(); WebServiceMessageSender messageSender = this.builder.build();
assertOkHttp3RequestFactory(messageSender); assertJettyClientHttpRequestFactory(messageSender);
} }
@Test @Test
@ -52,19 +52,19 @@ class HttpWebServiceMessageSenderBuilderOkHttp3IntegrationTests {
WebServiceMessageSender messageSender = this.builder.setConnectTimeout(Duration.ofSeconds(5)) WebServiceMessageSender messageSender = this.builder.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(2))
.build(); .build();
OkHttp3ClientHttpRequestFactory factory = assertOkHttp3RequestFactory(messageSender); JettyClientHttpRequestFactory factory = assertJettyClientHttpRequestFactory(messageSender);
OkHttpClient client = (OkHttpClient) ReflectionTestUtils.getField(factory, "client"); HttpClient client = (HttpClient) ReflectionTestUtils.getField(factory, "httpClient");
assertThat(client).isNotNull(); assertThat(client).isNotNull();
assertThat(client.connectTimeoutMillis()).isEqualTo(5000); assertThat(client.getConnectTimeout()).isEqualTo(5000);
assertThat(client.readTimeoutMillis()).isEqualTo(2000); assertThat(factory).hasFieldOrPropertyWithValue("readTimeout", 2000L);
} }
private OkHttp3ClientHttpRequestFactory assertOkHttp3RequestFactory(WebServiceMessageSender messageSender) { private JettyClientHttpRequestFactory assertJettyClientHttpRequestFactory(WebServiceMessageSender messageSender) {
assertThat(messageSender).isInstanceOf(ClientHttpRequestMessageSender.class); assertThat(messageSender).isInstanceOf(ClientHttpRequestMessageSender.class);
ClientHttpRequestMessageSender sender = (ClientHttpRequestMessageSender) messageSender; ClientHttpRequestMessageSender sender = (ClientHttpRequestMessageSender) messageSender;
ClientHttpRequestFactory requestFactory = sender.getRequestFactory(); ClientHttpRequestFactory requestFactory = sender.getRequestFactory();
assertThat(requestFactory).isInstanceOf(OkHttp3ClientHttpRequestFactory.class); assertThat(requestFactory).isInstanceOf(JettyClientHttpRequestFactory.class);
return (OkHttp3ClientHttpRequestFactory) requestFactory; return (JettyClientHttpRequestFactory) requestFactory;
} }
} }

View File

@ -13,9 +13,9 @@ dependencies {
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testImplementation(project(":spring-boot-project:spring-boot-testcontainers")) testImplementation(project(":spring-boot-project:spring-boot-testcontainers"))
testImplementation("com.squareup.okhttp3:okhttp")
testImplementation("io.projectreactor:reactor-core") testImplementation("io.projectreactor:reactor-core")
testImplementation("io.projectreactor:reactor-test") testImplementation("io.projectreactor:reactor-test")
testImplementation("org.apache.httpcomponents.client5:httpclient5")
testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.platform:junit-platform-engine") testImplementation("org.junit.platform:junit-platform-engine")
testImplementation("org.junit.platform:junit-platform-launcher") testImplementation("org.junit.platform:junit-platform-launcher")

View File

@ -17,13 +17,14 @@
package smoketest.data.couchbase; package smoketest.data.couchbase;
import java.time.Duration; import java.time.Duration;
import java.util.Base64;
import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.command.InspectContainerResponse;
import okhttp3.Credentials; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import okhttp3.OkHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients;
import okhttp3.Request; import org.apache.hc.core5.http.ClassicHttpRequest;
import okhttp3.RequestBody; import org.apache.hc.core5.http.HttpResponse;
import okhttp3.Response; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.testcontainers.couchbase.CouchbaseContainer; import org.testcontainers.couchbase.CouchbaseContainer;
import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.MountableFile;
@ -33,6 +34,7 @@ import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
* A {@link CouchbaseContainer} for Couchbase with SSL configuration. * A {@link CouchbaseContainer} for Couchbase with SSL configuration.
* *
* @author Scott Frederick * @author Scott Frederick
* @author Stephane Nicoll
*/ */
public class SecureCouchbaseContainer extends CouchbaseContainer { public class SecureCouchbaseContainer extends CouchbaseContainer {
@ -69,20 +71,26 @@ public class SecureCouchbaseContainer extends CouchbaseContainer {
} }
private void doHttpRequest(String path) { private void doHttpRequest(String path) {
Response response; HttpResponse response = post(path);
try { if (response.getCode() != 200) {
String url = "http://%s:%d/%s".formatted(getHost(), getMappedPort(MANAGEMENT_PORT), path);
Request.Builder requestBuilder = new Request.Builder().url(url)
.header("Authorization", Credentials.basic(ADMIN_USER, ADMIN_PASSWORD))
.post(RequestBody.create("".getBytes()));
response = new OkHttpClient().newCall(requestBuilder.build()).execute();
}
catch (Exception ex) {
throw new IllegalStateException("Error calling Couchbase HTTP endpoint", ex);
}
if (!response.isSuccessful()) {
throw new IllegalStateException("Error calling Couchbase HTTP endpoint: " + response); throw new IllegalStateException("Error calling Couchbase HTTP endpoint: " + response);
} }
} }
private HttpResponse post(String path) {
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
String basicAuth = "Basic "
+ Base64.getEncoder().encodeToString("%s:%s".formatted(ADMIN_USER, ADMIN_PASSWORD).getBytes());
String url = "http://%s:%d/%s".formatted(getHost(), getMappedPort(MANAGEMENT_PORT), path);
ClassicHttpRequest httpPost = ClassicRequestBuilder.post(url)
.addHeader("Authorization", basicAuth)
.setEntity("")
.build();
return httpclient.execute(httpPost, (response) -> response);
}
catch (Exception ex) {
throw new IllegalStateException("Error calling Couchbase HTTP endpoint", ex);
}
}
} }