-
Notifications
You must be signed in to change notification settings - Fork 41.6k
Description
When using an embedded Jetty server, configured for HTTP/2 (using Conscrypt), the ALPNServerConnectionFactory is configured to use "h2" as it's default protocol.
This has the consequence, as I understand it and observe from testing, that when negotiating with a client that does not support ALPN, it will default to using the HTTP/2 protocol.
In my experience, clients that do not support ALPN are unlikely to support HTTP/2, at least the clients that I have tested with.
I suggest that the default protocol should be set to "HTTP/1.1", or be made configurable.
I am using Spring Boot version 2.0.4, with the dependency versions defined in the spring-boot-starter-parent pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-conscrypt-server</artifactId>
<scope>runtime</scope>
</dependency>My workaround is to set the default protocol to HTTP/1.1 using a JettyServerCustomizer
/**
* Spring Boot configures ALPN with HTTP/2 as default protocol
* Clients that do not support ALPN are unlikely to support HTTP/2.
* HTTP/1.1 is a better default.
*
* @see org.springframework.boot.web.embedded.jetty.SslServerCustomizer
*/
@Configuration
@ConditionalOnClass(NegotiatingServerConnectionFactory.class)
class JettyAlpnFallbackModifyingWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableJettyWebServerFactory> {
@Override
public void customize(ConfigurableJettyWebServerFactory factory) {
factory.addServerCustomizers((JettyServerCustomizer) server -> Arrays.stream(server.getConnectors())
.flatMap(connector -> connector.getConnectionFactories().stream())
.flatMap(ofType(NegotiatingServerConnectionFactory.class))
.forEach(negotiatingServerConnectionFactory -> negotiatingServerConnectionFactory.setDefaultProtocol(HTTP_1_1.asString())));
}
@SuppressWarnings("SameParameterValue")
private static <F, T> Function<F, Stream<T>> ofType(Class<T> type) {
return value -> type.isInstance(value) ? Stream.of(type.cast(value)) : Stream.empty();
}
}(note my workaround is not a suggested solution and is probably not generic enough to support all use cases)