Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enable HTTP2 with Tomcat in Spring Boot

Tomcat 8.5, which will be the default in Spring Boot 1.4, supports HTTP/2.

How can HTTP/2 be enabled in a Spring Boot application?

like image 625
yglodt Avatar asked Jul 27 '16 12:07

yglodt


People also ask

How do I enable http2 in spring boot?

Enabling HTTP/2 at the Spring Boot level In the application. properties file, enable HTTP/2 and restart the application. Now, let's use curl to validate the HTTP version in use. This provides us with a response, like the one shown below.

Does spring use http2?

You can enable HTTP/2 support in your Spring Boot application with the server. http2. enabled configuration property. This support depends on the chosen web server and the application environment, since that protocol is not supported out-of-the-box by JDK8.


4 Answers

In Spring Boot 2.1 and above it is as simple as adding this property to your .properties (or .yml) file:

server.http2.enabled=true

You can also do it programmatically like this (in one of your configuration classes):

@Bean
public ConfigurableServletWebServerFactory tomcatCustomizer() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.addConnectorCustomizers(connector -> connector.addUpgradeProtocol(new Http2Protocol()));
    return factory;
}
like image 75
Mahozad Avatar answered Oct 24 '22 03:10

Mahozad


You need to add the HTTP 2 upgrade protocol to Tomcat's connector. You can do that by customizing the embedded Tomcat container:

Java 8:

@Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
    return (container) -> {
        if (container instanceof TomcatEmbeddedServletContainerFactory) {
            ((TomcatEmbeddedServletContainerFactory) container)
                    .addConnectorCustomizers((connector) -> {
                connector.addUpgradeProtocol(new Http2Protocol());
            });
        }
    };
}

Java 7:

@Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
    return new EmbeddedServletContainerCustomizer() {

        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            if (container instanceof TomcatEmbeddedServletContainerFactory) {
                ((TomcatEmbeddedServletContainerFactory) container)
                        .addConnectorCustomizers(new TomcatConnectorCustomizer() {
                    @Override
                    public void customize(Connector connector) {
                        connector.addUpgradeProtocol(new Http2Protocol());
                    }

                });
            }
        }

    };
}
like image 44
Andy Wilkinson Avatar answered Oct 24 '22 02:10

Andy Wilkinson


The most elegant and best-performing way to enable HTTP/2 with a Spring Boot application follows here.

First, as mentioned in Andy Wilkinson's answer, you need to enable HTTP/2 at Tomcat level:

@Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
    return (container) -> {
        if (container instanceof TomcatEmbeddedServletContainerFactory) {
            ((TomcatEmbeddedServletContainerFactory) container)
                    .addConnectorCustomizers((connector) -> {
                connector.addUpgradeProtocol(new Http2Protocol());
            });
        }
    };
}

In case you are not using an embedded Tomcat, you can set up HTTP/2 listening like this:

<Connector port="5080" protocol="HTTP/1.1" connectionTimeout="20000">
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
</Connector>

Remember that you need Tomcat >= 8.5.

Then, you should use HAProxy (version >= 1.7) in front of Tomcat to take care of encryption.

The client will speak https to HAProxy, and HAProxy will speak cleartext HTTP/1.1 or HTTP/2 to the backend, as the client requested. There will be no unnecessary protocol translations.

The matching HAProxy-configuration is here:

# Create PEM: cat cert.crt cert.key ca.crt > /etc/ssl/certs/cert.pem

global
    tune.ssl.default-dh-param 2048
    ssl-default-bind-options no-sslv3 no-tls-tickets force-tlsv12
    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
    chroot /var/lib/haproxy
    user haproxy
    group haproxy

defaults
    timeout connect 10000ms
    timeout client 60000ms
    timeout server 60000ms

frontend fe_https
    mode tcp
    rspadd Strict-Transport-Security:\ max-age=31536000;\ includeSubDomains;\ preload
    rspadd X-Frame-Options:\ DENY
    bind *:443 ssl crt /etc/ssl/certs/cert.pem alpn h2,http/1.1
    default_backend be_http

backend be_http
    mode tcp
    server domain 127.0.0.1:8080
    # compression algo gzip # does not work in mode "tcp"
    # compression type text/html text/css text/javascript application/json

Edit 2019

I face two problems when using mode "tcp"

  • Compression does not work, since it depends on mode http. So the backend has to take care of it
  • The backend can not see the client's IP-address. Probably I need NAT. Still investigating...

Generally, since haproxy proxies a lower level tcp connection, there is no access to any http stuff

like image 24
yglodt Avatar answered Oct 24 '22 03:10

yglodt


In Spring Boot 2 you first need a certificate - it can by generated like this:

keytool -genkey -keyalg RSA -alias my-the-best-api -keystore c:\tmp\keystore.store -storepass secret -validity 3650 -keysize 2048

Than you just need to add this certificate to classpath and add needed properties to application.properties:

server.http2.enabled=true
server.port = 8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
like image 14
Jozef Najman Avatar answered Oct 24 '22 03:10

Jozef Najman