Skip to content

Commit

Permalink
Add unified NextProtoNego extension support to SslContext
Browse files Browse the repository at this point in the history
Motivation:

- OpenSslEngine and JDK SSLEngine (+ Jetty NPN) have different APIs to
  support NextProtoNego extension.
  - It is impossible to configure NPN with SslContext when the provider
    type is JDK.

Modification:

- Implement NextProtoNego extension by overriding the behavior of
  SSLSession.getProtocol() for both OpenSSLEngine and JDK SSLEngine.
  - SSLEngine.getProtocol() returns a string delimited by a colon (':')
    where the first component is the transport protosol (e.g. TLSv1.2)
    and the second component is the name of the application protocol
- Remove the direct reference of Jetty NPN classes from the examples
- Add SslContext.newApplicationProtocolSelector

Result:

- A user can now use both JDK SSLEngine and OpenSslEngine for NPN-based
  protocols such as HTTP2 and SPDY
  • Loading branch information
trustin committed May 21, 2014
1 parent d4f2488 commit 861ed1e
Show file tree
Hide file tree
Showing 17 changed files with 634 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;

import javax.net.ssl.SSLException;
Expand Down Expand Up @@ -60,7 +60,13 @@ public class SpdyClient {
private EventLoopGroup workerGroup;

public SpdyClient(String host, int port) throws SSLException {
sslCtx = SslContext.newClientContext(SslProvider.JDK, InsecureTrustManagerFactory.INSTANCE);
sslCtx = SslContext.newClientContext(
null, InsecureTrustManagerFactory.INSTANCE, null,
SslContext.newApplicationProtocolSelector(
SelectedProtocol.SPDY_3_1.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0);

this.host = host;
this.port = port;
httpResponseHandler = new HttpResponseClientHandler();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@
import io.netty.handler.codec.spdy.SpdyHttpEncoder;
import io.netty.handler.codec.spdy.SpdySessionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import org.eclipse.jetty.npn.NextProtoNego;

import javax.net.ssl.SSLEngine;

import static io.netty.handler.codec.spdy.SpdyVersion.*;
import static io.netty.util.internal.logging.InternalLogLevel.*;
Expand All @@ -45,14 +41,8 @@ public SpdyClientInitializer(SslContext sslCtx, HttpResponseClientHandler httpRe

@Override
public void initChannel(SocketChannel ch) throws Exception {
SslHandler sslHandler = sslCtx.newHandler(ch.alloc());
SSLEngine engine = sslHandler.engine();
NextProtoNego.put(engine, new SpdyClientProvider());
NextProtoNego.debug = true;

ChannelPipeline pipeline = ch.pipeline();

pipeline.addLast("ssl", sslHandler);
pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc()));
pipeline.addLast("spdyFrameCodec", new SpdyFrameCodec(SPDY_3_1));
pipeline.addLast("spdyFrameLogger", new SpdyFrameLogger(INFO));
pipeline.addLast("spdySessionHandler", new SpdySessionHandler(SPDY_3_1, false));
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import io.netty.channel.ChannelInboundHandler;
import io.netty.handler.codec.spdy.SpdyOrHttpChooser;
import org.eclipse.jetty.npn.NextProtoNego;

import javax.net.ssl.SSLEngine;
import java.util.logging.Logger;
Expand All @@ -41,8 +40,8 @@ public SpdyOrHttpHandler(int maxSpdyContentLength, int maxHttpContentLength) {

@Override
protected SelectedProtocol getProtocol(SSLEngine engine) {
SpdyServerProvider provider = (SpdyServerProvider) NextProtoNego.get(engine);
SelectedProtocol selectedProtocol = provider.getSelectedProtocol();
String[] protocol = engine.getSession().getProtocol().split(":");
SelectedProtocol selectedProtocol = SelectedProtocol.protocol(protocol[1]);

logger.info("Selected Protocol is " + selectedProtocol);
return selectedProtocol;
Expand Down
27 changes: 10 additions & 17 deletions example/src/main/java/io/netty/example/spdy/server/SpdyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;

import java.util.Arrays;

/**
* A SPDY Server that responds to a GET request with a Hello World.
* <p>
Expand Down Expand Up @@ -72,7 +74,6 @@ public void run() throws Exception {
}

public static void main(String[] args) throws Exception {
checkForNpnSupport();
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
Expand All @@ -86,21 +87,13 @@ public static void main(String[] args) throws Exception {

// Configure SSL.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(SslProvider.JDK, ssc.certificate(), ssc.privateKey());
new SpdyServer(sslCtx, port).run();
}
SslContext sslCtx = SslContext.newServerContext(
ssc.certificate(), ssc.privateKey(), null, null,
Arrays.asList(
SelectedProtocol.SPDY_3_1.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0);

private static void checkForNpnSupport() {
try {
Class.forName("sun.security.ssl.NextProtoNegoExtension");
} catch (ClassNotFoundException ignored) {
System.err.println();
System.err.println("Could not locate Next Protocol Negotiation (NPN) implementation.");
System.err.println("The NPN jar should have been made available when building the examples with maven.");
System.err.println("Please check that your JDK is among those supported by Jetty-NPN:");
System.err.println("http://wiki.eclipse.org/Jetty/Feature/NPN#Versions");
System.err.println();
throw new IllegalStateException("Could not locate NPN implementation. See console err for details.");
}
new SpdyServer(sslCtx, port).run();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import org.eclipse.jetty.npn.NextProtoNego;

import javax.net.ssl.SSLEngine;

/**
* Sets up the Netty pipeline
Expand All @@ -38,15 +34,7 @@ public SpdyServerInitializer(SslContext sslCtx) {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();

SslHandler sslHandler = sslCtx.newHandler(ch.alloc());
SSLEngine engine = sslHandler.engine();
p.addLast("ssl", sslHandler);

// Setup NextProtoNego with our server provider
NextProtoNego.put(engine, new SpdyServerProvider());
NextProtoNego.debug = true;

p.addLast("ssl", sslCtx.newHandler(ch.alloc()));
// Negotiates with the browser if SPDY or HTTP is going to be used
p.addLast("handler", new SpdyOrHttpHandler());
}
Expand Down

This file was deleted.

11 changes: 11 additions & 0 deletions handler/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@
<artifactId>bcpkix-jdk15on</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.npn</groupId>
<artifactId>npn-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.netty.buffer.ByteBufInputStream;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
Expand All @@ -38,6 +39,7 @@
public final class JdkSslClientContext extends JdkSslContext {

private final SSLContext ctx;
private final ApplicationProtocolSelector nextProtocolSelector;

/**
* Creates a new instance.
Expand Down Expand Up @@ -105,10 +107,12 @@ public JdkSslClientContext(

super(ciphers);

if (nextProtocolSelector != null) {
if (nextProtocolSelector != null && !JettyNpnSslEngine.isAvailable()) {
throw new SSLException("NPN/ALPN unsupported: " + nextProtocolSelector);
}

this.nextProtocolSelector = nextProtocolSelector;

try {
if (certChainFile == null) {
ctx = SSLContext.getInstance(PROTOCOL);
Expand Down Expand Up @@ -166,7 +170,7 @@ public boolean isClient() {

@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return null;
return nextProtocolSelector;
}

@Override
Expand All @@ -178,4 +182,13 @@ public List<String> nextProtocols() {
public SSLContext context() {
return ctx;
}

@Override
SSLEngine wrapEngine(SSLEngine engine) {
if (nextProtocolSelector == null) {
return engine;
} else {
return new JettyNpnSslEngine(engine, nextProtocolSelector);
}
}
}
6 changes: 4 additions & 2 deletions handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public final SSLEngine newEngine(ByteBufAllocator alloc) {
engine.setEnabledCipherSuites(cipherSuites);
engine.setEnabledProtocols(PROTOCOLS);
engine.setUseClientMode(isClient());
return engine;
return wrapEngine(engine);
}

@Override
Expand All @@ -162,9 +162,11 @@ public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int pe
engine.setEnabledCipherSuites(cipherSuites);
engine.setEnabledProtocols(PROTOCOLS);
engine.setUseClientMode(isClient());
return engine;
return wrapEngine(engine);
}

abstract SSLEngine wrapEngine(SSLEngine engine);

private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
if (ciphers == null) {
return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
Expand Down
Loading

0 comments on commit 861ed1e

Please sign in to comment.