Spring Boot currently documents explicitly that it does not support h2c (i.e. HTTP/2 in the clear, no encryption). There are sound policy reasons for this as a default position, particularly assuming that the Spring Boot application faces the internet.

However, this complicates the adoption of Spring Boot for service mesh use cases where an L7 proxy (e.g. Envoy) is actually managing certificates and encrypted connections across the network, and the connection between the service and the proxy is just host-local. In this case, h2c between the proxy and the service is the most appropriate technical choice.

Is there not a case for allowing h2c via configuration for these use cases?

Comment From: wilkinsona

The documentation is probably worded a bit too strongly at the moment. What it really means is that there's no support for enabling h2c via configuration properties. You can still do so by adding a little bit of your own configuration. For example, the following customiser will enable h2c with Tomcat:

@Bean
public TomcatConnectorCustomizer customizer() {
    return (connector) -> connector.addUpgradeProtocol(new Http2Protocol());
}

We should probably add something to the documentation showing what's necessary for Jetty, Netty, Tomcat, and Undertow.

Comment From: nhw76

Thank you for the response.

My point here was that the level of opinionation currently is too strong: that comes across both in the documentation and the lack of configuration properties. Sorry if that wasn't clear!

Changing the documentation to say it is not enabled by default (vs. not supported), and then providing code snippets seems like a good balance between secure defaults and flexibility.

Comment From: dvlato

Hi,

Is this the only bit that needs changing? It seems this does not work with Spring Boot 2.2 and Tomcat 9.0.21-9.0.37; I get a 500 error when I try to upgrade the connection to HTTP/2.0 with curl, see stack trace below.

Could you please document the exact changes needed to enable H2C in Spring Boot (in my case, with Tomcat)?

java.lang.NullPointerException: null
    at org.apache.coyote.Request.doRead(Request.java:551) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
    at org.apache.catalina.connector.InputBuffer.realReadBytes(InputBuffer.java:336) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
    at org.apache.catalina.connector.InputBuffer.checkByteBufferEof(InputBuffer.java:632) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
    at org.apache.catalina.connector.InputBuffer.readByte(InputBuffer.java:350) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
    at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:84) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:83) ~[na:na]
    at java.base/java.io.PushbackInputStream.read(PushbackInputStream.java:136) ~[na:na]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver$EmptyBodyCheckingHttpInputMessage.<init>(AbstractMessageConverterMethodArgumentResolver.java:325) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]

Comment From: wilkinsona

@dvlato The bean in my comment above should be all that is necessary. Your stack trace suggests that a Request is being read from when its input buffer is null. On first impression, I think that's more likely to be a problem in Tomcat than in Spring Boot as it's at a level lower than where Spring Boot really gets involved. That said, if you'd like us to spend some time investigating, please open a new issue and provide a complete yet minimal sample that reproduces the problem.