![]() Version: 9.4.12.v20180830 |
private support for your internal/customer projects ... custom extensions and distributions ... versioned snapshots for indefinite support ... scalability guidance for your apps and Ajax/Comet projects ... development services for sponsored feature development
Table of Contents
The development of new web protocols such as HTTP/2 raised the need of protocol negotiation within a Transport Layer Security (TLS) handshake. A protocol negotiation called ALPN (Application Layer Protocol Negotiation - RFC7301) has been defined to accomplish this.
ALPN has now replaced the older (and now fully deprecated) NPN in the general Web as of 2016.
For those browsers that support HTTP/2, they all now support ALPN. Starting with Jetty 9.3.0, only ALPN is supported by Jetty.
Application Layer Protocol Negotiation (ALPN) is a TLS extension that allows client and server to negotiate the application protocol that they will use to communicate within the encryption provided by TLS.
Any protocol can be negotiated by ALPN within a TLS connection; the protocols that are most commonly negotiated are HTTP/2 and HTTP/1.1.
Browsers only support HTTP/2 over TLS by negotiating the HTTP/2 protocol via ALPN. You need to configure the server to support TLS and ALPN if you want browsers to use the HTTP/2 protocol, otherwise they will default to HTTP/1.1.
In the Jetty project, ALPN is used in two artifacts: jetty-alpn-client
and jetty-alpn-server
, respectively for the client and for the server.
When using Jetty as a standalone server via the Jetty distribution, the jetty-alpn-server
artifact is automatically included in the server classpath by the Jetty module system.
When using Jetty embedded, the jetty-alpn-client
and jetty-alpn-server
artifacts must be included in the classpath, respectively for client and server use cases.
The ALPN implementation is provided to these two artifacts with the following three options:
For JDK 8 only, a provider based on modified OpenJDK classes
-Xbootclasspath/p
option on command lineFor JDK 8 or later, a provider based on the Conscrypt security provider
For JDK 9 or later, a provider based on the ALPN APIs present in the JDK
The first, although hosted under the umbrella of the Jetty project, is independent of Jetty (the Servlet Container); you can use it in any other Java network server.
Each provider above provides an ALPN service implementation; Jetty uses the ServiceLoader
mechanism to load these service implementations.
At least one valid provider must be present in the server classpath.
For example, using JDK 8 with the JDK 9 ALPN provider is an invalid combination.
The absence of valid implementations is an error at startup (see also the troubleshooting section).
There may be multiple ALPN service providers in the server classpath.
When a new connection is created, an SSLEngine
instance is associated to it; each SSLEngine
is passed all service implementations, until one accepts it.
It is therefore possible to have multiple providers active at the same time, for example the JDK 9 provider and the Conscrypt provider, and at runtime the correct one will be chosen by the Jetty runtime.
When using JDKs based on OpenJDK 8 (for JDK 9 see above), and you do not or cannot use Conscrypt, you can use Jetty’s ALPN boot library to provide the ALPN service implementation, via the alpn-boot
artifact.
The Jetty ALPN boot library modifies the relevant OpenJDK classes to add ALPN support and provides an ALPN API that application can use to enable ALPN.
When using Jetty as a standalone server via the Jetty distribution, ALPN support is automatically enabled when the http2
module is enabled.
This enables transitively the alpn-8
module which puts the jetty-alpn-openjdk8-server
artifact in the server classpath, providing the ALPN OpenJDK 8 service implementation.
When using Jetty embedded, the ALPN support is provided by the jetty-alpn-openjdk8-client
and jetty-alpn-openjdk8-server
artifacts, respectively for client usage and server usage.
To get ALPN working with the Jetty ALPN Boot library, you need:
jetty-alpn-openjdk8-client
artifact or the jetty-alpn-openjdk8-server
artifact in the classpathStart the JVM as follows:
java -Xbootclasspath/p:<path_to_alpn_boot_jar> ...
Where path_to_alpn_boot_jar
is the path on the file system for the alpn-boot
artifact, such as the one at the Maven coordinates org.mortbay.jetty.alpn:alpn-boot
.
Be certain to get the ALPN boot artifact version that matches the version of your JRE.
When using JDK 8 or later, you can use the Conscrypt security provider to provide the ALPN service implementation.
Conscrypt binds natively to BoringSSL (a fork of OpenSSL by Google), so ALPN will be supported via the support provided by BoringSSL (bundled together with Conscrypt).
When using Jetty as a standalone server via the Jetty distribution, ALPN is enabled by enabling the conscrypt
module.
When using Jetty embedded, ALPN is enabled by the jetty-alpn-conscrypt-client
and jetty-alpn-conscrypt-server
artifacts, respectively for client usage and server usage.
In addition, you also need the Conscrypt artifacts, typically the org.conscrypt:conscrypt-openjdk-uber
artifact.
All these artifacts must be added to the classpath.
When using JDK 9 or later and Jetty as a standalone server via the Jetty distribution, ALPN support is automatically enabled when the http2
module is enabled.
This enables transitively the alpn-9
module which puts the jetty-alpn-java-server
artifact in the server classpath, providing the ALPN JDK 9 service implementation.
When using JDK 9 or later and Jetty embedded, the ALPN service implementation is provided by the jetty-alpn-java-client
and jetty-alpn-java-server
artifacts, respectively for client usage and server usage, and must be added to the classpath.
To use ALPN in an OSGi environment, in addition to what described above, you will also need to deploy the jetty-osgi-alpn
jar.
This jar contains a Fragment-Host
directive that ensures the ALPN classes will be available from the system bundle.
You can download the jetty-osgi-alpn jar from Maven Central.
Note
OSGi requires a
ServiceLoader
implementation for Jetty to function properly. OSGi leverages Apache Aries SPI Fly for this functionality. You can read more about OSGi andServiceLoader
here.
When starting the Jetty server, especially when using Jetty embedded, it may be possible that you see an error similar to this:
IllegalStateException: no ALPN processor
The error means that you don’t have the ALPN dependencies setup correctly in your classpath.
For example, you may have the jetty-alpn-java-server
artifact in the classpath (which is correct when using JDK 9), but run your application with JDK 8.
Another example is when you have correctly put the alpn-boot
artifact in the boot classpath, but you don’t have the jetty-alpn-openjdk8-server
artifact in the classpath.
The following sections only refer to the API and implementation of ALPN using the Jetty boot library.
The Jetty ALPN boot library is conceptually made of two parts: the ALPN APIs and the ALPN implementation.
The ALPN API is provided by the org.eclipse.jetty.alpn:alpn-api
artifact.
This artifact is only needed by application code that uses the ALPN APIs.
The ALPN implementation is provided by the org.mortbay.jetty.alpn:alpn-boot
artifact and consist of modifications to the OpenJDK classes.
The org.mortbay.jetty.alpn:alpn-boot
artifact contains also the classes present in the org.eclipse.jetty.alpn:alpn-api
artifact.
Applications need to interact with ALPN TLS extension protocol negotiations. For example, server applications need to know whether the client supports ALPN, and client applications needs to know whether the server supports ALPN.
To implement this interaction, Jetty’s ALPN implementation provides an API to applications, hosted at Maven coordinates org.eclipse.jetty.alpn:alpn-api
.
You need to declare this dependency as provided, because the alpn-boot
jar already includes it (see the previous section), and it is therefore available from the boot classpath.
The API consists of a single class, org.eclipse.jetty.alpn.ALPN
, and applications need to register instances of SSLSocket
or SSLEngine
with a ClientProvider
or ServerProvider
(depending on whether the application is a client application or server application).
Refer to ALPN
Javadocs and to the examples below for further details about client and server provider methods.
SSLContext sslContext = ...;
SSLSocket sslSocket = (SSLSocket)context.getSocketFactory().createSocket("localhost", server.getLocalPort());
ALPN.put(sslSocket, new ALPN.ClientProvider()
{
@Override
public List<String> protocols()
{
return Arrays.asList("h2", "http/1.1");
}
@Override
public void unsupported()
{
ALPN.remove(sslSocket);
}
@Override
public void selected(String protocol)
{
ALPN.remove(sslSocket);
System.out.println("Protocol Selected is: " + protocol);
}
});
The ALPN implementation calls ALPN.ClientProvider
methods protocols()
, unsupported()
and selected(String)
, so that the client application can:
The example for SSLEngine
is identical, and you just need to replace the SSLSocket
instance with an SSLEngine
instance.
SSLEngine sslEngine = ...;
ALPN.put(sslEngine, new ALPN.ServerProvider()
{
@Override
public void unsupported()
{
ALPN.remove(sslEngine);
}
@Override
public String select(List<String> protocols);
{
ALPN.remove(sslEngine);
return protocols.get(0);
}
});
The ALPN implementation calls ALPN.ServerProvider
methods unsupported()
, and select(List<String>),
so that the server application can:
It is important that implementations of ALPN.ServerProvider
and ALPN.ClientProvider
remove the sslSocket
or sslEngine
when the negotiation is complete, like shown in the examples above.
Failing to do so will cause a memory leak.
You can write and run unit tests that use the ALPN implementation. The solution that we use with Maven is to specify an additional command line argument to the Surefire plugin:
<project>
<properties>
<alpn-boot-version>8.1.4.v20150727</alpn-boot-version>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn-boot-version}/alpn-boot-${alpn-boot-version}.jar
</argLine>
</configuration>
</plugin>
...
</plugins>
</build>
...
</project>
You can enable debug logging for the ALPN implementation in this way:
ALPN.debug = true;
Since the ALPN class is in the boot classpath, we chose not to use logging libraries because we do not want to override application logging library choices; therefore the logging is performed directly on System.err
.
The ALPN implementation relies on modification of a few OpenJDK classes and on a few new classes that need to live in the sun.security.ssl
package.
These classes are released under the same GPLv2+exception
license of OpenJDK.
The ALPN class and its nested classes are released under same license as the classes of the Jetty project.
The ALPN implementation, relying on modifications of OpenJDK classes, updates every time there are updates to the modified OpenJDK classes.
Table 15.1. ALPN vs. OpenJDK versions
OpenJDK version | ALPN version |
---|---|
1.7.0u40 | 7.1.0.v20141016 |
1.7.0u45 | 7.1.0.v20141016 |
1.7.0u51 | 7.1.0.v20141016 |
1.7.0u55 | 7.1.0.v20141016 |
1.7.0u60 | 7.1.0.v20141016 |
1.7.0u65 | 7.1.0.v20141016 |
1.7.0u67 | 7.1.0.v20141016 |
1.7.0u71 | 7.1.2.v20141202 |
1.7.0u72 | 7.1.2.v20141202 |
1.7.0u75 | 7.1.3.v20150130 |
1.7.0u76 | 7.1.3.v20150130 |
1.7.0u79 | 7.1.3.v20150130 |
1.7.0u80 | 7.1.3.v20150130 |
1.8.0 | 8.1.0.v20141016 |
1.8.0u05 | 8.1.0.v20141016 |
1.8.0u11 | 8.1.0.v20141016 |
1.8.0u20 | 8.1.0.v20141016 |
1.8.0u25 | 8.1.2.v20141202 |
1.8.0u31 | 8.1.3.v20150130 |
1.8.0u40 | 8.1.3.v20150130 |
1.8.0u45 | 8.1.3.v20150130 |
1.8.0u51 | 8.1.4.v20150727 |
1.8.0u60 | 8.1.5.v20150921 |
1.8.0u65 | 8.1.6.v20151105 |
1.8.0u66 | 8.1.6.v20151105 |
1.8.0u71 | 8.1.7.v20160121 |
1.8.0u72 | 8.1.7.v20160121 |
1.8.0u73 | 8.1.7.v20160121 |
1.8.0u74 | 8.1.7.v20160121 |
1.8.0u77 | 8.1.7.v20160121 |
1.8.0u91 | 8.1.7.v20160121 |
1.8.0u92 | 8.1.8.v20160420 |
1.8.0u101 | 8.1.9.v20160720 |
1.8.0u102 | 8.1.9.v20160720 |
1.8.0u111 | 8.1.9.v20160720 |
1.8.0u112 | 8.1.10.v20161026 |
1.8.0u121 | 8.1.11.v20170118 |
1.8.0u131 | 8.1.11.v20170118 |
1.8.0u141 | 8.1.11.v20170118 |
1.8.0u144 | 8.1.11.v20170118 |
1.8.0u151 | 8.1.11.v20170118 |
1.8.0u152 | 8.1.11.v20170118 |
1.8.0u161 | 8.1.12.v20180117 |
1.8.0u162 | 8.1.12.v20180117 |
1.8.0u171 | 8.1.12.v20180117 |
1.8.0u172 | 8.1.12.v20180117 |
1.8.0u181 | 8.1.12.v20180117 |
This section is for Jetty developers that need to update the ALPN implementation with the OpenJDK versions.
Clone the OpenJDK repository with the following command:
$ hg clone http://hg.openjdk.java.net/jdk8u/jdk8u jdk8u $ cd !$ $ ./get_source.sh
To update the source to a specific tag, use the following command:
$ ./make/scripts/hgforest.sh update <tag-name>
The list of OpenJDK tags can be obtained from this page: OpenJDK 8 Tags.
You will then need to compare and incorporate the OpenJDK source changes into the modified OpenJDK classes at the ALPN GitHub Repository, branch master
.