Skip to content

Commit 00f88b9

Browse files
committed
Remove overly specific casts from SslConnectorCustomizer
Closes gh-43849
1 parent 9036249 commit 00f88b9

File tree

7 files changed

+195
-11
lines changed

7 files changed

+195
-11
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java

+10-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 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.
@@ -21,7 +21,7 @@
2121
import org.apache.catalina.connector.Connector;
2222
import org.apache.commons.logging.Log;
2323
import org.apache.coyote.ProtocolHandler;
24-
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
24+
import org.apache.coyote.http11.AbstractHttp11Protocol;
2525
import org.apache.tomcat.util.net.SSLHostConfig;
2626
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
2727
import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
@@ -58,28 +58,28 @@ class SslConnectorCustomizer {
5858
}
5959

6060
void update(String serverName, SslBundle updatedSslBundle) {
61-
AbstractHttp11JsseProtocol<?> protocol = (AbstractHttp11JsseProtocol<?>) this.connector.getProtocolHandler();
61+
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) this.connector.getProtocolHandler();
6262
String host = (serverName != null) ? serverName : protocol.getDefaultSSLHostConfigName();
6363
this.logger.debug("SSL Bundle for host " + host + " has been updated, reloading SSL configuration");
6464
addSslHostConfig(protocol, host, updatedSslBundle);
6565
}
6666

6767
void customize(SslBundle sslBundle, Map<String, SslBundle> serverNameSslBundles) {
6868
ProtocolHandler handler = this.connector.getProtocolHandler();
69-
Assert.state(handler instanceof AbstractHttp11JsseProtocol,
70-
"To use SSL, the connector's protocol handler must be an AbstractHttp11JsseProtocol subclass");
71-
configureSsl((AbstractHttp11JsseProtocol<?>) handler, sslBundle, serverNameSslBundles);
69+
Assert.state(handler instanceof AbstractHttp11Protocol,
70+
"To use SSL, the connector's protocol handler must be an AbstractHttp11Protocol subclass");
71+
configureSsl((AbstractHttp11Protocol<?>) handler, sslBundle, serverNameSslBundles);
7272
this.connector.setScheme("https");
7373
this.connector.setSecure(true);
7474
}
7575

7676
/**
77-
* Configure Tomcat's {@link AbstractHttp11JsseProtocol} for SSL.
77+
* Configure Tomcat's {@link AbstractHttp11Protocol} for SSL.
7878
* @param protocol the protocol
7979
* @param sslBundle the SSL bundle
8080
* @param serverNameSslBundles the SSL bundles for specific SNI host names
8181
*/
82-
private void configureSsl(AbstractHttp11JsseProtocol<?> protocol, SslBundle sslBundle,
82+
private void configureSsl(AbstractHttp11Protocol<?> protocol, SslBundle sslBundle,
8383
Map<String, SslBundle> serverNameSslBundles) {
8484
protocol.setSSLEnabled(true);
8585
if (sslBundle != null) {
@@ -88,16 +88,15 @@ private void configureSsl(AbstractHttp11JsseProtocol<?> protocol, SslBundle sslB
8888
serverNameSslBundles.forEach((serverName, bundle) -> addSslHostConfig(protocol, serverName, bundle));
8989
}
9090

91-
private void addSslHostConfig(AbstractHttp11JsseProtocol<?> protocol, String serverName, SslBundle sslBundle) {
91+
private void addSslHostConfig(AbstractHttp11Protocol<?> protocol, String serverName, SslBundle sslBundle) {
9292
SSLHostConfig sslHostConfig = new SSLHostConfig();
9393
sslHostConfig.setHostName(serverName);
9494
configureSslClientAuth(sslHostConfig);
9595
applySslBundle(protocol, sslHostConfig, sslBundle);
9696
protocol.addSslHostConfig(sslHostConfig, true);
9797
}
9898

99-
private void applySslBundle(AbstractHttp11JsseProtocol<?> protocol, SSLHostConfig sslHostConfig,
100-
SslBundle sslBundle) {
99+
private void applySslBundle(AbstractHttp11Protocol<?> protocol, SSLHostConfig sslHostConfig, SslBundle sslBundle) {
101100
SslBundleKey key = sslBundle.getKey();
102101
SslStoreBundle stores = sslBundle.getStores();
103102
SslOptions options = sslBundle.getOptions();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
plugins {
2+
id "java"
3+
}
4+
5+
description = "Spring Boot Tomcat 11 SSL smoke test"
6+
7+
configurations.all {
8+
resolutionStrategy.eachDependency {
9+
if (it.requested.group == 'org.apache.tomcat' || it.requested.group == 'org.apache.tomcat.embed') {
10+
it.useVersion '11.0.0'
11+
}
12+
}
13+
}
14+
15+
dependencies {
16+
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
17+
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-actuator"))
18+
19+
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
20+
testImplementation("org.apache.httpcomponents.client5:httpclient5")
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.tomcat.ssl;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
22+
@SpringBootApplication
23+
public class SampleTomcat11SslApplication {
24+
25+
public static void main(String[] args) {
26+
SpringApplication.run(SampleTomcat11SslApplication.class, args);
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.tomcat.ssl.web;
18+
19+
import org.springframework.web.bind.annotation.GetMapping;
20+
import org.springframework.web.bind.annotation.RestController;
21+
22+
@RestController
23+
public class SampleController {
24+
25+
@GetMapping("/")
26+
public String helloWorld() {
27+
return "Hello, world";
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
server.port=8443
2+
3+
management.endpoints.web.exposure.include=*
4+
management.endpoint.health.show-details=always
5+
management.health.ssl.certificate-validity-warning-threshold=7d
6+
management.health.ssl.enabled=true
7+
management.info.ssl.enabled=true
8+
9+
server.ssl.bundle=ssldemo
10+
spring.ssl.bundle.jks.ssldemo.keystore.location=classpath:sample.jks
11+
spring.ssl.bundle.jks.ssldemo.keystore.password=secret
12+
spring.ssl.bundle.jks.ssldemo.keystore.type=JKS
13+
spring.ssl.bundle.jks.ssldemo.key.password=password
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.tomcat.ssl;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.boot.test.context.SpringBootTest;
23+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
24+
import org.springframework.boot.test.web.client.TestRestTemplate;
25+
import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory;
26+
import org.springframework.http.HttpStatus;
27+
import org.springframework.http.ResponseEntity;
28+
import org.springframework.test.json.JsonContent;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
32+
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
33+
class SampleTomcat11SslApplicationTests {
34+
35+
@Autowired
36+
private TestRestTemplate restTemplate;
37+
38+
@Autowired
39+
private AbstractConfigurableWebServerFactory webServerFactory;
40+
41+
@Test
42+
void testSsl() {
43+
assertThat(this.webServerFactory.getSsl().isEnabled()).isTrue();
44+
}
45+
46+
@Test
47+
void testHome() {
48+
ResponseEntity<String> entity = this.restTemplate.getForEntity("/", String.class);
49+
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
50+
assertThat(entity.getBody()).isEqualTo("Hello, world");
51+
}
52+
53+
@Test
54+
void testSslInfo() {
55+
ResponseEntity<String> entity = this.restTemplate.getForEntity("/actuator/info", String.class);
56+
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
57+
JsonContent body = new JsonContent(entity.getBody());
58+
assertThat(body).extractingPath("ssl.bundles[0].name").isEqualTo("ssldemo");
59+
assertThat(body).extractingPath("ssl.bundles[0].certificateChains[0].alias")
60+
.isEqualTo("spring-boot-ssl-sample");
61+
assertThat(body).extractingPath("ssl.bundles[0].certificateChains[0].certificates[0].issuer")
62+
.isEqualTo("CN=localhost,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown");
63+
assertThat(body).extractingPath("ssl.bundles[0].certificateChains[0].certificates[0].subject")
64+
.isEqualTo("CN=localhost,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown");
65+
assertThat(body).extractingPath("ssl.bundles[0].certificateChains[0].certificates[0].validity.status")
66+
.isEqualTo("EXPIRED");
67+
assertThat(body).extractingPath("ssl.bundles[0].certificateChains[0].certificates[0].validity.message")
68+
.asString()
69+
.startsWith("Not valid after ");
70+
}
71+
72+
@Test
73+
void testSslHealth() {
74+
ResponseEntity<String> entity = this.restTemplate.getForEntity("/actuator/health", String.class);
75+
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
76+
JsonContent body = new JsonContent(entity.getBody());
77+
assertThat(body).extractingPath("status").isEqualTo("OUT_OF_SERVICE");
78+
assertThat(body).extractingPath("components.ssl.status").isEqualTo("OUT_OF_SERVICE");
79+
assertThat(body).extractingPath("components.ssl.details.invalidChains[0].alias")
80+
.isEqualTo("spring-boot-ssl-sample");
81+
assertThat(body).extractingPath("components.ssl.details.invalidChains[0].certificates[0].issuer")
82+
.isEqualTo("CN=localhost,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown");
83+
assertThat(body).extractingPath("components.ssl.details.invalidChains[0].certificates[0].subject")
84+
.isEqualTo("CN=localhost,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown");
85+
assertThat(body).extractingPath("components.ssl.details.invalidChains[0].certificates[0].validity.status")
86+
.isEqualTo("EXPIRED");
87+
assertThat(body).extractingPath("components.ssl.details.invalidChains[0].certificates[0].validity.message")
88+
.asString()
89+
.startsWith("Not valid after ");
90+
}
91+
92+
}

0 commit comments

Comments
 (0)