blob: 34ee58af58ccf1a5392933630216953d076743e4 [file] [log] [blame]
// ========================================================================
// 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].