Skip to content

Commit d05a006

Browse files
authored
BAEL-6005: Enable HTTP2 with Tomcat in Spring Boot (#18374)
1 parent 3a86037 commit d05a006

File tree

11 files changed

+222
-0
lines changed

11 files changed

+222
-0
lines changed

spring-boot-modules/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
<module>spring-boot-openapi</module>
127127
<module>spring-boot-brave</module>
128128
<module>spring-boot-simple</module>
129+
<module>spring-boot-http2</module>
129130
</modules>
130131

131132
<build>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<artifactId>spring-boot-http2</artifactId>
6+
<version>0.0.1-SNAPSHOT</version>
7+
<name>spring-boot-http2</name>
8+
<description>Demo project for Spring Boot</description>
9+
<parent>
10+
<groupId>com.baeldung.spring-boot-modules</groupId>
11+
<artifactId>spring-boot-modules</artifactId>
12+
<version>1.0.0-SNAPSHOT</version>
13+
</parent>
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.springframework.boot</groupId>
17+
<artifactId>spring-boot-starter-web</artifactId>
18+
</dependency>
19+
<dependency>
20+
<groupId>ch.qos.logback</groupId>
21+
<artifactId>logback-classic</artifactId>
22+
<version>${logback.version}</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>ch.qos.logback</groupId>
26+
<artifactId>logback-core</artifactId>
27+
<version>${logback.version}</version>
28+
</dependency>
29+
</dependencies>
30+
<build>
31+
<plugins>
32+
<plugin>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-maven-plugin</artifactId>
35+
</plugin>
36+
<plugin>
37+
<groupId>org.apache.maven.plugins</groupId>
38+
<artifactId>maven-compiler-plugin</artifactId>
39+
<configuration>
40+
<source>11</source>
41+
<target>11</target>
42+
<compilerArgs>
43+
<arg>-parameters</arg>
44+
</compilerArgs>
45+
</configuration>
46+
</plugin>
47+
</plugins>
48+
</build>
49+
<properties>
50+
<spring-boot.version>3.4.3</spring-boot.version>
51+
<logback.version>1.5.17</logback.version>
52+
</properties>
53+
</project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.baeldung.http2;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class Http2Application {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(Http2Application.class, args);
11+
}
12+
13+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.baeldung.http2.config;
2+
3+
import org.apache.catalina.connector.Connector;
4+
import org.apache.coyote.http2.Http2Protocol;
5+
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
6+
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.context.annotation.Profile;
10+
11+
@Configuration
12+
@Profile("config-class")
13+
public class Http2Config {
14+
15+
@Bean
16+
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> getWebServerFactoryCustomizer() {
17+
return factory -> {
18+
Connector httpConnector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
19+
httpConnector.setPort(8080);
20+
factory.addConnectorCustomizers(connector -> connector.addUpgradeProtocol(new Http2Protocol()));
21+
factory.addAdditionalTomcatConnectors(httpConnector);
22+
};
23+
}
24+
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.http2.controller;
2+
3+
import org.springframework.web.bind.annotation.GetMapping;
4+
import org.springframework.web.bind.annotation.RequestParam;
5+
import org.springframework.web.bind.annotation.RestController;
6+
7+
@RestController
8+
public class Http2Controller {
9+
10+
@GetMapping("/http2/echo")
11+
public String getEcho(@RequestParam String message) {
12+
return message;
13+
}
14+
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
server:
2+
http2:
3+
enabled: false
4+
5+
port: 8443
6+
7+
ssl:
8+
enabled: true
9+
key-store: classpath:keystore.p12
10+
key-store-password: abcd1234
11+
key-store-type: PKCS12
12+
key-alias: http2-alias
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
server:
2+
http2:
3+
enabled: true
4+
5+
port: 8443
6+
7+
ssl:
8+
enabled: true
9+
key-store: classpath:keystore.p12
10+
key-store-password: abcd1234
11+
key-store-type: PKCS12
12+
key-alias: http2-alias
Binary file not shown.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.baeldung.http2;
2+
3+
import javax.net.ssl.X509TrustManager;
4+
import java.security.cert.X509Certificate;
5+
6+
public class AllCertsTrustManager implements X509TrustManager {
7+
8+
public X509Certificate[] getAcceptedIssuers() {
9+
return null;
10+
}
11+
12+
public void checkClientTrusted(X509Certificate[] certs, String authType) {
13+
}
14+
15+
public void checkServerTrusted(X509Certificate[] certs, String authType) {
16+
}
17+
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.baeldung.http2;
2+
3+
import org.junit.jupiter.api.BeforeAll;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
7+
import org.springframework.boot.test.context.SpringBootTest;
8+
import org.springframework.boot.test.web.server.LocalServerPort;
9+
10+
import javax.net.ssl.SSLContext;
11+
import javax.net.ssl.TrustManager;
12+
13+
import java.net.URI;
14+
import java.net.http.HttpClient;
15+
import java.net.http.HttpRequest;
16+
import java.net.http.HttpResponse;
17+
import java.security.KeyManagementException;
18+
import java.security.NoSuchAlgorithmException;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
22+
// Integration test based on enabling HTTP/2 using the configuration in application.yml
23+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
24+
class Http2ApplicationIntegrationTest {
25+
26+
private static SSLContext sslContext;
27+
28+
@LocalServerPort
29+
private int port;
30+
31+
private HttpClient httpClient;
32+
33+
@BeforeAll
34+
static void setupOnce() throws NoSuchAlgorithmException, KeyManagementException {
35+
// Get rid of cert validation due to the self-signed certificate
36+
sslContext = SSLContext.getInstance("TLS");
37+
sslContext.init(null,
38+
new TrustManager[] { new AllCertsTrustManager() },
39+
new java.security.SecureRandom());
40+
}
41+
42+
@BeforeEach
43+
void setup() {
44+
httpClient = HttpClient.newBuilder()
45+
.sslContext(sslContext)
46+
.version(HttpClient.Version.HTTP_2)
47+
.build();
48+
}
49+
50+
@Test
51+
void whenSendHttp2Request_thenReturnHttp2Response() throws Exception {
52+
// when
53+
HttpRequest request = HttpRequest.newBuilder()
54+
.uri(new URI(String.format("https://localhost:%s/http2/echo?message=test", port)))
55+
.GET()
56+
.build();
57+
HttpResponse<?> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
58+
59+
// then
60+
assertThat(response.statusCode()).isEqualTo(200);
61+
assertThat(response.body()).isEqualTo("test");
62+
assertThat(response.version()).isEqualTo(HttpClient.Version.HTTP_2);
63+
}
64+
65+
}

0 commit comments

Comments
 (0)