Since HTTP/2 is gaining momentum I thought it would be a nice experiment to see if it’s possible to convert some applications to HTTP/2. We have a bunch of Spring Boot micro services and those services communicate with each other via REST calls. All communication happens via JSON (Jackson 2). Running Spring Boot with HTTP/2 should be easy and hopefully Spring RestTemplate supports HTTP/2 for the inter service communication. Let’s see…

Introduction

Most people are quite used to running Tomcat on port 8080 with an Apache server in front to provide https. We do this because it’s an easy and proven solution. With HTTP/2 you’re forced (by all major browsers) to use https, so you don’t need Apache (or nginx) anymore.

Update curl and install ‘HTTP/2 and SPDY indicator’ extensions

When I’m experimenting with HTTP/2 I often use curl to gain more insight in what’s happening under the hood. In order to use curl with HTTP/2 you probably need to update. Chances are your version of curl doesn’t support HTTP/2 (or supports it with bugs). I had some problems with 7.47.1 on a Mac, you should be safe with version 7.48.0 and higher. Note that HTTP/2 isn’t supported by any version by default, you should use a curl that’s built with HTTP/2 support. To check if your version supports HTTP/2 do a

curl --version

This will display the version and you should see HTTP2 in the features section.

If this is not the case I have a simple brew solution (for Mac only):

brew reinstall curl --with-openssl --with-nghttp2

On other operating systems please check this site. I haven’t tried it, but it looks solid.

You can see the difference between HTTP/1.1 and HTTP/2 by executing the following commands :

curl https://http2.akamai.com/ --http1.1 | grep Browser_support curl https://http2.akamai.com/ --http2 | grep gobutton

The grep should find exactly one line. You can of course omit the grep if you don’t believe me 😃

A final suggestion is to install ‘HTTP/2 and SPDY indicator’ for Chrome or FireFox. This displays a colored lightning bolt that indicates whether the page you’re visiting is using HTTP/2 (or SPDY or QUIC).

Known errors

When you get the error “curl: (1) Unsupported protocol” curl doesn’t support HTTP/2

When you get the error “curl: (35) Unknown SSL protocol error in connection to localhost:8443” you’re probably using an older version of curl.

Server

Spring Boot uses Tomcat by default. In my last article about HTTP/2 I chose undertow. A lot can happen in three months, so I did some investigation to see if things have changed. I found a nice presentation by Brian Clozel as my starting point. Currently Jetty and undertow are the only servers in Spring Boot that support HTTP/2.

Jetty has booked some progress and this repository shows an excellent example. In my opinion it’s still too much custom code, but they’re getting there.

The next candidate is undertow. It seems almost too easy, but it works. Because we use AJP in our current configuration it even means this HTTP/2 solution has less lines of code!

In short the steps (you can copy/paste everything from the github link I mentioned earlier)

Step 1 – update dependencies

The first step is excluding org.springframework.boot:spring-boot-starter-tomcat from spring-boot-starter-web since we want to switch undertow.

Next add a dependency to undertow : org.springframework.boot:spring-boot-starter-undertow

Also add a provided dependency to org.mortbay.jetty.alpn:alpn-boot:8.1.8.v20160420

You might need to comment the provided scope the first time you build your maven project to trigger the download of the jar (if it’s already in your repository you can omit this step).

Step 2 – create a servlet container bean

@Bean UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() { UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory(); factory.addBuilderCustomizers( builder -> builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true)); return factory; }

Step 3 – start your server with alpn

To start your server you have to provide the -Xbootclasspath parameter to include alpn because the jars included with your jdk probably don’t have support (should be fixed by JDK9).

-Xbootclasspath/p:/home/harrie/.m2/repository/org/mortbay/jetty/alpn/alpn-boot/8.1.8.v20160420/alpn-boot-8.1.8.v20160420.jar

When this parameter isn’t set properly you probably get an error or your server falls back to an HTTP/1.1 response.

Client

Between our micro service we communicate via HTTP and use Spring RestTemplate to do so. Ideally we keep using RestTemplate, but unfortunately the underlying libraries are a bit too old to support HTTP/2.

Currently Java HTTP/2 clients are scarce. According to this wiki Netty and OkHttp are the only two implementations supported by Spring. To switch HTTP-client in RestTemplate you have to call the constructor with a different ClientHttpRequestFactory (either Netty4ClientHttpRequestFactory or OkHttpClientHttpRequestFactory). The Netty implementation gave strange errors and after checking how to make a call manually I quickly gave up. The attempt with OkHttp failed because of an older version used by Spring (and no backward compatibility of OkHttp).

Now that we ditched RestTemplate Jetty joins the club of possible candidates. Fabian Stäber wrote an article about how to do it manually. The OkHttp solution looked promising and since I never used it before this was my choice (Netty and Jetty probably are perfectly fine, so don’t hesitate to use them).

updated at 1-apr-2016 18:30Z Brian pointed out that Spring 4.3 will support HTTP/2

Since right now there is a milestone build only you have to add the following dependencies to your pom.xml (assuming you’re using the ‘making’ demo)

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.0.RC1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.0.RC1</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.2.0</version> </dependency>

Now replace the hello method in DemoHttp2Application :

@RequestMapping String hello() { RestTemplate http2Template = new RestTemplate(new OkHttp3ClientHttpRequestFactory()); RestTemplate http11Template = new RestTemplate(); String http11Response = http11Template.getForObject("https://http2.akamai.com/", String.class); String http2Response = http2Template.getForObject("https://http2.akamai.com/", String.class); return "HTTP/1.1 : " + http11Response.contains("You are using HTTP/2 right now!") + "<br/>" + "HTTP/2 : " + http2Response.contains("You are using HTTP/2 right now!"); }

When you visit the page you should see the following result :

HTTP/1.1 : false HTTP/2 : true

I’m aware this is not the right way to create a RestTemplate, but for demo purposes it’s ok.

Conclusion

It’s now possible (without too much hassle) to run a HTTP/2 server and client in Spring Boot. Unfortunately not with a release version of the Spring RestTemplate, but it’s also a lesson that you don’t always need to solve your problems with Spring or be in front of the herd.

Update

1-apr-2016 9:08Z Brian pointed out that Spring 4.3 will support OkHttp3 (RC1 is availiable, no RELEASE yet). Tomcat 8.5 (also not released yet) will have HTTP/2 support without ALPN.

4-jun-2016 13:25Z Max pointed out the making example isn’t working with Chrome 51 anymore. I added a fix for it and updated the ALPN dependency. Made a pull request that was merged within 13 minutes (thanks Toshiaki Maki), I love open source 🙂

Sources

Slidshare presentation : “HTTP/2 for the Web Developer” (slides 29, 38)

Jetty example on Github

Undertow example on Github

Spring Boot Issue : “Support to HTTP/2 – Undertow #3025”

Stackoverflow – curl does not support http2 on mac

HTTP/2 Implementations

HTTP/2 Java Client Examples

HTTP/2 with curl

Chrome HTTP/2 and SPDY indicator

FireFox HTTP/2 and SPDY indicator