//
//  ========================================================================
//  Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//  ========================================================================
//  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

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 options:

* For Java 8 only, a provider based on a pure Java implementation (no native code)
** For Java 8 up to `1.8.0_242` included, this provider uses modified OpenJDK classes
  and requires the `-Xbootclasspath/p:` option on command line
** For Java 8 from `1.8.0_252` included and later, this provider uses the standard OpenJDK
  ALPN APIs introduced in Java 9 (see below) that have been backported to `1.8.0_252` and
  does not require the `-Xbootclasspath/p:` option on command line
* For Java 8 or later, a provider based on the link:#conscrypt[Conscrypt security provider]
** Works with JDK 8 or later and provides improved performance
** Binds to the OpenSSL native library shipped by Conscrypt and is therefore only available on the platforms supported by Conscrypt
* For Java 9 or later, a provider based on the standard OpenJDK ALPN APIs
** Works with JDK 9 or later, pure Java implementation (no native code)
** Lower performance than Conscrypt

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 link:#alpn-troubleshooting[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.

[[alpn-openjdk8]]
==== ALPN and OpenJDK 8

When using JDKs based on OpenJDK 8 (for JDK 9 see link:#alpn-jdk9[here]), and you do not or
cannot use link:#conscrypt[Conscrypt], the ALPN implementation is provided by the
`jetty-alpn-openjdk8-client` or `jetty-alpn-openjdk8-server` artifacts.

For Java 8 versions up to `1.8.0_242` included, you also need the Jetty's ALPN boot library
to provide the ALPN service implementation, via the `alpn-boot` artifact.
For Java 8 versions from `1.8.0_252` included and later, Jetty's ALPN boot library is not
necessary because the OpenJDK ALPN APIs have been backported to `1.8.0_252` and the
`jetty-alpn-openjdk8-*` artifacts can use these backported APIs if their presence is detected.

Alternatively, you can use the link:#alpn-openjdk8-agent[Jetty ALPN agent], that in turn uses
theJetty ALPN boot library to transform the relevant OpenJDK classes when they are loaded.

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 Java 8, you must have the `jetty-alpn-openjdk8-client` artifact or
the `jetty-alpn-openjdk8-server` artifact in the classpath.

Additionally, if you are using OpenJDK `1.8.0_242` or earlier, you need the Jetty ALPN boot
library (corresponding to the exact OpenJDK version you are using) in the boot classpath,
or alternatively you need the link:#alpn-openjdk8-agent[Jetty ALPN agent].

In the case of the Jetty ALPN boot library, 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` artifact, such as the one at the Maven coordinates `org.mortbay.jetty.alpn:alpn-boot`.

____
[IMPORTANT]
Be certain to get the
link:#alpn-versions[ALPN boot artifact version that matches the version of your JRE].
____

[[alpn-openjdk8-agent]]
==== ALPN agent and OpenJDK 8

The Jetty Project also maintains the
https://github.com/jetty-project/jetty-alpn-agent[Jetty ALPN agent], which is a JVM
agent that provides the ALPN implementation.
The Jetty ALPN agent can be use in alternative (never together) with the
link:#alpn-openjdk8[ALPN boot library].

The Jetty ALPN agent contains the ALPN boot libraries for every JDK 8 version.
The agent can be used only with Java 8, but works with _any_ Java 8 version.

The Jetty ALPN agent detects the JDK version currently running, picks the correspondent
ALPN boot library (or picks none if the JDK version is `1.8.0_252` or later), and
transforms, if necessary, the relevant OpenJDK classes to provide the ALPN support.

To use the Jetty ALPN agent, start the JVM as follows:

[source, plain, subs="{sub-order}"]
----
java -javaagent:<path_to_alpn_agent_jar> ...
----

____
[NOTE]
The Jetty ALPN agent works with any Java 8 version. It is _required_ if you use
an OpenJDK version up to `1.8.0_242` included, and it is _optional_ if you use an
OpenJDK version equal or greater than `1.8.0_252`.

The Jetty ALPN agent can be left on the command line even when using an OpenJDK version
equal or greater than `1.8.0_252` but we recommend to remove it from the command line
when you use OpenJDK `1.8.0_252` or later.
____

[[alpn-conscrypt]]
==== ALPN and Conscrypt

When using JDK 8 or later, you can use the https://conscrypt.org/[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.

[[alpn-jdk9]]
==== ALPN and JDK 9

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.

[[alpn-osgi]]
===== Starting in OSGi

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 https://repo1.maven.org/maven2/org/eclipse/jetty/osgi/jetty-osgi-alpn/[jetty-osgi-alpn jar] from Maven Central.

____
[NOTE]
OSGi *requires* a `ServiceLoader` implementation for Jetty to function properly.
OSGi leverages http://aries.apache.org/modules/spi-fly.html[Apache Aries SPI Fly] for this functionality.
You can read more about OSGi and `ServiceLoader` http://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html[here.]
____

[[alpn-troubleshooting]]
==== ALPN Troubleshooting

When starting the Jetty server, especially when using Jetty embedded, it may be possible that you see an error similar to this:

[source, plain, subs="{sub-order}"]
----
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.

[[alpn-openjdk8-details]]
==== Details about ALPN and OpenJDK 8

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.

[[alpn-openjdk8-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-openjdk8-client-example]]
==== Client Example

[source, java, subs="{sub-order}"]
----
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:

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

[[alpn-openjdk8-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}"]
----
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:

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

[[alpn-openjdk8-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-openjdk8-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-openjdk8-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-openjdk8-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="a,",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
|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
|1.8.0u191 |8.1.13.v20181017
|1.8.0u192 |8.1.13.v20181017
|1.8.0u201 |8.1.13.v20181017
|1.8.0u202 |8.1.13.v20181017
|1.8.0u211^[1]^ |8.1.13.v20181017
|1.8.0u212 |8.1.13.v20181017
|1.8.0u221^[1]^ |8.1.13.v20181017
|1.8.0u222 |8.1.13.v20181017
|1.8.0u231^[1]^ |8.1.13.v20181017
|1.8.0u232 |8.1.13.v20181017
|1.8.0u241^[1]^ |8.1.13.v20181017
|1.8.0u242 |8.1.13.v20181017
|1.8.0u252 and later | NOT NECESSARY
|=============================
^[1]^ These are Oracle releases for which the source code is not available,
or it is unclear what exactly is because there is no correspondent tag in
the OpenJDK repository.
We assume that the source code for these releases is equivalent (at least
for the files modified to make ALPN work) to the release that follows
(for example, for 1.8.0u211 we assume that the source code is equivalent
to 1.8.0u212, for 1.8.0u221 we assume 1.8.0u222, etc.).

[[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 https://hg.openjdk.java.net/jdk8u/jdk8u jdk8u
$ 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 this page:
https://hg.openjdk.java.net/jdk8u/jdk8u/tags[OpenJDK 8 Tags].

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 `master`.
