| // ======================================================================== |
| // 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. |
| // ======================================================================== |
| |
| How to build NPN |
| ================ |
| |
| [[npn]] |
| = |
| |
| [[npn-introduction]] |
| == Configuring NPN |
| |
| The Jetty project provides an implementation of the Transport Layer |
| Security (TLS) extension for Next Protocol Negotiation (NPN) for OpenJDK |
| 7 (but not for OpenJDK 8 or greater - see xref:alpn-chapter[] for |
| using a TLS protocol negotiation extension with OpenJDK 8 or greater). |
| NPN allows the application layer to negotiate which protocol to use over |
| the secure connection. |
| |
| NPN currently negotiates using SPDY as an application level protocol on |
| port 443, and also negotiates the SPDY version. However, NPN is not |
| SPDY-specific in any way. Jetty's NPN implementation, 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. |
| |
| [[npn-starting]] |
| == Starting the JVM |
| |
| To enable NPN support, start the JVM as follows: |
| |
| [source, plain, subs="{sub-order}"] |
| ---- |
| java -Xbootclasspath/p:<path_to_npn_boot_jar> ... |
| ---- |
| |
| where `path_to_npn_boot_jar` is the path on the file system for the NPN |
| Boot Jar file, for example, one at the Maven coordinates |
| org.mortbay.jetty.npn:npn-boot. |
| |
| Be aware that the current versions of the npn packages no longer align |
| with Jetty versions. Look at the dates in those file paths before |
| looking at the version number. |
| |
| [[npn-osgi]] |
| === Starting in OSGi |
| |
| To use NPN in an OSGi environment, in addition to putting the NPN jar on |
| the boot classpath for the container, you will also need to deploy the |
| jetty-osgi-npn jar. This jar contains a Fragment-Host directive that |
| ensures the NPN classes will be available from the system bundle. |
| |
| You can download the jetty-osgi-npn jar from maven central: |
| http://central.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-npn/ |
| |
| [[npn-understanding]] |
| == Understanding the NPN API |
| |
| Applications need to interact with NPN TLS extension protocol |
| negotiations. For example, server applications need to know whether the |
| client supports NPN, and client applications needs to know the list of |
| protocols the server supports, and so on. |
| |
| To implement this interaction, Jetty's NPN implementation provides an |
| API to applications, hosted at Maven coordinates |
| org.eclipse.jetty.npn:npn-api. You need to declare this dependency as |
| provided, because the npn-boot Jar already includes it (see the previous |
| section), and it is therefore available in the boot classpath. |
| |
| The API consists of a single class, |
| `org.eclipse.jetty.npn.NextProtoNego`, and applications need to register |
| instances of SSLSocket or SSLEngine with a ClientProvider or |
| ServerProvider (depending on whether the application is a client or |
| server application). Refer to NextProtoNego Javadocs and to the examples |
| below for further details about client and server provider methods. |
| |
| [[client-example]] |
| == Client Example |
| |
| [source, java, subs="{sub-order}"] |
| ---- |
| SSLContext sslContext = ...; |
| final SSLSocket sslSocket = (SSLSocket)context.getSocketFactory().createSocket("localhost", server.getLocalPort()); |
| |
| NextProtoNego.put(sslSocket, new NextProtoNego.ClientProvider() |
| { |
| @Override |
| public boolean supports() |
| { |
| return true; |
| } |
| |
| @Override |
| public void unsupported() |
| { |
| NextProtoNego.remove(sslSocket); |
| } |
| |
| @Override |
| public String selectProtocol(List<String> protocols) |
| { |
| NextProtoNego.remove(sslSocket); |
| return protocols.get(0); |
| } |
| }); |
| ---- |
| |
| The NPN implementation calls `NextProtoNego.ClientProvider` methods |
| `supports()`, `unsupported()` and `selectProtocol(List<String>)`, so |
| that the client application can: |
| |
| * decide whether to support NPN. |
| * know whether the server supports NPN. |
| * select one of the protocols the server supports. |
| |
| [[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 = ...; |
| NextProtoNego.put(sslSocket, new NextProtoNego.ServerProvider() |
| { |
| @Override |
| public void unsupported() |
| { |
| NextProtoNego.remove(sslSocket); |
| } |
| |
| @Override |
| public List<String> protocols() |
| { |
| return Arrays.asList("http/1.1"); |
| } |
| |
| @Override |
| public void protocolSelected(String protocol) |
| { |
| NextProtoNego.remove(sslSocket); |
| System.out.println("Protocol Selected is: " + protocol); |
| } |
| }); |
| ---- |
| |
| The NPN implementation calls `NextProtoNego.ServerProvider` methods |
| `unsupported()`, `protocols()` and `protocolSelected(String),` so that |
| the server application can: |
| |
| * know whether the client supports NPN. |
| * provide the list of protocols the server supports. |
| * know which protocol the client chooses. |
| |
| [[npn-implementation]] |
| == Implementation Details |
| |
| It is common that the NextProtoNego.ServerProvider and the |
| NextProtoNego.ClientProvider are implemented as (anonymous) inner |
| classes, and that their methods' implementations require references to |
| the the sslSocket (or sslEngine), either directly or indirectly. |
| |
| Since the NextProtoNego class holds [sslSocket/sslEngine, provider] |
| pairs in a `WeakHashMap`, if the value (that is, the provider |
| implementation) holds a strong (even indirect) reference to the key, |
| then the `WeakHashMap` entries are never removed, leading to a memory |
| leak. |
| |
| It is important that implementations of `NextProtoNego.ServerProvider` |
| and `NextProtoNego.ClientProvider` remove the `sslSocket` or `sslEngine` |
| when the negotiation is complete, like shown in the examples above. |
| |
| Be aware that declaring the SslConnection as a final local variable and |
| referencing it from within the anonymous NextProtoNego.ServerProvider |
| class generates a hidden field in the anonymous inner class, that may |
| cause a memory leak if the implementation does not call |
| `NextProtoNego.remove()`. |
| |
| [[npn-tests]] |
| == Unit Tests |
| |
| You can write and run unit tests that use the NPN 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> |
| <npn-version>1.1.1.v20121030</npn-version> |
| </properties> |
| |
| <build> |
| <plugins> |
| <plugin> |
| <artifactId>maven-surefire-plugin</artifactId> |
| <configuration> |
| <argLine> |
| -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar |
| </argLine> |
| </configuration> |
| </plugin> |
| |
| ... |
| |
| </plugins> |
| </build> |
| |
| ... |
| |
| </project> |
| ---- |
| |
| [[npn-debugging]] |
| == Debugging |
| |
| You can enable debug logging for the NPN implementation in this way: |
| |
| .... |
| NextProtoNego.debug = true; |
| .... |
| |
| Since the NextProtoNego 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.` |
| |
| [[npn-license-details]] |
| == License Details |
| |
| The NPN 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 NextProtoNego class is released under same license as the classes of |
| the Jetty project. |
| |
| [[npn-versions]] |
| == Versions |
| |
| The NPN implementation, relying on modifications of OpenJDK classes, |
| updates every time there are updates to the modified OpenJDK classes. |
| |
| .NPN vs. OpenJDK versions |
| [cols=",",options="header",] |
| |========================================================== |
| |NPN version |OpenJDK version |
| |1.0.0.v20120402 |1.7.0 - 1.7.0u2 - 1.7.0u3 |
| |1.1.0.v20120525 |1.7.0u4 - 1.7.0u5 |
| |1.1.1.v20121030 |1.7.0u6 - 1.7.0u7 |
| |1.1.3.v20130313 |1.7.0u9 - 1.7.0u10 - 1.7.0u11 |
| |1.1.4.v20130313 |1.7.0u13 |
| |1.1.5.v20130313 |1.7.0u15 - 1.7.0u17 - 1.7.0u21 - 1.7.0u25 |
| |1.1.6.v20130911 |1.7.0u40 - 1.7.0u45 - 1.7.0u51 |
| |1.1.8.v20141013 |1.7.0u55 - 1.7.0u60 - 1.7.0u65 - 1.7.0u67 |
| |1.1.9.v20141016 |1.7.0u71 - 1.7.0u72 |
| |1.1.10.v20150130 |1.7.0u75 - 1.7.0u76 - 1.7.0u79 |
| |1.1.11.v20150415 |1.7.0u80 |
| |========================================================== |
| |
| [[npn-build]] |
| == How to build NPN |
| |
| This section is for Jetty developers that need to update the NPN |
| 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 |
| $ cd jdk7u |
| $ ./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 |
| http://hg.openjdk.java.net/jdk7u/jdk7u/tags[this page]. |
| |
| Then you need to compare and incorporate the OpenJDK source changes into |
| the modified OpenJDK classes at the |
| https://github.com/jetty-project/jetty-npn[NPN GitHub Repository]. |