Skip to content

Commit d1976a4

Browse files
committed
Upgrade to HttpClient5 5.4
Closes gh-42675
1 parent dc78bd4 commit d1976a4

File tree

7 files changed

+115
-132
lines changed

7 files changed

+115
-132
lines changed

spring-boot-project/spring-boot-dependencies/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ bom {
575575
]
576576
}
577577
}
578-
library("HttpClient5", "5.3.1") {
578+
library("HttpClient5", "5.4") {
579579
group("org.apache.httpcomponents.client5") {
580580
modules = [
581581
"httpclient5",

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
3939
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
4040
import org.apache.hc.client5.http.protocol.HttpClientContext;
41-
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
42-
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
41+
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
42+
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
4343
import org.apache.hc.core5.http.io.SocketConfig;
4444
import org.apache.hc.core5.http.protocol.HttpContext;
4545
import org.apache.hc.core5.http.ssl.TLS;
@@ -992,7 +992,7 @@ public enum HttpClientOption {
992992
ENABLE_REDIRECTS,
993993

994994
/**
995-
* Use a {@link SSLConnectionSocketFactory} that trusts self-signed certificates.
995+
* Use a {@link TlsSocketStrategy} that trusts self-signed certificates.
996996
*/
997997
SSL
998998

@@ -1038,7 +1038,7 @@ private PoolingHttpClientConnectionManager createConnectionManager(Duration read
10381038
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
10391039
PoolingHttpClientConnectionManagerBuilder builder = PoolingHttpClientConnectionManagerBuilder.create();
10401040
if (ssl) {
1041-
builder.setSSLSocketFactory(createSocketFactory());
1041+
builder.setTlsSocketStrategy(createTlsSocketStrategy());
10421042
}
10431043
if (readTimeout != null) {
10441044
SocketConfig socketConfig = SocketConfig.custom()
@@ -1049,14 +1049,12 @@ private PoolingHttpClientConnectionManager createConnectionManager(Duration read
10491049
return builder.build();
10501050
}
10511051

1052-
private SSLConnectionSocketFactory createSocketFactory()
1053-
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
1052+
private TlsSocketStrategy createTlsSocketStrategy()
1053+
throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
10541054
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy())
10551055
.build();
1056-
return SSLConnectionSocketFactoryBuilder.create()
1057-
.setSslContext(sslContext)
1058-
.setTlsVersions(TLS.V_1_3, TLS.V_1_2)
1059-
.build();
1056+
return new DefaultClientTlsStrategy(sslContext, new String[] { TLS.V_1_3.getId(), TLS.V_1_2.getId() }, null,
1057+
null, null);
10601058
}
10611059

10621060
@Override

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818

1919
import java.io.IOException;
2020
import java.net.InetAddress;
21-
import java.net.InetSocketAddress;
21+
import java.net.Proxy;
2222
import java.net.Socket;
23-
import java.net.UnknownHostException;
2423

2524
import com.sun.jna.Platform;
2625
import org.apache.hc.client5.http.DnsResolver;
@@ -30,12 +29,11 @@
3029
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
3130
import org.apache.hc.client5.http.impl.classic.HttpClients;
3231
import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager;
32+
import org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator;
33+
import org.apache.hc.client5.http.io.DetachedSocketFactory;
3334
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
3435
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
35-
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
3636
import org.apache.hc.core5.http.HttpHost;
37-
import org.apache.hc.core5.http.config.Registry;
38-
import org.apache.hc.core5.http.config.RegistryBuilder;
3937
import org.apache.hc.core5.http.protocol.HttpContext;
4038
import org.apache.hc.core5.util.TimeValue;
4139

@@ -48,6 +46,7 @@
4846
*
4947
* @author Phillip Webb
5048
* @author Scott Frederick
49+
* @author Moritz Halbritter
5150
*/
5251
final class LocalHttpClientTransport extends HttpClientTransport {
5352

@@ -62,9 +61,9 @@ private LocalHttpClientTransport(HttpClient client, HttpHost host) {
6261
}
6362

6463
static LocalHttpClientTransport create(ResolvedDockerHost dockerHost) {
65-
HttpClientBuilder builder = HttpClients.custom();
66-
builder.setConnectionManager(new LocalConnectionManager(dockerHost.getAddress()));
67-
builder.setRoutePlanner(new LocalRoutePlanner());
64+
HttpClientBuilder builder = HttpClients.custom()
65+
.setConnectionManager(new LocalConnectionManager(dockerHost))
66+
.setRoutePlanner(new LocalRoutePlanner());
6867
HttpHost host = new HttpHost(DOCKER_SCHEME, dockerHost.getAddress());
6968
return new LocalHttpClientTransport(builder.build(), host);
7069
}
@@ -78,65 +77,53 @@ private static class LocalConnectionManager extends BasicHttpClientConnectionMan
7877
.setValidateAfterInactivity(TimeValue.NEG_ONE_MILLISECOND)
7978
.build();
8079

81-
LocalConnectionManager(String host) {
82-
super(getRegistry(host), null, null, new LocalDnsResolver());
80+
LocalConnectionManager(ResolvedDockerHost dockerHost) {
81+
super(new DefaultHttpClientConnectionOperator(new LocalDetachedSocketFactory(dockerHost), null,
82+
new LocalDnsResolver(), (name) -> null), null);
8383
setConnectionConfig(CONNECTION_CONFIG);
8484
}
8585

86-
private static Registry<ConnectionSocketFactory> getRegistry(String host) {
87-
RegistryBuilder<ConnectionSocketFactory> builder = RegistryBuilder.create();
88-
builder.register(DOCKER_SCHEME, new LocalConnectionSocketFactory(host));
89-
return builder.build();
90-
}
91-
9286
}
9387

9488
/**
95-
* {@link DnsResolver} that ensures only the loopback address is used.
89+
* {@link DetachedSocketFactory} for local Docker.
9690
*/
97-
private static final class LocalDnsResolver implements DnsResolver {
91+
static class LocalDetachedSocketFactory implements DetachedSocketFactory {
9892

99-
private static final InetAddress LOOPBACK = InetAddress.getLoopbackAddress();
93+
private static final String NPIPE_PREFIX = "npipe://";
10094

101-
@Override
102-
public InetAddress[] resolve(String host) throws UnknownHostException {
103-
return new InetAddress[] { LOOPBACK };
95+
private final ResolvedDockerHost dockerHost;
96+
97+
LocalDetachedSocketFactory(ResolvedDockerHost dockerHost) {
98+
this.dockerHost = dockerHost;
10499
}
105100

106101
@Override
107-
public String resolveCanonicalHostname(String host) throws UnknownHostException {
108-
return LOOPBACK.getCanonicalHostName();
102+
public Socket create(Proxy proxy) throws IOException {
103+
String address = this.dockerHost.getAddress();
104+
if (address.startsWith(NPIPE_PREFIX)) {
105+
return NamedPipeSocket.get(address.substring(NPIPE_PREFIX.length()));
106+
}
107+
return (!Platform.isWindows()) ? UnixDomainSocket.get(address) : NamedPipeSocket.get(address);
109108
}
110109

111110
}
112111

113112
/**
114-
* {@link ConnectionSocketFactory} that connects to the local Docker domain socket or
115-
* named pipe.
113+
* {@link DnsResolver} that ensures only the loopback address is used.
116114
*/
117-
private static class LocalConnectionSocketFactory implements ConnectionSocketFactory {
118-
119-
private static final String NPIPE_PREFIX = "npipe://";
120-
121-
private final String host;
115+
private static final class LocalDnsResolver implements DnsResolver {
122116

123-
LocalConnectionSocketFactory(String host) {
124-
this.host = host;
125-
}
117+
private static final InetAddress LOOPBACK = InetAddress.getLoopbackAddress();
126118

127119
@Override
128-
public Socket createSocket(HttpContext context) throws IOException {
129-
if (this.host.startsWith(NPIPE_PREFIX)) {
130-
return NamedPipeSocket.get(this.host.substring(NPIPE_PREFIX.length()));
131-
}
132-
return (!Platform.isWindows()) ? UnixDomainSocket.get(this.host) : NamedPipeSocket.get(this.host);
120+
public InetAddress[] resolve(String host) {
121+
return new InetAddress[] { LOOPBACK };
133122
}
134123

135124
@Override
136-
public Socket connectSocket(TimeValue connectTimeout, Socket socket, HttpHost host,
137-
InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context)
138-
throws IOException {
139-
return socket;
125+
public String resolveCanonicalHostname(String host) {
126+
return LOOPBACK.getCanonicalHostName();
140127
}
141128

142129
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/RemoteHttpClientTransport.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,8 +25,8 @@
2525
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
2626
import org.apache.hc.client5.http.impl.classic.HttpClients;
2727
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
28-
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
29-
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
28+
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
29+
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
3030
import org.apache.hc.core5.http.HttpHost;
3131
import org.apache.hc.core5.http.io.SocketConfig;
3232
import org.apache.hc.core5.util.Timeout;
@@ -74,7 +74,7 @@ private static RemoteHttpClientTransport create(DockerHost host, SslContextFacto
7474
.create()
7575
.setDefaultSocketConfig(socketConfig);
7676
if (host.isSecure()) {
77-
connectionManagerBuilder.setSSLSocketFactory(getSecureConnectionSocketFactory(host, sslContextFactory));
77+
connectionManagerBuilder.setTlsSocketStrategy(getTlsSocketStrategy(host, sslContextFactory));
7878
}
7979
HttpClientBuilder builder = HttpClients.custom();
8080
builder.setConnectionManager(connectionManagerBuilder.build());
@@ -83,13 +83,12 @@ private static RemoteHttpClientTransport create(DockerHost host, SslContextFacto
8383
return new RemoteHttpClientTransport(builder.build(), httpHost);
8484
}
8585

86-
private static LayeredConnectionSocketFactory getSecureConnectionSocketFactory(DockerHost host,
87-
SslContextFactory sslContextFactory) {
86+
private static TlsSocketStrategy getTlsSocketStrategy(DockerHost host, SslContextFactory sslContextFactory) {
8887
String directory = host.getCertificatePath();
8988
Assert.hasText(directory,
9089
() -> "Docker host TLS verification requires trust material location to be specified with certificate path");
9190
SSLContext sslContext = sslContextFactory.forDirectory(directory);
92-
return new SSLConnectionSocketFactory(sslContext);
91+
return new DefaultClientTlsStrategy(sslContext);
9392
}
9493

9594
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
3737
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
3838
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
39+
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
3940
import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
40-
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
4141
import org.apache.hc.core5.http.io.SocketConfig;
4242
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
4343
import org.eclipse.jetty.io.ClientConnector;
@@ -209,9 +209,8 @@ private static HttpClient createHttpClient(Duration readTimeout, SslBundle sslBu
209209
}
210210
if (sslBundle != null) {
211211
SslOptions options = sslBundle.getOptions();
212-
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslBundle.createSslContext(),
213-
options.getEnabledProtocols(), options.getCiphers(), new DefaultHostnameVerifier());
214-
connectionManagerBuilder.setSSLSocketFactory(socketFactory);
212+
connectionManagerBuilder.setTlsSocketStrategy(new DefaultClientTlsStrategy(sslBundle.createSslContext(),
213+
options.getEnabledProtocols(), options.getCiphers(), null, new DefaultHostnameVerifier()));
215214
}
216215
PoolingHttpClientConnectionManager connectionManager = connectionManagerBuilder.useSystemProperties()
217216
.build();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import javax.naming.InitialContext;
3434
import javax.naming.NamingException;
3535
import javax.net.ssl.HostnameVerifier;
36+
import javax.net.ssl.SSLContext;
3637
import javax.net.ssl.SSLPeerUnverifiedException;
3738
import javax.net.ssl.SSLSession;
3839

@@ -64,7 +65,8 @@
6465
import org.apache.hc.client5.http.HttpHostConnectException;
6566
import org.apache.hc.client5.http.classic.HttpClient;
6667
import org.apache.hc.client5.http.impl.classic.HttpClients;
67-
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
68+
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
69+
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
6870
import org.apache.hc.core5.http.HttpResponse;
6971
import org.apache.hc.core5.http.NoHttpResponseException;
7072
import org.apache.hc.core5.ssl.SSLContextBuilder;
@@ -670,12 +672,12 @@ void shouldUpdateSslWhenReloadingSslBundles() throws Exception {
670672
this.webServer = factory.getWebServer();
671673
this.webServer.start();
672674
RememberingHostnameVerifier verifier = new RememberingHostnameVerifier();
673-
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
674-
new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(), verifier);
675-
HttpComponentsClientHttpRequestFactory requestFactory = createHttpComponentsRequestFactory(socketFactory);
675+
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
676+
TlsSocketStrategy tlsSocketStrategy = new DefaultClientTlsStrategy(sslContext, verifier);
677+
HttpComponentsClientHttpRequestFactory requestFactory = createHttpComponentsRequestFactory(tlsSocketStrategy);
676678
assertThat(getResponse(getLocalUrl("https", "/test.txt"), requestFactory)).isEqualTo("test");
677679
assertThat(verifier.getLastPrincipal()).isEqualTo("CN=1");
678-
requestFactory = createHttpComponentsRequestFactory(socketFactory);
680+
requestFactory = createHttpComponentsRequestFactory(tlsSocketStrategy);
679681
bundles.updateBundle("test", createPemSslBundle("classpath:org/springframework/boot/web/embedded/tomcat/2.crt",
680682
"classpath:org/springframework/boot/web/embedded/tomcat/2.key"));
681683
assertThat(getResponse(getLocalUrl("https", "/test.txt"), requestFactory)).isEqualTo("test");
@@ -690,9 +692,8 @@ void sslWithHttp11Nio2Protocol() throws Exception {
690692
factory.setSsl(getSsl(null, "password", "src/test/resources/test.jks"));
691693
this.webServer = factory.getWebServer();
692694
this.webServer.start();
693-
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
694-
new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build());
695-
HttpComponentsClientHttpRequestFactory requestFactory = createHttpComponentsRequestFactory(socketFactory);
695+
HttpComponentsClientHttpRequestFactory requestFactory = createHttpComponentsRequestFactory(
696+
createTrustSelfSignedTlsSocketStrategy());
696697
assertThat(getResponse(getLocalUrl("https", "/test.txt"), requestFactory)).isEqualTo("test");
697698
}
698699

0 commit comments

Comments
 (0)