Jetty Logo
Version: 9.4.5.v20170502
Contact the core Jetty developers at www.webtide.com

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

Chapter 15. Application Layer Protocol Negotiation (ALPN)

Table of Contents

Introducing ALPN

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 of 2016.

For those browsers that support HTTP/2, they all now support the ALPN negotiation layers for TLS. Starting with Jetty 9.3.0, only ALPN is supported by Jetty.

Introducing ALPN

The Jetty project provides an implementation of the TLS extension for ALPN for OpenJDK 7 and OpenJDK 8. ALPN allows the application layer to negotiate which protocol to use over the secure connection.

Any protocol can be negotiated by ALPN within a TLS connection. The protocols that are most commonly negotiated are HTTP/2 (for browsers that support it) and, historically, SPDY. The ALPN implementation is therefore not HTTP/2 or SPDY specific in any way. Jetty’s ALPN implementation, although hosted under the umbrella of the Jetty project, is independent of Jetty (the Servlet Container); you can use the ALPN implementation in any other Java network server.

The Jetty distribution will automatically enable ALPN when it is needed to by a HTTP/2 connector, so for the most part ALPN is transparent to the average deployer. This section provides the detail required for non-standard deployments or developing to the ALPN API.

Starting the JVM

To enable ALPN support, start 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 Jar file,such as the one at the Maven coordinates org.mortbay.jetty.alpn:alpn-boot.

Be certain to get the ALPN Boot Jar version which matches the version of your JRE.

Starting in OSGi

To use ALPN in an OSGi environment, in addition to putting the ALPN jar on the boot classpath for the container, 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.

Understanding the ALPN API

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.

Client Example

SSLContext sslContext = ...;
final SSLSocket sslSocket = (SSLSocket)context.getSocketFactory().createSocket("localhost", server.getLocalPort());

ALPN.put(sslSocket, new ALPN.ClientProvider()
{
    @Override
    public boolean supports()
    {
        return true;
    }

    @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 supports(), protocols(), unsupported() and selected(String), so that the client application can:

  • Decide whether to support ALPN
  • Provide the protocols supported
  • Know whether the server supports ALPN
  • Know the protocol chosen by the server

Server Example

The example for SSLEngine is identical, and you just need to replace the SSLSocket instance with an SSLEngine instance.

final SSLSocket sslSocket = ...;
ALPN.put(sslSocket, new ALPN.ServerProvider()
{
    @Override
    public void unsupported()
    {
        ALPN.remove(sslSocket);
    }

    @Override
    public String select(List<String> protocols);
    {
        ALPN.remove(sslSocket);
        return protocols.get(0);
    }
});

The ALPN implementation calls ALPN.ServerProvider methods unsupported(), and select(List<String>), so that the server application can:

  • know whether the client supports ALPN.
  • select one of the protocols the client supports.

Implementation Details

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.

Unit Tests

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>

Debugging

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.

License Details

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.

Versions

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 versionALPN 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


How to build ALPN

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/jdk7u/jdk7u jdk7u # OpenJDK 7
$ hg clone http://hg.openjdk.java.net/jdk8u/jdk8u jdk8u # OpenJDK 8
$ 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 these pages: OpenJDK 7 / OpenJDK 8.

You will then need to compare and incorporate the OpenJDK source changes into the modified OpenJDK classes at the ALPN GitHub Repository, branch openjdk7 for OpenJDK 7 and branch master for OpenJDK 8.

See an error or something missing? Contribute to this documentation at Github!(Generated: 2017-05-02)