HTTP2 and Spring Boot

Introduction

HTTP2 is the successor of HTTP/1.1. It enables faster connections, multiplexing and server pushes via TCP. It’s not a completely new protocol, it’s more an evolutionary step to improve  HTTP/1.1 that has been in use since 1999. Since that year, many thing have changed: more users, more content, more cyber crime. So, it’s quite obvious that also security issues are in the focus of HTTP2.

In this blog I want to take a closer view on how HTTP2 can be used in a Spring Boot environment. As reference, I set up a project you can see on github: https://github.com/ollihoo/http2go

HTTP2 – the security basics

The RFC7540 has a separate section concerning security. You can find several hints on why the standard recommends secured communication. HTTP2 works on TLS (Transport Layer Security). This on the  first glance is no news. But in fact there is something new, it’s TLS ALPN („TLS application-layer negotiation“). This has some impact on (not only) Spring Boot applications. See more details in the next section.

There might be people saying: „Okay, something new. Forget about TLS!“ Well, no. Let me explain: HTTP2 defines protocol identifiers offering two ways of communication:

  • „h2“ – means that the protocol uses HTTP/2 over TLS (TLS1.2)
  • „h2c“ – means that the protocol uses HTTP2 over cleartext TCP

So, HTTP2 definitely says that there CAN be a communication without TLS. Don’t forget the client side: most browsers only support secured HTTP2 communication. Of course, there are ways to avoid TLS, but at the moment it seems that browsers make „h2″/TLS mandatory.

 

Spring Boot and HTTP2

First of all, not every (servlet) container does support HTTP2 yet. Spring Boot’s standard container Tomcat starts support with Tomcat 9 (not yet released by 1 July 2016). Luckily, Jetty and Undertow already support HTTP2, both can be used by Spring Boot alternatively.

The second challenge concerns TLS ALPN. This protocol isn’t supported by Java JDK 7 and 8. Java JDK 9 is told to natively support TLS ALPN. For Java 7 and 8, there is already a workaround. Unfortunately this support has to be injected during Java VM start. You will find more information later in this blog.

In summary there are four steps to do:

  • replace tomcat by Undertow
  • configure Undertow
  • create a keystore with a server key pair (optionally signed to avoid well-known alerts in browsers)
  • during start-up inject ALPN

Since descriptions can be easily misleading, I built up a project that gives a complete overview:   https://github.com/ollihoo/http2go

So, these are the details…

Replace Tomcat by Undertow

That’s quite simple. This is done by excluding tomcat from spring-boot-starter-web package and adding the spring-boot-starter-undertow package instead. This example shows the gradle configuration (build.gradle). The same way also works in Maven POM:

dependencies {
  // ...
  compile("org.springframework.boot:spring-boot-starter-web") {
    exclude module: "spring-boot-starter-tomcat"
  }
  compile("org.springframework.boot:spring-boot-starter-undertow")
  // ...
}

Now there is some configuration necessary. This is done in the following section.

 

Configure Undertow

Undertow needs to be told to support HTTP2. This is done by an undertow option (HTTP2_ENABLE). In the reference project this is done by a programmatic configuration.

This is the  core of this step:

private UndertowBuilderCustomizer createCustomizerWithHttp2() {
   new UndertowBuilderCustomizer() {
       @Override
       void customize(Undertow.Builder builder) {
          builder.setServerOption(UndertowOptions.ENABLE_HTTP2,
                                  true)
       }
   }
}

Okay, assume Undertow is configured. But Spring Boot still wouldn’t work, because there is no key pair needed for TLS. So, let’s create the Keystore.

 

Create Keystore

Spring Boot can include most options and needed files by a resources directory. We also use that for the keystore. You should replace the keystore.jks file in the reference project.  More information about this key generation can be found in the reference’s README.md. The most important step is shown here.

ollihoo@ws:~/src/main/resources$ keytool -genkeypair -alias mycert -keyalg RSA -sigalg MD5withRSA -keystore my.jks -storepass secret -keypass secret -validity 9999

As a result, you should find a new keystore.jks file. Make sure, it is referenced in the application.properties with it’s storepass and keypass to grant access.

Does it run now? Well, yes. But without HTTP2. There is another step to do.

 

Inject ALPN standard in Java 8

AS already mentioned, HTTP2 uses a new protocol called TLS ALPN (TLS application-layer protocol negotiation). This is the protocol that is also defined in application.properties:

server.ssl.protocol: TLSv1.2

The really last step is now to inject a library called org.mortbay.jetty.alpn:alpn-boot into the JVM. The procedure again is described in the README.md.

The correct alpn-boot library is defined by the JDK you are using. You can find out your version by typing java -version in the command line. Please download the library and make it easily reachable for your Application callup. With the command

java -Xbootclasspath/p:./alpn-boot-8.1.6.v20151105.jar -jar myapplication.jar

This command starts Spring Boot application. The option -Xbootclasspath/p: ensures the integration of TLS ALPN into the security layer. The reason for this complicated way is caused by the lack of an configurable API to the security layer in the Java standard library.

So, Spring Boot is configured to offer HTTP2 endpoints. Try it by starting the reference project.

By simply starting „http://localhost:8080/“ in Firefox, you should see an response header like this:

HTTP/2.0 200 OK

Content-Type: text/html;charset=UTF-8
Content-Length: 237
x-application-context: application:8443
Content-Language: de-
Date: Sun, 03 Jul 2016 09:04:40 GMT
X-Firefox-Spdy: h2

As you can see here, the protocol used is HTTP/2.0. Awesome. And you see the used protocol „h2“ that is listed in header field „X-Firefox-Spdy“. So this works well.

One last hint: Undertow offers fallback mechanisms that switch back to HTTP/1.1 when there is now ALPN available. There might be more things that cause that. To ensure that your application runs on HTTP2 care about tests.

Summary

HTTP2 is not yet an established standard in web containers. The lack of good support of TLS ALPN makes the delivery more complex than needed. But it’s possible. So, try the future.

There are questions? Let me know! Contact me by mail: oliver at hoogvliet.de