HTTP/2 in Payara Platform 5

Photo of Matthew Gill by Matthew Gill

Payara Platform 5 brought with it an implementation of Servlet 4.0, which itself contains support for the HTTP/2 standard. HTTP/2 support in Java has been fairly obscure for JDK 8 users, causing issues for many depending on their JDK minor version. This blog hopes to clarify the state of HTTP/2 in Payara Platform 5.

 

What is HTTP/2?

HTTP/2 is an update on HTTP 1.1 that aims to address its shortfalls caused by the evolution of modern websites. Some of the main features of HTTP/2 include:

  1. HTTP/2 is a binary protocol, which makes data transfer more efficient than plain text.
  2. Resource prioritisation, which allows the server to prioritise which requests to fulfill first.
  3. Multiple requests can be multiplexed over a single connection, allowing them to be served concurrently.
  4. The big one - server push. This allows resources to be pushed to the client before they are requested. So rather than an HTML page and associated CSS file taking 2 request round trips to be fetched, it takes 1.

You can see the performance difference demonstrated in the Akamai HTTP/2 Demo.

 

Does Java Include Native Support for HTTP/2?

In short: kind of. JDK 9 includes full HTTP/2 support, but this isn't particularly helpful to JDK 8 users.

 

JDK 9 includes JEP 110 which describes an HTTP/2 compatible client to replace the legacy HttpURLConnection API. This means that native HTTP/2 client support is only available in JDK 9 or above. This means that in JDK 8, the only way to get an HTTP/2 compatible client is through an external library (such as the Grizzly Client).

 

JDK 9 also includes JEP 244 which describes TLS Application-Layer Protocol Negotiation Extension (ALPN), which is essential to all HTTP/2 support in Java whether natively or through a library. This is because HTTP/2 is only supported on major browsers over a secure TLS connection and HTTP/2 requires a protocol negotiation during the TLS handshake, which the javax.net.ssl packages didn't support before JEP 244.

 

Does Payara Server Support HTTP/2?

All Payara Server 5 versions before 5.183 have both HTTP/2 and HTTP/2 server push enabled by default.

 

NOTE: Although HTTP/2 isn't strictly prohibited without SSL, at the time of writing this blog no major browsers support HTTP/2 on an insecure connection.

 

Since 5.183, server push is disabled by default. This is because server push was causing some issues. Although a lot of these issues have been fixed, it is still disabled by default until the HTTP/2 push experience has been smoothed out. You can still manually enable this feature in Payara Server to try it out in your own environment. Server push can be enabled through either the admin console or the asadmin utility.

 

Asadmin Utility

asadmin> set configs.config.server-config.network-config.protocols.protocol.http-listener-2.http.http2-push-enabled=true

 

Admin Console

Admin Console

 

 

How Does Payara Server Support HTTP/2 Without Native JDK Support?

Since JEP 244 wasn't backported to JDK 8, Payara Server 5 needed to either only support JDK 9 and above, or find a way to support HTTP/2 on JDK 8. Fortunately Grizzly produced an ALPN JAR (from now on referred to as Grizzly NPN Bootstrap) which, when placed on the Java boot classpath would forcefully replace the JDK SSL classes used in ALPN negotiation. While this sounds great at a glance, any time the internal JDK classes change, it risks breaking the replacement JAR. Because of this, the inclusion of each new ALPN JAR would completely replace the list of JDK 8 update versions that would work with HTTP/2.

 

Payara Server utilises the fact that the start command runs a separate Java process with a customised list of JVM options as well as versioned JVM options to provide a different ALPN JAR depending on the running JDK update version. This means that for any given release, Payara Server can now work for every JDK 8 update version available at that time. The inevitable problem with this however, is that any new JDK update may require a new ALPN JAR to be added to the runtime options.

 

 

Does This Mean I Can Patch My Payara Server Installation to Support Later JDKs?

Yes! Each JDK 8 update version should have a corresponding Grizzly NPN Bootstrap JAR version that can be used to allow HTTP/2 to work seamlessly with that JDK version. In Payara Server 5.184 the mappings from JDK version to NPN bootstrap version are as follows:

 

Minimum JDK Version Maximum JDK Version Grizzly NPN Bootstrap Version
N/A 1.8u120 1.6
1.8u121 1.8u160 1.7
1.8u161 1.8u190 1.8
1.8u191 N/A 1.8.1

 

NOTE: the JDK selection can only check the JDK version, and not the build number. Be aware that issues may still arise if a backport of an API change has been made to a subsequent build of a JDK version. For example, specific builds of 1.8u182 will require NPN Bootstrap JAR 1.8.1, despite this chart saying otherwise.

 

These NPN version mappings are specified in the domain.xml, and may be updated with additional versions:

<jvm-options>[1.8.0|1.8.0u120]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.6.jar</jvm-options>
<jvm-options>[1.8.0u121|1.8.0u160]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.7.jar</jvm-options>
<jvm-options>[1.8.0u161|1.8.0u190]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.8.jar</jvm-options>
<jvm-options>[1.8.0u191|1.8.0u500]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.8.1.jar</jvm-options>

So if for example you found that JDK 1.8u200 breaks with NPN bootstrap version 1.8.1 and you see a more recent NPN bootstrap JAR, you could download a more recent JAR and add an additional domain.xml entry, for example:

<jvm-options>[1.8.0|1.8.0u120]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.6.jar</jvm-options>
<jvm-options>[1.8.0u121|1.8.0u160]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.7.jar</jvm-options>
<jvm-options>[1.8.0u161|1.8.0u190]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.8.jar</jvm-options>
<jvm-options>[1.8.0u191|1.8.0u200]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-1.8.1.jar</jvm-options>
<jvm-options>[1.8.0u201|1.8.0u500]-Xbootclasspath/p:${com.sun.aas.installRoot}/lib/grizzly-npn-bootstrap-new-version.jar</jvm-options>

 

How Can I Tell if the ALPN JAR Version is Incorrect?

When the ALPN JAR version is incorrect and you make requests to the server, you often may find errors in the server logs such as the following:

 

[2018-08-19T19:07:07.724+0000] [] [WARNING] [] [org.glassfish.grizzly.filterchain.DefaultFilterChain] [tid: _ThreadID=48 _ThreadName=http-thread-pool::https-listener(1)] [timeMillis: 1534705627724] [levelValue: 900] [[
GRIZZLY0013: Exception during FilterChain execution
java.lang.NoClassDefFoundError: sun/security/ssl/EllipticCurvesExtension
at sun.security.ssl.Handshaker.getActiveProtocols(Handshaker.java:793)
at sun.security.ssl.Handshaker.activate(Handshaker.java:549)
at sun.security.ssl.SSLEngineImpl.kickstartHandshake(SSLEngineImpl.java:744)
at sun.security.ssl.SSLEngineImpl.beginHandshake(SSLEngineImpl.java:771)
...
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
at java.lang.Thread.run(Thread.java:748)
]]

[2018-10-17T15:20:32.654+0200] [Payara 5.183] [WARNING] [] [org.glassfish.grizzly.filterchain.DefaultFilterChain] [tid: _ThreadID=94 _ThreadName=http-thread-pool::http-listener-2(1)] [timeMillis: 1539782432654] [levelValue: 900] [[
GRIZZLY0013: Exception during FilterChain execution
java.lang.NoSuchMethodError: sun.security.ssl.SSLSessionImpl.<init>(Lsun/security/ssl/ProtocolVersion;Lsun/security/ssl/CipherSuite;Ljava/util/Collection;Ljava/security/SecureRandom;Ljava/lang/String;IZ)V
at sun.security.ssl.ServerHandshaker.clientHello(ServerHandshaker.java:862)
at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:245)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1077)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:1017)
...
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
at java.lang.Thread.run(Thread.java:748)
]]

When you see errors like these, it likely means the wrong version of the ALPN JAR has been used. Try out the nearest Grizzly NPN Bootstrap JAR versions to find the one that fits your JDK distribution.

 

Conclusion

HTTP/2 support in Java 8 is complicated, and the solution provided by Payara makes the best of a bad situation. When Payara Server introduces JDK 9 support, the issue of ALPN patched JARs won't exist and all JDK 9 and above versions should work with no problems. Until that point, while HTTP/2 is possible, it's good to be aware of the potential JDK compatibility issues.

 

 

Comments