ALPN java implementation
Motivation: Netty only supports a java NPN implementation provided by npn-api and npn-boot. There is no java implementation for ALPN. ALPN is needed to be compliant with the HTTP/2 spec. Modifications: -SslContext and JdkSslContext to support ALPN -JettyNpn* class restructure for NPN and ALPN common aspects -Pull in alpn-api and alpn-boot optional dependencies for ALPN java implementation Result: -Netty provides access to a java implementation of APLN
This commit is contained in:
parent
0d549706f1
commit
5f232b2220
@ -48,7 +48,6 @@
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -28,6 +28,7 @@ import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http2.Http2OrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.ssl.JettyAlpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.netty.util.CharsetUtil;
|
||||
@ -55,8 +56,15 @@ public final class Http2Client {
|
||||
final SslContext sslCtx;
|
||||
if (SSL) {
|
||||
sslCtx = SslContext.newClientContext(
|
||||
null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
null, InsecureTrustManagerFactory.INSTANCE,
|
||||
Arrays.asList("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
// NOTE: Block ciphers are prohibited by the HTTP/2 specification
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-9.2.2.
|
||||
// The following cipher exists to allow these examples to run with older JREs.
|
||||
// Please consult the HTTP/2 specification when selecting cipher suites.
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"),
|
||||
Arrays.asList(SelectedProtocol.HTTP_2.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyAlpnSslEngineWrapper.instance(),
|
||||
0, 0);
|
||||
} else {
|
||||
sslCtx = null;
|
||||
@ -90,7 +98,8 @@ public final class Http2Client {
|
||||
// Create a simple GET request.
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, URL);
|
||||
request.headers().add(HttpHeaders.Names.HOST, hostName);
|
||||
request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
// TODO: fix me when HTTP/2 supports decompression
|
||||
// request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
channel.writeAndFlush(request);
|
||||
responseHandler.put(streamId, channel.newPromise());
|
||||
streamId += 2;
|
||||
@ -100,7 +109,8 @@ public final class Http2Client {
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, URL2,
|
||||
Unpooled.copiedBuffer(URL2DATA.getBytes(CharsetUtil.UTF_8)));
|
||||
request.headers().add(HttpHeaders.Names.HOST, hostName);
|
||||
request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
// TODO: fix me when HTTP/2 supports decompression
|
||||
// request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
channel.writeAndFlush(request);
|
||||
responseHandler.put(streamId, channel.newPromise());
|
||||
streamId += 2;
|
||||
|
@ -76,7 +76,13 @@ public class HttpResponseHandler extends SimpleChannelInboundHandler<FullHttpRes
|
||||
|
||||
@Override
|
||||
protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
|
||||
int streamId = Integer.parseInt(msg.headers().get(HttpUtil.ExtensionHeaders.Names.STREAM_ID));
|
||||
String streamIdText = msg.headers().get(HttpUtil.ExtensionHeaders.Names.STREAM_ID);
|
||||
if (streamIdText == null) {
|
||||
System.err.println("HttpResponseHandler unexpected message received: " + msg);
|
||||
return;
|
||||
}
|
||||
|
||||
int streamId = Integer.parseInt(streamIdText);
|
||||
ChannelPromise promise = streamidPromiseMap.get(streamId);
|
||||
if (promise == null) {
|
||||
System.err.println("Message received for unknown stream id " + streamId);
|
||||
|
@ -37,10 +37,13 @@ public class Http2OrHttpHandler extends Http2OrHttpChooser {
|
||||
@Override
|
||||
protected SelectedProtocol getProtocol(SSLEngine engine) {
|
||||
String[] protocol = engine.getSession().getProtocol().split(":");
|
||||
if (protocol != null && protocol.length > 1) {
|
||||
SelectedProtocol selectedProtocol = SelectedProtocol.protocol(protocol[1]);
|
||||
System.err.println("Selected Protocol is " + selectedProtocol);
|
||||
return selectedProtocol;
|
||||
}
|
||||
return SelectedProtocol.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChannelHandler createHttp1RequestHandler() {
|
||||
|
@ -25,6 +25,7 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.http2.Http2OrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.JettyAlpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
|
||||
@ -45,10 +46,17 @@ public final class Http2Server {
|
||||
if (SSL) {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
sslCtx = SslContext.newServerContext(
|
||||
ssc.certificate(), ssc.privateKey(), null, null,
|
||||
ssc.certificate(), ssc.privateKey(), null,
|
||||
Arrays.asList("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
// NOTE: Block ciphers are prohibited by the HTTP/2 specification
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-9.2.2.
|
||||
// The following cipher exists to allow these examples to run with older JREs.
|
||||
// Please consult the HTTP/2 specification when selecting cipher suites.
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"),
|
||||
Arrays.asList(
|
||||
SelectedProtocol.HTTP_2.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyAlpnSslEngineWrapper.instance(),
|
||||
0, 0);
|
||||
} else {
|
||||
sslCtx = null;
|
||||
|
@ -27,6 +27,7 @@ import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http2.Http2OrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.codec.memcache.binary.BinaryMemcacheClientCodec;
|
||||
import io.netty.handler.codec.memcache.binary.BinaryMemcacheObjectAggregator;
|
||||
import io.netty.handler.ssl.JettyAlpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
@ -50,6 +51,7 @@ public final class MemcacheClient {
|
||||
sslCtx = SslContext.newClientContext(
|
||||
null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
Arrays.asList(SelectedProtocol.HTTP_2.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyAlpnSslEngineWrapper.instance(),
|
||||
0, 0);
|
||||
} else {
|
||||
sslCtx = null;
|
||||
|
@ -27,6 +27,7 @@ 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.JettyNpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
@ -56,6 +57,7 @@ public final class SpdyClient {
|
||||
final SslContext sslCtx = SslContext.newClientContext(
|
||||
null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyNpnSslEngineWrapper.instance(),
|
||||
0, 0);
|
||||
|
||||
HttpResponseClientHandler httpResponseHandler = new HttpResponseClientHandler();
|
||||
|
@ -24,6 +24,7 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.JettyNpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
|
||||
@ -57,6 +58,7 @@ public final class SpdyServer {
|
||||
SslContext sslCtx = SslContext.newServerContext(
|
||||
ssc.certificate(), ssc.privateKey(), null, null,
|
||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyNpnSslEngineWrapper.instance(),
|
||||
0, 0);
|
||||
|
||||
// Configure the server.
|
||||
|
@ -66,6 +66,22 @@
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* Factory for not wrapping {@link SSLEngine} object and just returning it
|
||||
*/
|
||||
public final class DefaultSslWrapperFactory implements SslEngineWrapperFactory {
|
||||
private static final DefaultSslWrapperFactory INSTANCE = new DefaultSslWrapperFactory();
|
||||
|
||||
private DefaultSslWrapperFactory() {
|
||||
}
|
||||
|
||||
public static DefaultSslWrapperFactory instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, List<String> protocols, boolean isServer) {
|
||||
return engine;
|
||||
}
|
||||
}
|
@ -19,19 +19,19 @@ package io.netty.handler.ssl;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.File;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A client-side {@link SslContext} which uses JDK's SSL/TLS implementation.
|
||||
@ -45,7 +45,7 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public JdkSslClientContext() throws SSLException {
|
||||
this(null, null, null, null, 0, 0);
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +79,7 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
* {@code null} to use the default.
|
||||
*/
|
||||
public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
this(certChainFile, trustManagerFactory, null, null, 0, 0);
|
||||
this(certChainFile, trustManagerFactory, null, null, DefaultSslWrapperFactory.instance(), 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,6 +94,8 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @param wrapperFactory a factory used to wrap the underlying {@link SSLEngine}.
|
||||
* This is required if {@code nextProtocols} is not {@code null} or empty
|
||||
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||
* {@code 0} to use the default value.
|
||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||
@ -102,26 +104,12 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
public JdkSslClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
SslEngineWrapperFactory wrapperFactory,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
super(ciphers);
|
||||
super(ciphers, wrapperFactory);
|
||||
|
||||
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
||||
if (!JettyNpnSslEngine.isAvailable()) {
|
||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
||||
}
|
||||
|
||||
List<String> nextProtoList = new ArrayList<String>();
|
||||
for (String p: nextProtocols) {
|
||||
if (p == null) {
|
||||
break;
|
||||
}
|
||||
nextProtoList.add(p);
|
||||
}
|
||||
this.nextProtocols = Collections.unmodifiableList(nextProtoList);
|
||||
} else {
|
||||
this.nextProtocols = Collections.emptyList();
|
||||
}
|
||||
this.nextProtocols = translateProtocols(nextProtocols);
|
||||
|
||||
try {
|
||||
if (certChainFile == null) {
|
||||
|
@ -20,14 +20,15 @@ import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
|
||||
/**
|
||||
* An {@link SslContext} which uses JDK's SSL/TLS implementation.
|
||||
*/
|
||||
@ -110,10 +111,15 @@ public abstract class JdkSslContext extends SslContext {
|
||||
|
||||
private final String[] cipherSuites;
|
||||
private final List<String> unmodifiableCipherSuites;
|
||||
private final SslEngineWrapperFactory wrapperFactory;
|
||||
|
||||
JdkSslContext(Iterable<String> ciphers) {
|
||||
JdkSslContext(Iterable<String> ciphers, SslEngineWrapperFactory wrapperFactory) {
|
||||
if (wrapperFactory == null) {
|
||||
throw new NullPointerException("wrapperFactory");
|
||||
}
|
||||
cipherSuites = toCipherSuiteArray(ciphers);
|
||||
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
|
||||
this.wrapperFactory = wrapperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,11 +172,7 @@ public abstract class JdkSslContext extends SslContext {
|
||||
}
|
||||
|
||||
private SSLEngine wrapEngine(SSLEngine engine) {
|
||||
if (nextProtocols().isEmpty()) {
|
||||
return engine;
|
||||
} else {
|
||||
return new JettyNpnSslEngine(engine, nextProtocols(), isServer());
|
||||
}
|
||||
return wrapperFactory.wrapSslEngine(engine, nextProtocols(), isServer());
|
||||
}
|
||||
|
||||
private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
|
||||
|
@ -19,16 +19,6 @@ package io.netty.handler.ssl;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.EncryptedPrivateKeyInfo;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
@ -43,9 +33,20 @@ import java.security.cert.CertificateFactory;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.EncryptedPrivateKeyInfo;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
|
||||
/**
|
||||
* A server-side {@link SslContext} which uses JDK's SSL/TLS implementation.
|
||||
*/
|
||||
@ -73,7 +74,7 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
* {@code null} if it's not password-protected.
|
||||
*/
|
||||
public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
this(certChainFile, keyFile, keyPassword, null, null, DefaultSslWrapperFactory.instance(), 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,6 +88,8 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @param wrapperFactory a factory used to wrap the underlying {@link SSLEngine}.
|
||||
* This is required if {@code nextProtocols} is not {@code null} or empty
|
||||
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||
* {@code 0} to use the default value.
|
||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||
@ -95,9 +98,12 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
public JdkSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
SslEngineWrapperFactory wrapperFactory,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
super(ciphers);
|
||||
super(ciphers, wrapperFactory);
|
||||
|
||||
this.nextProtocols = translateProtocols(nextProtocols);
|
||||
|
||||
if (certChainFile == null) {
|
||||
throw new NullPointerException("certChainFile");
|
||||
@ -110,24 +116,6 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
keyPassword = "";
|
||||
}
|
||||
|
||||
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
||||
if (!JettyNpnSslEngine.isAvailable()) {
|
||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (String p: nextProtocols) {
|
||||
if (p == null) {
|
||||
break;
|
||||
}
|
||||
list.add(p);
|
||||
}
|
||||
|
||||
this.nextProtocols = Collections.unmodifiableList(list);
|
||||
} else {
|
||||
this.nextProtocols = Collections.emptyList();
|
||||
}
|
||||
|
||||
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
|
||||
if (algorithm == null) {
|
||||
algorithm = "SunX509";
|
||||
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.eclipse.jetty.alpn.ALPN;
|
||||
import org.eclipse.jetty.alpn.ALPN.ClientProvider;
|
||||
import org.eclipse.jetty.alpn.ALPN.ServerProvider;
|
||||
|
||||
final class JettyAlpnSslEngine extends JettySslEngine {
|
||||
private static boolean available;
|
||||
|
||||
static boolean isAvailable() {
|
||||
updateAvailability();
|
||||
return available;
|
||||
}
|
||||
|
||||
private static void updateAvailability() {
|
||||
if (available) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to get the bootstrap class loader.
|
||||
ClassLoader bootloader = ClassLoader.getSystemClassLoader().getParent();
|
||||
if (bootloader == null) {
|
||||
// If failed, use the system class loader,
|
||||
// although it's not perfect to tell if APLN extension has been loaded.
|
||||
bootloader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
Class.forName("sun.security.ssl.ALPNExtension", true, bootloader);
|
||||
available = true;
|
||||
} catch (Exception ignore) {
|
||||
// alpn-boot was not loaded.
|
||||
}
|
||||
}
|
||||
|
||||
JettyAlpnSslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
|
||||
super(engine, nextProtocols, server);
|
||||
|
||||
if (server) {
|
||||
final String[] array = nextProtocols.toArray(new String[nextProtocols.size()]);
|
||||
final String fallback = array[array.length - 1];
|
||||
|
||||
ALPN.put(engine, new ServerProvider() {
|
||||
@Override
|
||||
public String select(List<String> protocols) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
String p = array[i];
|
||||
if (protocols.contains(p)) {
|
||||
session.setApplicationProtocol(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
session.setApplicationProtocol(fallback);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
session.setApplicationProtocol(null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ALPN.put(engine, new ClientProvider() {
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return nextProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(String protocol) {
|
||||
getSession().setApplicationProtocol(protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInbound() throws SSLException {
|
||||
ALPN.remove(engine);
|
||||
super.closeInbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOutbound() {
|
||||
ALPN.remove(engine);
|
||||
super.closeOutbound();
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* Factory for wrapping {@link SSLEngine} object in {@link JettyAlpnSslEngine} objects
|
||||
*/
|
||||
public final class JettyAlpnSslEngineWrapper implements SslEngineWrapperFactory {
|
||||
private static JettyAlpnSslEngineWrapper instance;
|
||||
|
||||
private JettyAlpnSslEngineWrapper() throws SSLException {
|
||||
if (!JettyAlpnSslEngine.isAvailable()) {
|
||||
throw new SSLException("ALPN unsupported. Is your classpatch configured correctly?" +
|
||||
" See http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-starting");
|
||||
}
|
||||
}
|
||||
|
||||
public static JettyAlpnSslEngineWrapper instance() throws SSLException {
|
||||
if (instance == null) {
|
||||
instance = new JettyAlpnSslEngineWrapper();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, List<String> protocols, boolean isServer) {
|
||||
return new JettyAlpnSslEngine(engine, protocols, isServer);
|
||||
}
|
||||
}
|
@ -16,21 +16,16 @@
|
||||
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.eclipse.jetty.npn.NextProtoNego;
|
||||
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
||||
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
final class JettyNpnSslEngine extends SSLEngine {
|
||||
|
||||
final class JettyNpnSslEngine extends JettySslEngine {
|
||||
private static boolean available;
|
||||
|
||||
static boolean isAvailable() {
|
||||
@ -57,14 +52,8 @@ final class JettyNpnSslEngine extends SSLEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private final SSLEngine engine;
|
||||
private final JettyNpnSslSession session;
|
||||
|
||||
JettyNpnSslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
|
||||
assert !nextProtocols.isEmpty();
|
||||
|
||||
this.engine = engine;
|
||||
session = new JettyNpnSslSession(engine);
|
||||
super(engine, nextProtocols, server);
|
||||
|
||||
if (server) {
|
||||
NextProtoNego.put(engine, new ServerProvider() {
|
||||
@ -84,8 +73,8 @@ final class JettyNpnSslEngine extends SSLEngine {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final String[] list = nextProtocols.toArray(new String[nextProtocols.size()]);
|
||||
final String fallback = list[list.length - 1];
|
||||
final String[] array = nextProtocols.toArray(new String[nextProtocols.size()]);
|
||||
final String fallback = array[array.length - 1];
|
||||
|
||||
NextProtoNego.put(engine, new ClientProvider() {
|
||||
@Override
|
||||
@ -100,181 +89,29 @@ final class JettyNpnSslEngine extends SSLEngine {
|
||||
|
||||
@Override
|
||||
public String selectProtocol(List<String> protocols) {
|
||||
for (String p: list) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
String p = array[i];
|
||||
if (protocols.contains(p)) {
|
||||
session.setApplicationProtocol(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
session.setApplicationProtocol(fallback);
|
||||
return fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JettyNpnSslSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInbound() throws SSLException {
|
||||
NextProtoNego.remove(engine);
|
||||
engine.closeInbound();
|
||||
super.closeInbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOutbound() {
|
||||
NextProtoNego.remove(engine);
|
||||
engine.closeOutbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPeerHost() {
|
||||
return engine.getPeerHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeerPort() {
|
||||
return engine.getPeerPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult wrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException {
|
||||
return engine.wrap(byteBuffer, byteBuffer2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult wrap(ByteBuffer[] byteBuffers, ByteBuffer byteBuffer) throws SSLException {
|
||||
return engine.wrap(byteBuffers, byteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i2, ByteBuffer byteBuffer) throws SSLException {
|
||||
return engine.wrap(byteBuffers, i, i2, byteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException {
|
||||
return engine.unwrap(byteBuffer, byteBuffer2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers) throws SSLException {
|
||||
return engine.unwrap(byteBuffer, byteBuffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, int i, int i2) throws SSLException {
|
||||
return engine.unwrap(byteBuffer, byteBuffers, i, i2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable getDelegatedTask() {
|
||||
return engine.getDelegatedTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInboundDone() {
|
||||
return engine.isInboundDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutboundDone() {
|
||||
return engine.isOutboundDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return engine.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledCipherSuites() {
|
||||
return engine.getEnabledCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledCipherSuites(String[] strings) {
|
||||
engine.setEnabledCipherSuites(strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedProtocols() {
|
||||
return engine.getSupportedProtocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledProtocols() {
|
||||
return engine.getEnabledProtocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledProtocols(String[] strings) {
|
||||
engine.setEnabledProtocols(strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLSession getHandshakeSession() {
|
||||
return engine.getHandshakeSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginHandshake() throws SSLException {
|
||||
engine.beginHandshake();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeStatus getHandshakeStatus() {
|
||||
return engine.getHandshakeStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseClientMode(boolean b) {
|
||||
engine.setUseClientMode(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseClientMode() {
|
||||
return engine.getUseClientMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNeedClientAuth(boolean b) {
|
||||
engine.setNeedClientAuth(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNeedClientAuth() {
|
||||
return engine.getNeedClientAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWantClientAuth(boolean b) {
|
||||
engine.setWantClientAuth(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getWantClientAuth() {
|
||||
return engine.getWantClientAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnableSessionCreation(boolean b) {
|
||||
engine.setEnableSessionCreation(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getEnableSessionCreation() {
|
||||
return engine.getEnableSessionCreation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLParameters getSSLParameters() {
|
||||
return engine.getSSLParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSSLParameters(SSLParameters sslParameters) {
|
||||
engine.setSSLParameters(sslParameters);
|
||||
super.closeOutbound();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* Factory for wrapping {@link SSLEngine} object in {@link JettyNpnSslEngine} objects
|
||||
*/
|
||||
public final class JettyNpnSslEngineWrapper implements SslEngineWrapperFactory {
|
||||
private static JettyNpnSslEngineWrapper instance;
|
||||
|
||||
private JettyNpnSslEngineWrapper() throws SSLException {
|
||||
if (!JettyNpnSslEngine.isAvailable()) {
|
||||
throw new SSLException("NPN unsupported. Is your classpatch configured correctly?" +
|
||||
" See http://www.eclipse.org/jetty/documentation/current/npn-chapter.html#npn-starting");
|
||||
}
|
||||
}
|
||||
|
||||
public static JettyNpnSslEngineWrapper instance() throws SSLException {
|
||||
if (instance == null) {
|
||||
instance = new JettyNpnSslEngineWrapper();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, List<String> protocols, boolean isServer) {
|
||||
return new JettyNpnSslEngine(engine, protocols, isServer);
|
||||
}
|
||||
}
|
208
handler/src/main/java/io/netty/handler/ssl/JettySslEngine.java
Normal file
208
handler/src/main/java/io/netty/handler/ssl/JettySslEngine.java
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
class JettySslEngine extends SSLEngine {
|
||||
protected final SSLEngine engine;
|
||||
protected final JettySslSession session;
|
||||
|
||||
JettySslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
|
||||
if (nextProtocols == null) {
|
||||
throw new NullPointerException("nextProtocols");
|
||||
}
|
||||
if (nextProtocols.isEmpty()) {
|
||||
throw new IllegalArgumentException("nextProtocols can not be empty");
|
||||
}
|
||||
|
||||
this.engine = engine;
|
||||
session = new JettySslSession(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JettySslSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInbound() throws SSLException {
|
||||
engine.closeInbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOutbound() {
|
||||
engine.closeOutbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPeerHost() {
|
||||
return engine.getPeerHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeerPort() {
|
||||
return engine.getPeerPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult wrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException {
|
||||
return engine.wrap(byteBuffer, byteBuffer2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult wrap(ByteBuffer[] byteBuffers, ByteBuffer byteBuffer) throws SSLException {
|
||||
return engine.wrap(byteBuffers, byteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i2, ByteBuffer byteBuffer) throws SSLException {
|
||||
return engine.wrap(byteBuffers, i, i2, byteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException {
|
||||
return engine.unwrap(byteBuffer, byteBuffer2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers) throws SSLException {
|
||||
return engine.unwrap(byteBuffer, byteBuffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, int i, int i2) throws SSLException {
|
||||
return engine.unwrap(byteBuffer, byteBuffers, i, i2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable getDelegatedTask() {
|
||||
return engine.getDelegatedTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInboundDone() {
|
||||
return engine.isInboundDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutboundDone() {
|
||||
return engine.isOutboundDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return engine.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledCipherSuites() {
|
||||
return engine.getEnabledCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledCipherSuites(String[] strings) {
|
||||
engine.setEnabledCipherSuites(strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedProtocols() {
|
||||
return engine.getSupportedProtocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledProtocols() {
|
||||
return engine.getEnabledProtocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledProtocols(String[] strings) {
|
||||
engine.setEnabledProtocols(strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLSession getHandshakeSession() {
|
||||
return engine.getHandshakeSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginHandshake() throws SSLException {
|
||||
engine.beginHandshake();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeStatus getHandshakeStatus() {
|
||||
return engine.getHandshakeStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseClientMode(boolean b) {
|
||||
engine.setUseClientMode(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseClientMode() {
|
||||
return engine.getUseClientMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNeedClientAuth(boolean b) {
|
||||
engine.setNeedClientAuth(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNeedClientAuth() {
|
||||
return engine.getNeedClientAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWantClientAuth(boolean b) {
|
||||
engine.setWantClientAuth(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getWantClientAuth() {
|
||||
return engine.getWantClientAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnableSessionCreation(boolean b) {
|
||||
engine.setEnableSessionCreation(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getEnableSessionCreation() {
|
||||
return engine.getEnableSessionCreation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLParameters getSSLParameters() {
|
||||
return engine.getSSLParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSSLParameters(SSLParameters sslParameters) {
|
||||
engine.setSSLParameters(sslParameters);
|
||||
}
|
||||
}
|
@ -24,12 +24,11 @@ import javax.security.cert.X509Certificate;
|
||||
import java.security.Principal;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
final class JettyNpnSslSession implements SSLSession {
|
||||
|
||||
final class JettySslSession implements SSLSession {
|
||||
private final SSLEngine engine;
|
||||
private volatile String applicationProtocol;
|
||||
|
||||
JettyNpnSslSession(SSLEngine engine) {
|
||||
JettySslSession(SSLEngine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
private final List<String> unmodifiableCiphers = Collections.unmodifiableList(ciphers);
|
||||
private final long sessionCacheSize;
|
||||
private final long sessionTimeout;
|
||||
private final List<String> nextProtocols;
|
||||
private final List<String> protocols;
|
||||
|
||||
/** The OpenSSL SSL_CTX object */
|
||||
private final long ctx;
|
||||
@ -135,9 +135,8 @@ public final class OpenSslServerContext extends SslContext {
|
||||
if (keyPassword == null) {
|
||||
keyPassword = "";
|
||||
}
|
||||
if (nextProtocols == null) {
|
||||
nextProtocols = Collections.emptyList();
|
||||
}
|
||||
|
||||
protocols = translateProtocols(nextProtocols);
|
||||
|
||||
for (String c: ciphers) {
|
||||
if (c == null) {
|
||||
@ -146,15 +145,6 @@ public final class OpenSslServerContext extends SslContext {
|
||||
this.ciphers.add(c);
|
||||
}
|
||||
|
||||
List<String> nextProtoList = new ArrayList<String>();
|
||||
for (String p: nextProtocols) {
|
||||
if (p == null) {
|
||||
break;
|
||||
}
|
||||
nextProtoList.add(p);
|
||||
}
|
||||
this.nextProtocols = Collections.unmodifiableList(nextProtoList);
|
||||
|
||||
// Allocate a new APR pool.
|
||||
aprPool = Pool.create(0);
|
||||
|
||||
@ -218,11 +208,11 @@ public final class OpenSslServerContext extends SslContext {
|
||||
}
|
||||
|
||||
/* Set next protocols for next protocol negotiation extension, if specified */
|
||||
if (!nextProtoList.isEmpty()) {
|
||||
if (!protocols.isEmpty()) {
|
||||
// Convert the protocol list into a comma-separated string.
|
||||
StringBuilder nextProtocolBuf = new StringBuilder();
|
||||
for (String p: nextProtoList) {
|
||||
nextProtocolBuf.append(p);
|
||||
for (int i = 0; i < protocols.size(); ++i) {
|
||||
nextProtocolBuf.append(protocols.get(i));
|
||||
nextProtocolBuf.append(',');
|
||||
}
|
||||
nextProtocolBuf.setLength(nextProtocolBuf.length() - 1);
|
||||
@ -284,7 +274,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
|
||||
@Override
|
||||
public List<String> nextProtocols() {
|
||||
return nextProtocols;
|
||||
return protocols;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,10 +296,10 @@ public final class OpenSslServerContext extends SslContext {
|
||||
*/
|
||||
@Override
|
||||
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||
if (nextProtocols.isEmpty()) {
|
||||
if (protocols.isEmpty()) {
|
||||
return new OpenSslEngine(ctx, alloc, null);
|
||||
} else {
|
||||
return new OpenSslEngine(ctx, alloc, nextProtocols.get(nextProtocols.size() - 1));
|
||||
return new OpenSslEngine(ctx, alloc, protocols.get(protocols.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,11 @@ import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -51,7 +55,6 @@ import java.util.List;
|
||||
* </pre>
|
||||
*/
|
||||
public abstract class SslContext {
|
||||
|
||||
/**
|
||||
* Returns the default server-side implementation provider currently in use.
|
||||
*
|
||||
@ -74,6 +77,28 @@ public abstract class SslContext {
|
||||
return SslProvider.JDK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an {@link Iterable} of protocols to an unmodifiable {@link List}
|
||||
* @param protocols Protocols to translate
|
||||
* @return An unmodifiable {@link List} of protocols
|
||||
*/
|
||||
public static List<String> translateProtocols(Iterable<String> protocols) {
|
||||
Iterator<String> itr = protocols == null ? null : protocols.iterator();
|
||||
if (itr != null) {
|
||||
List<String> nextProtoList = new ArrayList<String>(4);
|
||||
while (itr.hasNext()) {
|
||||
String p = itr.next();
|
||||
if (p == null) {
|
||||
break;
|
||||
}
|
||||
nextProtoList.add(p);
|
||||
}
|
||||
return Collections.unmodifiableList(nextProtoList);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new server-side {@link SslContext}.
|
||||
*
|
||||
@ -82,7 +107,7 @@ public abstract class SslContext {
|
||||
* @return a new server-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
|
||||
return newServerContext(null, certChainFile, keyFile, null, null, null, 0, 0);
|
||||
return newServerContext(certChainFile, keyFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +121,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
return newServerContext(null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
return newServerContext(null, certChainFile, keyFile, keyPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,6 +135,9 @@ public abstract class SslContext {
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @param wrapperFactory a factory used to wrap the underlying {@link SSLEngine}.
|
||||
* This is required if {@code nextProtocols} is not {@code null} or empty
|
||||
* and if OpenSSL is available
|
||||
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||
* {@code 0} to use the default value.
|
||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||
@ -119,10 +147,11 @@ public abstract class SslContext {
|
||||
public static SslContext newServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
SslEngineWrapperFactory wrapperFactory,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newServerContext(
|
||||
null, certChainFile, keyFile, keyPassword,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
ciphers, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,7 +165,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
SslProvider provider, File certChainFile, File keyFile) throws SSLException {
|
||||
return newServerContext(provider, certChainFile, keyFile, null, null, null, 0, 0);
|
||||
return newServerContext(provider, certChainFile, keyFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,7 +181,8 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
return newServerContext(provider, certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
return newServerContext(provider, certChainFile, keyFile, keyPassword, null, null,
|
||||
DefaultSslWrapperFactory.instance(), 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,6 +198,9 @@ public abstract class SslContext {
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @param wrapperFactory a factory used to wrap the underlying {@link SSLEngine}.
|
||||
* This is required if {@code nextProtocols} is not {@code null} or empty
|
||||
* and if the {@code provider} is {@link SslProvider#JDK}
|
||||
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||
* {@code 0} to use the default value.
|
||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||
@ -178,6 +211,7 @@ public abstract class SslContext {
|
||||
SslProvider provider,
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
SslEngineWrapperFactory wrapperFactory,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
if (provider == null) {
|
||||
@ -188,7 +222,7 @@ public abstract class SslContext {
|
||||
case JDK:
|
||||
return new JdkSslServerContext(
|
||||
certChainFile, keyFile, keyPassword,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
ciphers, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
case OPENSSL:
|
||||
return new OpenSslServerContext(
|
||||
certChainFile, keyFile, keyPassword,
|
||||
@ -204,7 +238,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext() throws SSLException {
|
||||
return newClientContext(null, null, null, null, null, 0, 0);
|
||||
return newClientContext(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,7 +249,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(File certChainFile) throws SSLException {
|
||||
return newClientContext(null, certChainFile, null, null, null, 0, 0);
|
||||
return newClientContext(null, certChainFile);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,7 +262,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(null, null, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(null, null, trustManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,7 +278,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(null, certChainFile, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(null, certChainFile, trustManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,6 +293,9 @@ public abstract class SslContext {
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @param wrapperFactory a factory used to wrap the underlying {@link SSLEngine}.
|
||||
* This is required if {@code nextProtocols} is not {@code null} or empty
|
||||
* and if OpenSSL is available
|
||||
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||
* {@code 0} to use the default value.
|
||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||
@ -269,10 +306,11 @@ public abstract class SslContext {
|
||||
public static SslContext newClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
SslEngineWrapperFactory wrapperFactory,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newClientContext(
|
||||
null, certChainFile, trustManagerFactory,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
ciphers, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,7 +322,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(SslProvider provider) throws SSLException {
|
||||
return newClientContext(provider, null, null, null, null, 0, 0);
|
||||
return newClientContext(provider, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -298,7 +336,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
|
||||
return newClientContext(provider, certChainFile, null, null, null, 0, 0);
|
||||
return newClientContext(provider, certChainFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,7 +352,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(provider, null, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(provider, null, trustManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,7 +370,8 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(provider, certChainFile, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(provider, certChainFile, trustManagerFactory, null, null,
|
||||
DefaultSslWrapperFactory.instance(), 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -349,6 +388,9 @@ public abstract class SslContext {
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @param wrapperFactory a factory used to wrap the underlying {@link SSLEngine}.
|
||||
* This is required if {@code nextProtocols} is not {@code null} or empty
|
||||
* and if the {@code provider} is {@link SslProvider#JDK}
|
||||
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||
* {@code 0} to use the default value.
|
||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||
@ -360,6 +402,7 @@ public abstract class SslContext {
|
||||
SslProvider provider,
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
SslEngineWrapperFactory wrapperFactory,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
if (provider != null && provider != SslProvider.JDK) {
|
||||
@ -368,7 +411,7 @@ public abstract class SslContext {
|
||||
|
||||
return new JdkSslClientContext(
|
||||
certChainFile, trustManagerFactory,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
ciphers, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
SslContext() { }
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* Abstract factory pattern for wrapping an {@link SSLEngine} object.
|
||||
* This is useful for NPN/APLN support.
|
||||
*/
|
||||
public interface SslEngineWrapperFactory {
|
||||
/**
|
||||
* Abstract factory pattern for wrapping an {@link SSLEngine} object.
|
||||
* This is useful for NPN/APLN support.
|
||||
*
|
||||
* @param engine The engine to wrap
|
||||
* @param protocols The application level protocols that are supported
|
||||
* @param isServer
|
||||
* <ul>
|
||||
* <li>{@code true} if the engine is for server side of connections</li>
|
||||
* <li>{@code false} if the engine is for client side of connections</li>
|
||||
* </ul>
|
||||
* @return The resulting wrapped engine. This may just be {@code engine}
|
||||
*/
|
||||
SSLEngine wrapSslEngine(SSLEngine engine, List<String> protocols, boolean isServer);
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
public class JettySslEngineTest {
|
||||
private static final String APPLICATION_LEVEL_PROTOCOL = "my-protocol";
|
||||
|
||||
@Mock
|
||||
private MessageReciever serverReceiver;
|
||||
@Mock
|
||||
private MessageReciever clientReceiver;
|
||||
|
||||
private SslContext serverSslCtx;
|
||||
private SslContext clientSslCtx;
|
||||
private ServerBootstrap sb;
|
||||
private Bootstrap cb;
|
||||
private Channel serverChannel;
|
||||
private Channel serverConnectedChannel;
|
||||
private Channel clientChannel;
|
||||
private CountDownLatch serverLatch;
|
||||
private CountDownLatch clientLatch;
|
||||
|
||||
private interface MessageReciever {
|
||||
void messageReceived(ByteBuf msg);
|
||||
}
|
||||
|
||||
private final class MessageDelegatorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
private final MessageReciever receiver;
|
||||
|
||||
public MessageDelegatorChannelHandler(MessageReciever receiver) {
|
||||
super(false);
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
|
||||
receiver.messageReceived(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
serverLatch = new CountDownLatch(1);
|
||||
clientLatch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws InterruptedException {
|
||||
if (serverChannel != null) {
|
||||
serverChannel.close().sync();
|
||||
sb.group().shutdownGracefully();
|
||||
cb.group().shutdownGracefully();
|
||||
}
|
||||
clientChannel = null;
|
||||
serverChannel = null;
|
||||
serverConnectedChannel = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNpn() throws Exception {
|
||||
SslEngineWrapperFactory wrapper = null;
|
||||
try {
|
||||
wrapper = JettyNpnSslEngineWrapper.instance();
|
||||
} catch (SSLException e) {
|
||||
// NPN availability is dependent on the java version. If NPN is not available because of
|
||||
// java version incompatibility don't fail the test, but instead just skip the test
|
||||
assumeNoException(e);
|
||||
}
|
||||
mySetup(wrapper);
|
||||
runTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlpn() throws Exception {
|
||||
SslEngineWrapperFactory wrapper = null;
|
||||
try {
|
||||
wrapper = JettyAlpnSslEngineWrapper.instance();
|
||||
} catch (SSLException e) {
|
||||
// ALPN availability is dependent on the java version. If NPN is not available because of
|
||||
// java version incompatibility don't fail the test, but instead just skip the test
|
||||
assumeNoException(e);
|
||||
}
|
||||
mySetup(wrapper);
|
||||
runTest();
|
||||
}
|
||||
|
||||
private void mySetup(SslEngineWrapperFactory wrapper) throws InterruptedException, SSLException,
|
||||
CertificateException {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContext.newServerContext(SslProvider.JDK, ssc.certificate(), ssc.privateKey(), null, null,
|
||||
Arrays.asList(APPLICATION_LEVEL_PROTOCOL), wrapper, 0, 0);
|
||||
clientSslCtx = SslContext.newClientContext(SslProvider.JDK, null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
Arrays.asList(APPLICATION_LEVEL_PROTOCOL), wrapper, 0, 0);
|
||||
|
||||
serverConnectedChannel = null;
|
||||
sb = new ServerBootstrap();
|
||||
cb = new Bootstrap();
|
||||
|
||||
sb.group(new NioEventLoopGroup(), new NioEventLoopGroup());
|
||||
sb.channel(NioServerSocketChannel.class);
|
||||
sb.childHandler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(serverSslCtx.newHandler(ch.alloc()));
|
||||
p.addLast(new MessageDelegatorChannelHandler(serverReceiver));
|
||||
serverConnectedChannel = ch;
|
||||
}
|
||||
});
|
||||
|
||||
cb.group(new NioEventLoopGroup());
|
||||
cb.channel(NioSocketChannel.class);
|
||||
cb.handler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(clientSslCtx.newHandler(ch.alloc()));
|
||||
p.addLast(new MessageDelegatorChannelHandler(clientReceiver));
|
||||
}
|
||||
});
|
||||
|
||||
serverChannel = sb.bind(new InetSocketAddress(0)).sync().channel();
|
||||
int port = ((InetSocketAddress) serverChannel.localAddress()).getPort();
|
||||
|
||||
ChannelFuture ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port));
|
||||
assertTrue(ccf.awaitUninterruptibly().isSuccess());
|
||||
clientChannel = ccf.channel();
|
||||
}
|
||||
|
||||
private void runTest() throws Exception {
|
||||
ByteBuf clientMessage = Unpooled.copiedBuffer("I am a client".getBytes());
|
||||
ByteBuf serverMessage = Unpooled.copiedBuffer("I am a server".getBytes());
|
||||
try {
|
||||
writeAndVerifyReceived(clientMessage.retain(), clientChannel, serverLatch, serverReceiver);
|
||||
writeAndVerifyReceived(serverMessage.retain(), serverConnectedChannel, clientLatch, clientReceiver);
|
||||
verifyApplicationLevelProtocol(clientChannel);
|
||||
verifyApplicationLevelProtocol(serverConnectedChannel);
|
||||
} finally {
|
||||
clientMessage.release();
|
||||
serverMessage.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyApplicationLevelProtocol(Channel channel) {
|
||||
SslHandler handler = channel.pipeline().get(SslHandler.class);
|
||||
assertNotNull(handler);
|
||||
String[] protocol = handler.engine().getSession().getProtocol().split(":");
|
||||
assertNotNull(protocol);
|
||||
assertTrue("protocol.length must be greater than 1 but is " + protocol.length, protocol.length > 1);
|
||||
assertEquals(APPLICATION_LEVEL_PROTOCOL, protocol[1]);
|
||||
}
|
||||
|
||||
private static void writeAndVerifyReceived(ByteBuf message, Channel sendChannel, CountDownLatch receiverLatch,
|
||||
MessageReciever receiver) throws Exception {
|
||||
sendChannel.writeAndFlush(message);
|
||||
receiverLatch.await(2, TimeUnit.SECONDS);
|
||||
message.resetReaderIndex();
|
||||
verify(receiver).messageReceived(eq(message));
|
||||
}
|
||||
}
|
128
pom.xml
128
pom.xml
@ -109,7 +109,8 @@
|
||||
<!-- Our Javadoc has poor enough quality to fail the build thanks to JDK8 javadoc which got more strict. -->
|
||||
<maven.javadoc.failOnError>false</maven.javadoc.failOnError>
|
||||
<!-- npn-boot does not work with JDK 8 -->
|
||||
<argLine.bootcp>-D_</argLine.bootcp>
|
||||
<jetty.alpn.version>8.0.0.v20140317</jetty.alpn.version>
|
||||
<argLine.bootcp>-Xbootclasspath/p:${jetty.alpn.path}</argLine.bootcp>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
@ -172,8 +173,9 @@
|
||||
</profile>
|
||||
|
||||
<!--
|
||||
Profiles that assigns proper Jetty npn-boot version.
|
||||
Profiles that assigns proper Jetty npn-boot and alpn-boot version.
|
||||
See: http://www.eclipse.org/jetty/documentation/current/npn-chapter.html#npn-versions
|
||||
See: http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-versions
|
||||
-->
|
||||
<profile>
|
||||
<id>npn-7u9</id>
|
||||
@ -272,7 +274,7 @@
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-7u40</id>
|
||||
<id>npn-alpn-7u40</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
@ -281,10 +283,12 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-7u45</id>
|
||||
<id>npn-alpn-7u45</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
@ -293,10 +297,12 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-7u51</id>
|
||||
<id>npn-alpn-7u51</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
@ -305,6 +311,81 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u55</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_55</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u60</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_60</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u65</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_65</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u67</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_67</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<!--
|
||||
This profile exists because either ALPN or NPN can exits on the class path at once, but not both.
|
||||
The JDK version is typically used to distinguish which should be used but there is some overlap
|
||||
where both could be used. ALPN is the default and this profile is enabled with a -Dforcenpn=true arugument
|
||||
-->
|
||||
<id>forcenpn</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>forcenpn</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<argLine.bootcp>-Xbootclasspath/p:${jetty.npn.path}</argLine.bootcp>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
@ -315,6 +396,8 @@
|
||||
<jboss.marshalling.version>1.3.18.GA</jboss.marshalling.version>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.npn.path>${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${jetty.npn.version}/npn-boot-${jetty.npn.version}.jar</jetty.npn.path>
|
||||
<jetty.alpn.version>8.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.alpn.path>${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${jetty.alpn.version}/alpn-boot-${jetty.alpn.version}.jar</jetty.alpn.path>
|
||||
<argLine.common>
|
||||
-server
|
||||
-dsa -da -ea:io.netty...
|
||||
@ -325,7 +408,8 @@
|
||||
-XX:+OptimizeStringConcat
|
||||
-XX:+HeapDumpOnOutOfMemoryError
|
||||
</argLine.common>
|
||||
<argLine.bootcp>-Xbootclasspath/p:${jetty.npn.path}</argLine.bootcp>
|
||||
<!-- Default to ALPN. Then classpath is used to refine selection. See forcenpn profile to force NPN -->
|
||||
<argLine.bootcp>-Xbootclasspath/p:${jetty.alpn.path}</argLine.bootcp>
|
||||
<argLine.leak>-verbose:gc</argLine.leak> <!-- Overridden when 'leak' profile is active -->
|
||||
<argLine.coverage>-D_</argLine.coverage> <!-- Overridden when 'coverage' profile is active -->
|
||||
</properties>
|
||||
@ -385,6 +469,16 @@
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<version>${jetty.npn.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<version>${jetty.alpn.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Protocol Buffers - completely optional -->
|
||||
<dependency>
|
||||
@ -665,6 +759,14 @@
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<version>1.9</version>
|
||||
<dependencies>
|
||||
<!-- Upgrade ASM and support Java 8 bytecode -->
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-all</artifactId>
|
||||
<version>5.0.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<signature>
|
||||
<groupId>org.codehaus.mojo.signature</groupId>
|
||||
@ -754,6 +856,18 @@
|
||||
<version>${jetty.npn.version}</version>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>get-alpn-boot</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>get</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<version>${jetty.alpn.version}</version>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@ -791,7 +905,7 @@
|
||||
<instructions>
|
||||
<Export-Package>${project.groupId}.*</Export-Package>
|
||||
<!-- enforce JVM vendor package as optional -->
|
||||
<Import-Package>sun.misc.*;resolution:=optional,sun.nio.ch;resolution:=optional,sun.security.*;resolution:=optional,*</Import-Package>
|
||||
<Import-Package>sun.misc.*;resolution:=optional,sun.nio.ch;resolution:=optional,sun.security.*;resolution:=optional,org.eclipse.jetty.alpn;version="[1,)";resolution=optional,*</Import-Package>
|
||||
<!-- override "internal" private package convention -->
|
||||
<Private-Package>!*</Private-Package>
|
||||
</instructions>
|
||||
|
@ -40,9 +40,15 @@ EXAMPLE_MAP=(
|
||||
'localecho:io.netty.example.localecho.LocalEcho'
|
||||
)
|
||||
|
||||
NEEDS_NPN_MAP=(
|
||||
'spdy-client'
|
||||
'spdy-server'
|
||||
)
|
||||
|
||||
EXAMPLE=''
|
||||
EXAMPLE_CLASS=''
|
||||
EXAMPLE_ARGS='-D_'
|
||||
FORCE_NPN=''
|
||||
I=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@ -92,7 +98,13 @@ if [[ -z "$EXAMPLE" ]] || [[ -z "$EXAMPLE_CLASS" ]] || [[ $# -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for E in "${NEEDS_NPN_MAP[@]}"; do
|
||||
if [[ "$EXAMPLE" = "$E" ]]; then
|
||||
FORCE_NPN='true'
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
cd "`dirname "$0"`"/example
|
||||
echo "[INFO] Running: $EXAMPLE ($EXAMPLE_CLASS $EXAMPLE_ARGS)"
|
||||
exec mvn -q -nsu compile exec:exec -Dcheckstyle.skip=true -DargLine.example="$EXAMPLE_ARGS" -DexampleClass="$EXAMPLE_CLASS"
|
||||
|
||||
exec mvn -q -nsu compile exec:exec -Dcheckstyle.skip=true -Dforcenpn="$FORCE_NPN" -DargLine.example="$EXAMPLE_ARGS" -DexampleClass="$EXAMPLE_CLASS"
|
||||
|
Loading…
Reference in New Issue
Block a user