| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ======================================================================== |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| |
| [[alpn]] |
| === 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. |
| |
| [[alpn-starting]] |
| ==== Starting the JVM |
| |
| To enable ALPN support, start the JVM as follows: |
| |
| [source, plain, subs="{sub-order}"] |
| ---- |
| 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 link:#alpn-versions[to get the ALPN Boot Jar version which matches the version of your JRE]. |
| |
| [[alpn-osgi]] |
| ===== 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 http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-alpn/[jetty-osgi-alpn jar] from Maven Central. |
| |
| [[alpn-understanding]] |
| ==== 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. |
| |
| [[alpn-client-example]] |
| ==== Client Example |
| |
| [source, java, subs="{sub-order}"] |
| ---- |
| 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 |
| |
| [[alpn-server-example]] |
| ==== Server Example |
| |
| The example for SSLEngine is identical, and you just need to replace the SSLSocket instance with an SSLEngine instance. |
| |
| [source, java, subs="{sub-order}"] |
| ---- |
| 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. |
| |
| [[alpn-implementation]] |
| ==== 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. |
| |
| [[alpn-tests]] |
| ==== 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: |
| |
| [source, xml, subs="{sub-order}"] |
| ---- |
| <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> |
| ---- |
| |
| [[alpn-debugging]] |
| ==== 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`. |
| |
| [[alpn-license-details]] |
| ==== 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. |
| |
| [[alpn-versions]] |
| ==== Versions |
| |
| The ALPN implementation, relying on modifications of OpenJDK classes, updates every time there are updates to the modified OpenJDK classes. |
| |
| .ALPN vs. OpenJDK versions |
| [cols=",",options="header",] |
| |============================= |
| |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 |
| |============================= |
| |
| [[alpn-build]] |
| ==== 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: |
| |
| [source, screen, subs="{sub-order}"] |
| .... |
| $ 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: |
| |
| [source, screen, subs="{sub-order}"] |
| .... |
| $ ./make/scripts/hgforest.sh update <tag-name> |
| |
| .... |
| |
| The list of OpenJDK tags can be obtained from these pages: http://hg.openjdk.java.net/jdk7u/jdk7u/tags[OpenJDK 7] / http://hg.openjdk.java.net/jdk8u/jdk8u/tags[OpenJDK 8]. |
| |
| You will then need to compare and incorporate the OpenJDK source changes into the modified OpenJDK classes at the https://github.com/jetty-project/jetty-alpn[ALPN GitHub Repository], branch `openjdk7` for OpenJDK 7 and branch `master` for OpenJDK 8. |