diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java index d9f57b33ff..d0648c66b5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java @@ -106,24 +106,25 @@ class SslServerCustomizer implements JettyServerCustomizer { private ServerConnector createHttp11ServerConnector(Server server, HttpConfiguration config, SslContextFactory.Server sslContextFactory) { HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config); - SslConnectionFactory sslConnectionFactory; + return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), + createSslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), connectionFactory); + } + + private SslConnectionFactory createSslConnectionFactory(SslContextFactory.Server sslContextFactory, + String protocol) { try { - sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); + return new SslConnectionFactory(sslContextFactory, protocol); } catch (NoSuchMethodError ex) { // Jetty 10 try { - sslConnectionFactory = SslConnectionFactory.class - .getConstructor(SslContextFactory.Server.class, String.class) - .newInstance(sslContextFactory, HttpVersion.HTTP_1_1.asString()); + return SslConnectionFactory.class.getConstructor(SslContextFactory.Server.class, String.class) + .newInstance(sslContextFactory, protocol); } catch (Exception ex2) { throw new RuntimeException(ex2); } } - - return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), sslConnectionFactory, - connectionFactory); } private boolean isJettyAlpnPresent() { @@ -143,7 +144,7 @@ class SslServerCustomizer implements JettyServerCustomizer { if (isConscryptPresent()) { sslContextFactory.setProvider("Conscrypt"); } - SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol()); + SslConnectionFactory ssl = createSslConnectionFactory(sslContextFactory, alpn.getProtocol()); return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), ssl, alpn, h2, http); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/build.gradle index a8b3171a87..2591ecd3fc 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/build.gradle @@ -14,6 +14,24 @@ dependencies { implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web")) { exclude module: "spring-boot-starter-tomcat" } + + runtimeOnly("org.eclipse.jetty:jetty-alpn-java-server") + runtimeOnly("org.eclipse.jetty.http2:http2-server") testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) + testImplementation("org.eclipse.jetty:jetty-client") + testImplementation("org.eclipse.jetty.http2:http2-client") + testImplementation("org.eclipse.jetty.http2:http2-http-client-transport") +} + +def buildingWithJava11OrLater() { + return project.hasProperty("toolchainVersion") || JavaVersion.current().java11Compatible +} + +compileTestJava { + enabled = buildingWithJava11OrLater() +} + +test { + enabled = buildingWithJava11OrLater() } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/src/main/resources/sample.jks b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/src/main/resources/sample.jks new file mode 100644 index 0000000000..6aa9a28053 Binary files /dev/null and b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/src/main/resources/sample.jks differ diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/src/test/java/smoketest/jetty10/Jetty10Http2OverTlsTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/src/test/java/smoketest/jetty10/Jetty10Http2OverTlsTests.java new file mode 100644 index 0000000000..40114a9a5f --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty10/src/test/java/smoketest/jetty10/Jetty10Http2OverTlsTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package smoketest.jetty10; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http2.client.HTTP2Client; +import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for HTTP/2 over TLS (h2) with Jetty 10. + * + * @author Andy Wilkinson + */ +@EnabledForJreRange(min = JRE.JAVA_11) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "server.http2.enabled=true", "server.ssl.enabled=true", + "server.ssl.keystore=classpath:sample.jks", "server.ssl.key-store-password=secret", + "server.ssl.key-password=password", "logging.level.org.eclipse.jetty=debug" }) +public class Jetty10Http2OverTlsTests { + + @LocalServerPort + private int port; + + @Test + void httpOverTlsGetWhenHttp2AndSslAreEnabledSucceeds() throws Exception { + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setTrustAll(true); + ClientConnector clientConnector = new ClientConnector(); + clientConnector.setSslContextFactory(sslContextFactory); + HttpClient client = new HttpClient(new HttpClientTransportOverHTTP2(new HTTP2Client(clientConnector))); + client.start(); + try { + ContentResponse response = client.GET("https://localhost:" + this.port + "/"); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.getContentAsString()).isEqualTo("Hello World"); + } + finally { + client.stop(); + } + } + +}