Fix HTTP/2 over TLS with Jetty 10

Fixes gh-26988
pull/27157/head
Andy Wilkinson 3 years ago
parent 2c2ebfce1b
commit 580b1b81ab

@ -106,24 +106,25 @@ class SslServerCustomizer implements JettyServerCustomizer {
private ServerConnector createHttp11ServerConnector(Server server, HttpConfiguration config, private ServerConnector createHttp11ServerConnector(Server server, HttpConfiguration config,
SslContextFactory.Server sslContextFactory) { SslContextFactory.Server sslContextFactory) {
HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config); 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 { try {
sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); return new SslConnectionFactory(sslContextFactory, protocol);
} }
catch (NoSuchMethodError ex) { catch (NoSuchMethodError ex) {
// Jetty 10 // Jetty 10
try { try {
sslConnectionFactory = SslConnectionFactory.class return SslConnectionFactory.class.getConstructor(SslContextFactory.Server.class, String.class)
.getConstructor(SslContextFactory.Server.class, String.class) .newInstance(sslContextFactory, protocol);
.newInstance(sslContextFactory, HttpVersion.HTTP_1_1.asString());
} }
catch (Exception ex2) { catch (Exception ex2) {
throw new RuntimeException(ex2); throw new RuntimeException(ex2);
} }
} }
return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), sslConnectionFactory,
connectionFactory);
} }
private boolean isJettyAlpnPresent() { private boolean isJettyAlpnPresent() {
@ -143,7 +144,7 @@ class SslServerCustomizer implements JettyServerCustomizer {
if (isConscryptPresent()) { if (isConscryptPresent()) {
sslContextFactory.setProvider("Conscrypt"); 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); return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), ssl, alpn, h2, http);
} }

@ -15,5 +15,23 @@ dependencies {
exclude module: "spring-boot-starter-tomcat" 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(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()
} }

@ -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();
}
}
}
Loading…
Cancel
Save