Skip to content

Commit 77c468c

Browse files
committed
Add test to check SSL RestTemplate requests work against server
Add a test to `AbstractClientHttpRequestFactoriesTests` to ensure that SSL configuration works when calling a real Tomcat server. See gh-34810
1 parent 72c1f66 commit 77c468c

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
package org.springframework.boot.web.client;
1818

19+
import java.io.IOException;
1920
import java.lang.reflect.Constructor;
2021
import java.lang.reflect.Field;
2122
import java.lang.reflect.Method;
23+
import java.net.HttpURLConnection;
2224
import java.time.Duration;
2325
import java.util.concurrent.TimeUnit;
2426
import java.util.function.Supplier;
2527

28+
import javax.net.ssl.HttpsURLConnection;
2629
import javax.net.ssl.SSLSocketFactory;
2730
import javax.net.ssl.TrustManager;
2831
import javax.net.ssl.X509TrustManager;
@@ -220,14 +223,38 @@ private static OkHttp3ClientHttpRequestFactory createRequestFactory(SslBundle ss
220223
static class Simple {
221224

222225
static SimpleClientHttpRequestFactory get(ClientHttpRequestFactorySettings settings) {
223-
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
226+
SslBundle sslBundle = settings.sslBundle();
227+
SimpleClientHttpRequestFactory requestFactory = (sslBundle != null)
228+
? new SimpleClientHttpsRequestFactory(sslBundle) : new SimpleClientHttpRequestFactory();
224229
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
225230
map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout);
226231
map.from(settings::connectTimeout).asInt(Duration::toMillis).to(requestFactory::setConnectTimeout);
227232
map.from(settings::bufferRequestBody).to(requestFactory::setBufferRequestBody);
228233
return requestFactory;
229234
}
230235

236+
/**
237+
* {@link SimpleClientHttpsRequestFactory} to configure SSL from an
238+
* {@link SslBundle}.
239+
*/
240+
private static class SimpleClientHttpsRequestFactory extends SimpleClientHttpRequestFactory {
241+
242+
private SslBundle sslBundle;
243+
244+
SimpleClientHttpsRequestFactory(SslBundle sslBundle) {
245+
this.sslBundle = sslBundle;
246+
}
247+
248+
@Override
249+
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
250+
if (this.sslBundle != null && connection instanceof HttpsURLConnection secureConnection) {
251+
SSLSocketFactory socketFactory = this.sslBundle.createSslContext().getSocketFactory();
252+
secureConnection.setSSLSocketFactory(socketFactory);
253+
}
254+
}
255+
256+
}
257+
231258
}
232259

233260
/**

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,30 @@
1616

1717
package org.springframework.boot.web.client;
1818

19+
import java.net.URI;
20+
import java.nio.charset.StandardCharsets;
1921
import java.time.Duration;
2022

23+
import javax.net.ssl.SSLHandshakeException;
24+
2125
import org.junit.jupiter.api.Test;
2226

27+
import org.springframework.boot.ssl.SslBundle;
28+
import org.springframework.boot.ssl.SslBundleKey;
29+
import org.springframework.boot.ssl.jks.JksSslStoreBundle;
30+
import org.springframework.boot.ssl.jks.JksSslStoreDetails;
31+
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
32+
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
33+
import org.springframework.boot.web.server.Ssl;
34+
import org.springframework.boot.web.server.Ssl.ClientAuth;
35+
import org.springframework.boot.web.server.WebServer;
36+
import org.springframework.http.HttpMethod;
37+
import org.springframework.http.client.ClientHttpRequest;
2338
import org.springframework.http.client.ClientHttpRequestFactory;
39+
import org.springframework.util.StreamUtils;
2440

2541
import static org.assertj.core.api.Assertions.assertThat;
42+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2643

2744
/**
2845
* Base classes for testing of {@link ClientHttpRequestFactories} with different HTTP
@@ -31,6 +48,7 @@
3148
* @param <T> the {@link ClientHttpRequestFactory} to be produced
3249
* @author Andy Wilkinson
3350
*/
51+
@DirtiesUrlFactories
3452
abstract class AbstractClientHttpRequestFactoriesTests<T extends ClientHttpRequestFactory> {
3553

3654
private final Class<T> requestFactoryType;
@@ -76,6 +94,39 @@ void getReturnsRequestFactoryWithConfiguredReadTimeout() {
7694
assertThat(readTimeout((T) requestFactory)).isEqualTo(Duration.ofSeconds(120).toMillis());
7795
}
7896

97+
@Test
98+
void connectWithSslBundle() throws Exception {
99+
TomcatServletWebServerFactory webServerFactory = new TomcatServletWebServerFactory(0);
100+
Ssl ssl = new Ssl();
101+
ssl.setClientAuth(ClientAuth.NEED);
102+
ssl.setKeyPassword("password");
103+
ssl.setKeyStore("classpath:test.jks");
104+
ssl.setTrustStore("classpath:test.jks");
105+
webServerFactory.setSsl(ssl);
106+
WebServer webServer = webServerFactory.getWebServer();
107+
try {
108+
webServer.start();
109+
int port = webServer.getPort();
110+
URI uri = new URI("https://localhost:%s".formatted(port));
111+
ClientHttpRequestFactory insecureRequestFactory = ClientHttpRequestFactories
112+
.get(ClientHttpRequestFactorySettings.DEFAULTS);
113+
ClientHttpRequest insecureRequest = insecureRequestFactory.createRequest(uri, HttpMethod.GET);
114+
assertThatExceptionOfType(SSLHandshakeException.class)
115+
.isThrownBy(() -> insecureRequest.execute().getBody());
116+
JksSslStoreDetails storeDetails = JksSslStoreDetails.forLocation("classpath:test.jks");
117+
JksSslStoreBundle stores = new JksSslStoreBundle(storeDetails, storeDetails);
118+
SslBundle sslBundle = SslBundle.of(stores, SslBundleKey.of("password"));
119+
ClientHttpRequestFactory secureRequestFactory = ClientHttpRequestFactories
120+
.get(ClientHttpRequestFactorySettings.DEFAULTS.withSslBundle(sslBundle));
121+
ClientHttpRequest secureRequest = secureRequestFactory.createRequest(uri, HttpMethod.GET);
122+
String secureResponse = StreamUtils.copyToString(secureRequest.execute().getBody(), StandardCharsets.UTF_8);
123+
assertThat(secureResponse).contains("HTTP Status 404 – Not Found");
124+
}
125+
finally {
126+
webServer.stop();
127+
}
128+
}
129+
79130
protected abstract long connectTimeout(T requestFactory);
80131

81132
protected abstract long readTimeout(T requestFactory);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.File;
2020

2121
import okhttp3.OkHttpClient;
22+
import org.junit.jupiter.api.Disabled;
2223
import org.junit.jupiter.api.Test;
2324

2425
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
@@ -66,4 +67,9 @@ protected long readTimeout(OkHttp3ClientHttpRequestFactory requestFactory) {
6667
return ((OkHttpClient) ReflectionTestUtils.getField(requestFactory, "client")).readTimeoutMillis();
6768
}
6869

70+
@Override
71+
@Disabled("OkHostnameVerifier fails because the JSK doesn't have a type 2 SubjectAltName")
72+
void connectWithSslBundle() throws Exception {
73+
}
74+
6975
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.File;
2020

2121
import okhttp3.OkHttpClient;
22+
import org.junit.jupiter.api.Disabled;
2223
import org.junit.jupiter.api.Test;
2324

2425
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
@@ -64,4 +65,9 @@ protected long readTimeout(OkHttp3ClientHttpRequestFactory requestFactory) {
6465
return ((OkHttpClient) ReflectionTestUtils.getField(requestFactory, "client")).readTimeoutMillis();
6566
}
6667

68+
@Override
69+
@Disabled("OkHostnameVerifier fails because the JSK doesn't have a type 2 SubjectAltName")
70+
void connectWithSslBundle() throws Exception {
71+
}
72+
6773
}

0 commit comments

Comments
 (0)