ALPN should allow handshake failure if no compatible protocols found
Motivation: If there are no common protocols in the ALPN protocol exchange we still compete the handshake successfully. This handshake should fail according to http://tools.ietf.org/html/rfc7301#section-3.2 with a status of no_application_protocol. The specification also allows for the server to "play dumb" and not advertise that it supports ALPN in this case (see MAY clauses in http://tools.ietf.org/html/rfc7301#section-3.1) Modifications: -Upstream project used for ALPN (alpn-boot) does not support this. So a PR https://github.com/jetty-project/jetty-alpn/pull/3 was submitted. -The netty code using alpn-boot should support the new interface (return null on existing method). -Version number of alpn-boot must be updated in pom.xml files Result: -Netty fails the SSL handshake if ALPN is used and there are no common protocols.
This commit is contained in:
parent
41d9830e07
commit
f8af84d599
@ -29,14 +29,16 @@ 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.codec.http2.Http2SecurityUtil;
|
||||
import io.netty.handler.ssl.JettyAlpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -63,8 +65,12 @@ public final class Http2Client {
|
||||
/* NOTE: the following filter may not include all ciphers required by the HTTP/2 specification
|
||||
* Please refer to the HTTP/2 specification for cipher requirements. */
|
||||
SupportedCipherSuiteFilter.INSTANCE,
|
||||
Arrays.asList(SelectedProtocol.HTTP_2.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyAlpnSslEngineWrapper.instance(),
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.ALPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.HTTP_2.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
0, 0);
|
||||
} else {
|
||||
sslCtx = null;
|
||||
|
@ -26,13 +26,14 @@ import io.netty.handler.codec.http2.Http2OrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.codec.http2.Http2SecurityUtil;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.JettyAlpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A HTTP/2 Server that responds to requests with a Hello World. Once started, you can test the
|
||||
* server with the example client.
|
||||
@ -53,10 +54,12 @@ public final class Http2Server {
|
||||
/* NOTE: the following filter may not include all ciphers required by the HTTP/2 specification
|
||||
* Please refer to the HTTP/2 specification for cipher requirements. */
|
||||
SupportedCipherSuiteFilter.INSTANCE,
|
||||
Arrays.asList(
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.ALPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.HTTP_2.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyAlpnSslEngineWrapper.instance(),
|
||||
0, 0);
|
||||
} else {
|
||||
sslCtx = null;
|
||||
|
@ -24,18 +24,20 @@ import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http2.Http2SecurityUtil;
|
||||
import io.netty.handler.codec.http2.Http2OrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.codec.http2.Http2SecurityUtil;
|
||||
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.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Simple memcache client that demonstrates get and set commands against a memcache server.
|
||||
@ -55,8 +57,12 @@ public final class MemcacheClient {
|
||||
/* NOTE: the following filter may not include all ciphers required by the HTTP/2 specification
|
||||
* Please refer to the HTTP/2 specification for cipher requirements. */
|
||||
SupportedCipherSuiteFilter.INSTANCE,
|
||||
Arrays.asList(SelectedProtocol.HTTP_2.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyAlpnSslEngineWrapper.instance(),
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.ALPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.HTTP_2.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
0, 0);
|
||||
} else {
|
||||
sslCtx = null;
|
||||
|
@ -27,13 +27,14 @@ 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.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.IdentityCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.JettyNpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An SPDY client that allows you to send HTTP GET to a SPDY server.
|
||||
* <p>
|
||||
@ -57,8 +58,12 @@ public final class SpdyClient {
|
||||
// Configure SSL.
|
||||
final SslContext sslCtx = SslContext.newClientContext(
|
||||
null, InsecureTrustManagerFactory.INSTANCE, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyNpnSslEngineWrapper.instance(),
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.NPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
0, 0);
|
||||
|
||||
HttpResponseClientHandler httpResponseHandler = new HttpResponseClientHandler();
|
||||
|
@ -24,13 +24,14 @@ 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.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.IdentityCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.JettyNpnSslEngineWrapper;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
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>
|
||||
@ -58,8 +59,12 @@ public final class SpdyServer {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
SslContext sslCtx = SslContext.newServerContext(
|
||||
ssc.certificate(), ssc.privateKey(), null, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
JettyNpnSslEngineWrapper.instance(),
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.NPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
0, 0);
|
||||
|
||||
// Configure the server.
|
||||
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 io.netty.handler.ssl.ApplicationProtocolUtil.toList;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* Provides an {@link SSLEngine} agnostic way to configure a {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
public final class ApplicationProtocolConfig {
|
||||
private final List<String> supportedProtocols;
|
||||
private final Protocol protocol;
|
||||
private final SelectorFailureBehavior selectorBehavior;
|
||||
private final SelectedListenerFailureBehavior selectedBehavior;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocol The application protocol functionality to use.
|
||||
* @param selectorBehavior How the peer selecting the protocol should behave.
|
||||
* @param selectedBehavior How the peer being notified of the selected protocol should behave.
|
||||
* @param supportedProtocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior,
|
||||
SelectedListenerFailureBehavior selectedBehavior, Iterable<String> supportedProtocols) {
|
||||
this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocol The application protocol functionality to use.
|
||||
* @param selectorBehavior How the peer selecting the protocol should behave.
|
||||
* @param selectedBehavior How the peer being notified of the selected protocol should behave.
|
||||
* @param supportedProtocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior,
|
||||
SelectedListenerFailureBehavior selectedBehavior, String... supportedProtocols) {
|
||||
this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocol The application protocol functionality to use.
|
||||
* @param selectorBehavior How the peer selecting the protocol should behave.
|
||||
* @param selectedBehavior How the peer being notified of the selected protocol should behave.
|
||||
* @param supportedProtocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
private ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior,
|
||||
SelectedListenerFailureBehavior selectedBehavior, List<String> supportedProtocols) {
|
||||
this.supportedProtocols = Collections.unmodifiableList(checkNotNull(supportedProtocols, "supportedProtocols"));
|
||||
this.protocol = checkNotNull(protocol, "protocol");
|
||||
this.selectorBehavior = checkNotNull(selectorBehavior, "selectorBehavior");
|
||||
this.selectedBehavior = checkNotNull(selectedBehavior, "selectedBehavior");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines which application level protocol negotiation to use.
|
||||
*/
|
||||
public enum Protocol {
|
||||
NONE, NPN, ALPN, NPN_AND_ALPN
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the most common behaviors for the peer that selects the application protocol.
|
||||
*/
|
||||
public enum SelectorFailureBehavior {
|
||||
FATAL_ALERT, NO_ADVERTISE, CHOOSE_MY_LAST_PROTOCOL
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the most common behaviors for the peer which is notified of the selected protocol.
|
||||
*/
|
||||
public enum SelectedListenerFailureBehavior {
|
||||
ACCEPT, FATAL_ALERT, CHOOSE_MY_LAST_PROTOCOL
|
||||
}
|
||||
|
||||
/**
|
||||
* The application level protocols supported.
|
||||
*/
|
||||
public List<String> supportedProtocols() {
|
||||
return supportedProtocols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get which application level protocol negotiation to use.
|
||||
*/
|
||||
public Protocol protocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the desired behavior for the peer who selects the application protocol.
|
||||
*/
|
||||
public SelectorFailureBehavior selectorFailureBehavior() {
|
||||
return selectorBehavior;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the desired behavior for the peer who is notified of the selected protocol.
|
||||
*/
|
||||
public SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
|
||||
return selectedBehavior;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface to support Application Protocol Negotiation.
|
||||
* <p>
|
||||
* Default implementations are provided for:
|
||||
* <ul>
|
||||
* <li><a href="https://technotes.googlecode.com/git/nextprotoneg.html">Next Protocol Negotiation</a></li>
|
||||
* <li><a href="http://tools.ietf.org/html/rfc7301">Application-Layer Protocol Negotiation</a></li>
|
||||
* </ul>
|
||||
*/
|
||||
public interface ApplicationProtocolNegotiator {
|
||||
/**
|
||||
* Get the collection of application protocols supported by this application (in preference order).
|
||||
*/
|
||||
List<String> protocols();
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility class for application protocol common operations.
|
||||
*/
|
||||
final class ApplicationProtocolUtil {
|
||||
private static final int DEFAULT_LIST_SIZE = 2;
|
||||
|
||||
private ApplicationProtocolUtil() {
|
||||
}
|
||||
|
||||
static List<String> toList(Iterable<String> protocols) {
|
||||
return toList(DEFAULT_LIST_SIZE, protocols);
|
||||
}
|
||||
|
||||
static List<String> toList(int initialListSize, Iterable<String> protocols) {
|
||||
if (protocols == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<String>(initialListSize);
|
||||
for (String p : protocols) {
|
||||
if (p == null || p.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocol cannot be null or empty");
|
||||
}
|
||||
result.add(p);
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocols cannot empty");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<String> toList(String... protocols) {
|
||||
return toList(DEFAULT_LIST_SIZE, protocols);
|
||||
}
|
||||
|
||||
static List<String> toList(int initialListSize, String... protocols) {
|
||||
if (protocols == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<String>(initialListSize);
|
||||
for (String p : protocols) {
|
||||
if (p == null || p.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocol cannot be null or empty");
|
||||
}
|
||||
result.add(p);
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocols cannot empty");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* The {@link JdkApplicationProtocolNegotiator} to use if you need ALPN and are using {@link SslProvider#JDK}.
|
||||
*/
|
||||
public final class JdkAlpnApplicationProtocolNegotiator extends JdkBaseApplicationProtocolNegotiator {
|
||||
private static final SslEngineWrapperFactory ALPN_WRAPPER = new SslEngineWrapperFactory() {
|
||||
{
|
||||
if (!JdkAlpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("ALPN unsupported. Is your classpatch configured correctly?"
|
||||
+ " See http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-starting");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator,
|
||||
boolean isServer) {
|
||||
return new JdkAlpnSslEngine(engine, applicationNegotiator, isServer);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(Iterable<String> protocols) {
|
||||
this(false, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(String... protocols) {
|
||||
this(false, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, Iterable<String> protocols) {
|
||||
this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, String... protocols) {
|
||||
this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
||||
* @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
||||
boolean serverFailIfNoCommonProtocols, Iterable<String> protocols) {
|
||||
this(serverFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
||||
clientFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
||||
protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
||||
* @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
||||
boolean serverFailIfNoCommonProtocols, String... protocols) {
|
||||
this(serverFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
||||
clientFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
||||
protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
||||
* @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
||||
ProtocolSelectionListenerFactory listenerFactory, Iterable<String> protocols) {
|
||||
super(ALPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
||||
* @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkAlpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
||||
ProtocolSelectionListenerFactory listenerFactory, String... protocols) {
|
||||
super(ALPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
||||
}
|
||||
}
|
@ -15,6 +15,12 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
|
||||
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
@ -24,7 +30,7 @@ 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 {
|
||||
final class JdkAlpnSslEngine extends JdkSslEngine {
|
||||
private static boolean available;
|
||||
|
||||
static boolean isAvailable() {
|
||||
@ -52,52 +58,51 @@ final class JettyAlpnSslEngine extends JettySslEngine {
|
||||
}
|
||||
}
|
||||
|
||||
JettyAlpnSslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
|
||||
super(engine, nextProtocols, server);
|
||||
JdkAlpnSslEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator, boolean server) {
|
||||
super(engine);
|
||||
checkNotNull(applicationNegotiator, "applicationNegotiator");
|
||||
|
||||
if (server) {
|
||||
final String[] array = nextProtocols.toArray(new String[nextProtocols.size()]);
|
||||
final String fallback = array[array.length - 1];
|
||||
|
||||
final ProtocolSelector protocolSelector = checkNotNull(applicationNegotiator.protocolSelectorFactory()
|
||||
.newSelector(this, new HashSet<String>(applicationNegotiator.protocols())), "protocolSelector");
|
||||
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;
|
||||
}
|
||||
try {
|
||||
return protocolSelector.select(protocols);
|
||||
} catch (Throwable t) {
|
||||
PlatformDependent.throwException(t);
|
||||
return null;
|
||||
}
|
||||
session.setApplicationProtocol(fallback);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
session.setApplicationProtocol(null);
|
||||
protocolSelector.unsupported();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final ProtocolSelectionListener protocolListener = checkNotNull(applicationNegotiator
|
||||
.protocolListenerFactory().newListener(this, applicationNegotiator.protocols()),
|
||||
"protocolListener");
|
||||
ALPN.put(engine, new ClientProvider() {
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return nextProtocols;
|
||||
return applicationNegotiator.protocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(String protocol) {
|
||||
getSession().setApplicationProtocol(protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports() {
|
||||
return true;
|
||||
try {
|
||||
protocolListener.selected(protocol);
|
||||
} catch (Throwable t) {
|
||||
PlatformDependent.throwException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1));
|
||||
protocolListener.unsupported();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -105,13 +110,13 @@ final class JettyAlpnSslEngine extends JettySslEngine {
|
||||
|
||||
@Override
|
||||
public void closeInbound() throws SSLException {
|
||||
ALPN.remove(engine);
|
||||
ALPN.remove(getWrappedEngine());
|
||||
super.closeInbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOutbound() {
|
||||
ALPN.remove(engine);
|
||||
ALPN.remove(getWrappedEngine());
|
||||
super.closeOutbound();
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* JDK extension methods to support {@link ApplicationProtocolNegotiator}
|
||||
*/
|
||||
public interface JdkApplicationProtocolNegotiator extends ApplicationProtocolNegotiator {
|
||||
/**
|
||||
* Abstract factory pattern for wrapping an {@link SSLEngine} object. This is useful for NPN/APLN JDK 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 applicationNegotiator The application level protocol negotiator
|
||||
* @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, JdkApplicationProtocolNegotiator applicationNegotiator,
|
||||
boolean isServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to define the role of an application protocol selector in the SSL handshake process. Either
|
||||
* {@link ProtocolSelector#unsupported()} OR {@link ProtocolSelector#select(List)} will be called for each SSL
|
||||
* handshake.
|
||||
*/
|
||||
public interface ProtocolSelector {
|
||||
/**
|
||||
* Callback invoked to let the application know that the peer does not support this
|
||||
* {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
void unsupported();
|
||||
|
||||
/**
|
||||
* Callback invoked to select the application level protocol from the {@code protocols} provided.
|
||||
*
|
||||
* @param protocols the protocols sent by the protocol advertiser
|
||||
* @return the protocol selected by this {@link ProtocolSelector}. A {@code null} value will indicate the no
|
||||
* protocols were selected but the handshake should not fail. The decision to fail the handshake is left to the
|
||||
* other end negotiating the SSL handshake.
|
||||
* @throws Exception If the {@code protocols} provide warrant failing the SSL handshake with a fatal alert.
|
||||
*/
|
||||
String select(List<String> protocols) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener to be notified by which protocol was select by its peer. Either the
|
||||
* {@link ProtocolSelectionListener#unsupported()} OR the {@link ProtocolSelectionListener#selected(String)} method
|
||||
* will be called for each SSL handshake.
|
||||
*/
|
||||
public interface ProtocolSelectionListener {
|
||||
/**
|
||||
* Callback invoked to let the application know that the peer does not support this
|
||||
* {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
void unsupported();
|
||||
|
||||
/**
|
||||
* Callback invoked to let this application know the protocol chosen by the peer.
|
||||
*
|
||||
* @param protocol the protocol selected by the peer. May be {@code null} or empty as supported by the
|
||||
* application negotiation protocol.
|
||||
* @throws Exception This may be thrown if the selected protocol is not acceptable and the desired behavior is
|
||||
* to fail the handshake with a fatal alert.
|
||||
*/
|
||||
void selected(String protocol) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory interface for {@link ProtocolSelector} objects.
|
||||
*/
|
||||
public interface ProtocolSelectorFactory {
|
||||
/**
|
||||
* Generate a new instance of {@link ProtocolSelector}.
|
||||
* @param engine The {@link SSLEngine} that the returned {@link ProtocolSelector} will be used to create an
|
||||
* instance for.
|
||||
* @param supportedProtocols The protocols that are supported.
|
||||
* @return A new instance of {@link ProtocolSelector}.
|
||||
*/
|
||||
ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory interface for {@link ProtocolSelectionListener} objects.
|
||||
*/
|
||||
public interface ProtocolSelectionListenerFactory {
|
||||
/**
|
||||
* Generate a new instance of {@link ProtocolSelectionListener}.
|
||||
* @param engine The {@link SSLEngine} that the returned {@link ProtocolSelectionListener} will be used to
|
||||
* create an instance for.
|
||||
* @param supportedProtocols The protocols that are supported in preference order.
|
||||
* @return A new instance of {@link ProtocolSelectionListener}.
|
||||
*/
|
||||
ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link SslEngineWrapperFactory}.
|
||||
*/
|
||||
SslEngineWrapperFactory wrapperFactory();
|
||||
|
||||
/**
|
||||
* Get the {@link ProtocolSelectorFactory}.
|
||||
*/
|
||||
ProtocolSelectorFactory protocolSelectorFactory();
|
||||
|
||||
/**
|
||||
* Get the {@link ProtocolSelectionListenerFactory}.
|
||||
*/
|
||||
ProtocolSelectionListenerFactory protocolListenerFactory();
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 io.netty.handler.ssl.ApplicationProtocolUtil.toList;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
/**
|
||||
* Common base class for {@link JdkApplicationProtocolNegotiator} classes to inherit from.
|
||||
*/
|
||||
class JdkBaseApplicationProtocolNegotiator implements JdkApplicationProtocolNegotiator {
|
||||
private final List<String> protocols;
|
||||
private final ProtocolSelectorFactory selectorFactory;
|
||||
private final ProtocolSelectionListenerFactory listenerFactory;
|
||||
private final SslEngineWrapperFactory wrapperFactory;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
|
||||
* @param selectorFactory How the peer selecting the protocol should behave.
|
||||
* @param listenerFactory How the peer being notified of the selected protocol should behave.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
protected JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
|
||||
ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
|
||||
Iterable<String> protocols) {
|
||||
this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
|
||||
* @param selectorFactory How the peer selecting the protocol should behave.
|
||||
* @param listenerFactory How the peer being notified of the selected protocol should behave.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
protected JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
|
||||
ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
|
||||
String... protocols) {
|
||||
this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
|
||||
* @param selectorFactory How the peer selecting the protocol should behave.
|
||||
* @param listenerFactory How the peer being notified of the selected protocol should behave.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
private JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
|
||||
ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
|
||||
List<String> protocols) {
|
||||
this.wrapperFactory = checkNotNull(wrapperFactory, "wrapperFactory");
|
||||
this.selectorFactory = checkNotNull(selectorFactory, "selectorFactory");
|
||||
this.listenerFactory = checkNotNull(listenerFactory, "listenerFactory");
|
||||
this.protocols = Collections.unmodifiableList(checkNotNull(protocols, "protocols"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectorFactory protocolSelectorFactory() {
|
||||
return selectorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectionListenerFactory protocolListenerFactory() {
|
||||
return listenerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslEngineWrapperFactory wrapperFactory() {
|
||||
return wrapperFactory;
|
||||
}
|
||||
|
||||
static final ProtocolSelectorFactory FAIL_SELECTOR_FACTORY = new ProtocolSelectorFactory() {
|
||||
@Override
|
||||
public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
|
||||
return new FailProtocolSelector((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
static final ProtocolSelectorFactory NO_FAIL_SELECTOR_FACTORY = new ProtocolSelectorFactory() {
|
||||
@Override
|
||||
public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
|
||||
return new NoFailProtocolSelector((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
static final ProtocolSelectionListenerFactory FAIL_SELECTION_LISTENER_FACTORY =
|
||||
new ProtocolSelectionListenerFactory() {
|
||||
@Override
|
||||
public ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols) {
|
||||
return new FailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
static final ProtocolSelectionListenerFactory NO_FAIL_SELECTION_LISTENER_FACTORY =
|
||||
new ProtocolSelectionListenerFactory() {
|
||||
@Override
|
||||
public ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols) {
|
||||
return new NoFailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
protected static class NoFailProtocolSelector implements ProtocolSelector {
|
||||
private final JdkSslEngine jettyWrapper;
|
||||
private final Set<String> supportedProtocols;
|
||||
|
||||
public NoFailProtocolSelector(JdkSslEngine jettyWrapper, Set<String> supportedProtocols) {
|
||||
this.jettyWrapper = jettyWrapper;
|
||||
this.supportedProtocols = supportedProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
jettyWrapper.getSession().setApplicationProtocol(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String select(List<String> protocols) throws Exception {
|
||||
for (int i = 0; i < protocols.size(); ++i) {
|
||||
String p = protocols.get(i);
|
||||
if (supportedProtocols.contains(p)) {
|
||||
jettyWrapper.getSession().setApplicationProtocol(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return noSelectMatchFound();
|
||||
}
|
||||
|
||||
public String noSelectMatchFound() throws Exception {
|
||||
jettyWrapper.getSession().setApplicationProtocol(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class FailProtocolSelector extends NoFailProtocolSelector {
|
||||
public FailProtocolSelector(JdkSslEngine jettyWrapper, Set<String> supportedProtocols) {
|
||||
super(jettyWrapper, supportedProtocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String noSelectMatchFound() throws Exception {
|
||||
throw new SSLHandshakeException("Selected protocol is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
protected static class NoFailProtocolSelectionListener implements ProtocolSelectionListener {
|
||||
private final JdkSslEngine jettyWrapper;
|
||||
private final List<String> supportedProtocols;
|
||||
|
||||
public NoFailProtocolSelectionListener(JdkSslEngine jettyWrapper, List<String> supportedProtocols) {
|
||||
this.jettyWrapper = jettyWrapper;
|
||||
this.supportedProtocols = supportedProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
jettyWrapper.getSession().setApplicationProtocol(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(String protocol) throws Exception {
|
||||
if (supportedProtocols.contains(protocol)) {
|
||||
jettyWrapper.getSession().setApplicationProtocol(protocol);
|
||||
} else {
|
||||
noSelectedMatchFound(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public void noSelectedMatchFound(String protocol) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class FailProtocolSelectionListener extends NoFailProtocolSelectionListener {
|
||||
public FailProtocolSelectionListener(JdkSslEngine jettyWrapper, List<String> supportedProtocols) {
|
||||
super(jettyWrapper, supportedProtocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noSelectedMatchFound(String protocol) throws Exception {
|
||||
throw new SSLHandshakeException("No compatible protocols found");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* The {@link JdkApplicationProtocolNegotiator} to use if you do not care about NPN or ALPN and are using
|
||||
* {@link SslProvider#JDK}.
|
||||
*/
|
||||
final class JdkDefaultApplicationProtocolNegotiator implements JdkApplicationProtocolNegotiator {
|
||||
public static final JdkDefaultApplicationProtocolNegotiator INSTANCE =
|
||||
new JdkDefaultApplicationProtocolNegotiator();
|
||||
private static final SslEngineWrapperFactory DEFAULT_SSL_ENGINE_WRAPPER_FACTORY = new SslEngineWrapperFactory() {
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator,
|
||||
boolean isServer) {
|
||||
return engine;
|
||||
}
|
||||
};
|
||||
|
||||
private JdkDefaultApplicationProtocolNegotiator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslEngineWrapperFactory wrapperFactory() {
|
||||
return DEFAULT_SSL_ENGINE_WRAPPER_FACTORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectorFactory protocolSelectorFactory() {
|
||||
throw new UnsupportedOperationException("Application protocol negotiation unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectionListenerFactory protocolListenerFactory() {
|
||||
throw new UnsupportedOperationException("Application protocol negotiation unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* The {@link JdkApplicationProtocolNegotiator} to use if you need NPN and are using {@link SslProvider#JDK}.
|
||||
*/
|
||||
public final class JdkNpnApplicationProtocolNegotiator extends JdkBaseApplicationProtocolNegotiator {
|
||||
private static final SslEngineWrapperFactory NPN_WRAPPER = new SslEngineWrapperFactory() {
|
||||
{
|
||||
if (!JdkNpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("NPN unsupported. Is your classpatch configured correctly?"
|
||||
+ " See http://www.eclipse.org/jetty/documentation/current/npn-chapter.html#npn-starting");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator,
|
||||
boolean isServer) {
|
||||
return new JdkNpnSslEngine(engine, applicationNegotiator, isServer);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(Iterable<String> protocols) {
|
||||
this(false, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(String... protocols) {
|
||||
this(false, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, Iterable<String> protocols) {
|
||||
this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, String... protocols) {
|
||||
this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
||||
* @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
||||
boolean serverFailIfNoCommonProtocols, Iterable<String> protocols) {
|
||||
this(clientFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
||||
serverFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
||||
protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
||||
* @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
||||
boolean serverFailIfNoCommonProtocols, String... protocols) {
|
||||
this(clientFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
||||
serverFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
||||
protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
||||
* @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
||||
ProtocolSelectionListenerFactory listenerFactory, Iterable<String> protocols) {
|
||||
super(NPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
||||
* @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public JdkNpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
||||
ProtocolSelectionListenerFactory listenerFactory, String... protocols) {
|
||||
super(NPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
||||
}
|
||||
}
|
@ -16,6 +16,12 @@
|
||||
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
|
||||
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
@ -25,7 +31,7 @@ import org.eclipse.jetty.npn.NextProtoNego;
|
||||
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
||||
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
|
||||
|
||||
final class JettyNpnSslEngine extends JettySslEngine {
|
||||
final class JdkNpnSslEngine extends JdkSslEngine {
|
||||
private static boolean available;
|
||||
|
||||
static boolean isAvailable() {
|
||||
@ -52,30 +58,37 @@ final class JettyNpnSslEngine extends JettySslEngine {
|
||||
}
|
||||
}
|
||||
|
||||
JettyNpnSslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
|
||||
super(engine, nextProtocols, server);
|
||||
JdkNpnSslEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator, boolean server) {
|
||||
super(engine);
|
||||
checkNotNull(applicationNegotiator, "applicationNegotiator");
|
||||
|
||||
if (server) {
|
||||
final ProtocolSelectionListener protocolListener = checkNotNull(applicationNegotiator
|
||||
.protocolListenerFactory().newListener(this, applicationNegotiator.protocols()),
|
||||
"protocolListener");
|
||||
NextProtoNego.put(engine, new ServerProvider() {
|
||||
@Override
|
||||
public void unsupported() {
|
||||
getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1));
|
||||
protocolListener.unsupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return nextProtocols;
|
||||
return applicationNegotiator.protocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void protocolSelected(String protocol) {
|
||||
getSession().setApplicationProtocol(protocol);
|
||||
try {
|
||||
protocolListener.selected(protocol);
|
||||
} catch (Throwable t) {
|
||||
PlatformDependent.throwException(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final String[] array = nextProtocols.toArray(new String[nextProtocols.size()]);
|
||||
final String fallback = array[array.length - 1];
|
||||
|
||||
final ProtocolSelector protocolSelector = checkNotNull(applicationNegotiator.protocolSelectorFactory()
|
||||
.newSelector(this, new HashSet<String>(applicationNegotiator.protocols())), "protocolSelector");
|
||||
NextProtoNego.put(engine, new ClientProvider() {
|
||||
@Override
|
||||
public boolean supports() {
|
||||
@ -84,20 +97,17 @@ final class JettyNpnSslEngine extends JettySslEngine {
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
session.setApplicationProtocol(null);
|
||||
protocolSelector.unsupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String selectProtocol(List<String> protocols) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
String p = array[i];
|
||||
if (protocols.contains(p)) {
|
||||
session.setApplicationProtocol(p);
|
||||
return p;
|
||||
}
|
||||
try {
|
||||
return protocolSelector.select(protocols);
|
||||
} catch (Throwable t) {
|
||||
PlatformDependent.throwException(t);
|
||||
return null;
|
||||
}
|
||||
session.setApplicationProtocol(fallback);
|
||||
return fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -105,13 +115,13 @@ final class JettyNpnSslEngine extends JettySslEngine {
|
||||
|
||||
@Override
|
||||
public void closeInbound() throws SSLException {
|
||||
NextProtoNego.remove(engine);
|
||||
NextProtoNego.remove(getWrappedEngine());
|
||||
super.closeInbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOutbound() {
|
||||
NextProtoNego.remove(engine);
|
||||
NextProtoNego.remove(getWrappedEngine());
|
||||
super.closeOutbound();
|
||||
}
|
||||
}
|
@ -23,10 +23,8 @@ 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;
|
||||
@ -39,7 +37,6 @@ import javax.security.auth.x500.X500Principal;
|
||||
public final class JdkSslClientContext extends JdkSslContext {
|
||||
|
||||
private final SSLContext ctx;
|
||||
private final List<String> nextProtocols;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@ -80,7 +77,7 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
*/
|
||||
public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
this(certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
null, DefaultSslWrapperFactory.INSTANCE, 0, 0);
|
||||
JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,10 +91,7 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* @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 apn Provides a means to configure parameters related to application protocol negotiation.
|
||||
* @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.
|
||||
@ -105,13 +99,35 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
*/
|
||||
public JdkSslClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
|
||||
Iterable<String> nextProtocols, SslEngineWrapperFactory wrapperFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(certChainFile, trustManagerFactory, ciphers, cipherFilter,
|
||||
toNegotiator(apn, false), sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param certChainFile an X.509 certificate chain file in PEM format.
|
||||
* {@code null} to use the system default
|
||||
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
|
||||
* that verifies the certificates sent from servers.
|
||||
* {@code null} to use the default.
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* @param apn Application Protocol Negotiator object.
|
||||
* @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.
|
||||
* {@code 0} to use the default value.
|
||||
*/
|
||||
public JdkSslClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
super(ciphers, cipherFilter, wrapperFactory);
|
||||
|
||||
this.nextProtocols = translateProtocols(nextProtocols);
|
||||
super(ciphers, cipherFilter, apn);
|
||||
|
||||
try {
|
||||
if (certChainFile == null) {
|
||||
@ -168,11 +184,6 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nextProtocols() {
|
||||
return nextProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLContext context() {
|
||||
return ctx;
|
||||
|
@ -16,13 +16,11 @@
|
||||
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
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;
|
||||
@ -30,6 +28,10 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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.
|
||||
*/
|
||||
@ -120,18 +122,18 @@ public abstract class JdkSslContext extends SslContext {
|
||||
|
||||
private final String[] cipherSuites;
|
||||
private final List<String> unmodifiableCipherSuites;
|
||||
private final SslEngineWrapperFactory wrapperFactory;
|
||||
private final JdkApplicationProtocolNegotiator apn;
|
||||
|
||||
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, SslEngineWrapperFactory wrapperFactory) {
|
||||
if (wrapperFactory == null) {
|
||||
throw new NullPointerException("wrapperFactory");
|
||||
}
|
||||
if (cipherFilter == null) {
|
||||
throw new NullPointerException("cipherFilter");
|
||||
}
|
||||
cipherSuites = cipherFilter.filterCipherSuites(ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS);
|
||||
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config,
|
||||
boolean isServer) {
|
||||
this(ciphers, cipherFilter, toNegotiator(config, isServer));
|
||||
}
|
||||
|
||||
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn) {
|
||||
this.apn = checkNotNull(apn, "apn");
|
||||
cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
|
||||
ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS);
|
||||
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
|
||||
this.wrapperFactory = wrapperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -184,6 +186,75 @@ public abstract class JdkSslContext extends SslContext {
|
||||
}
|
||||
|
||||
private SSLEngine wrapEngine(SSLEngine engine) {
|
||||
return wrapperFactory.wrapSslEngine(engine, nextProtocols(), isServer());
|
||||
return apn.wrapperFactory().wrapSslEngine(engine, apn, isServer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
|
||||
return apn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a {@link ApplicationProtocolConfiguration} object to a {@link JdkApplicationProtocolNegotiator} object.
|
||||
* @param config The configuration which defines the translation
|
||||
* @param isServer {@code true} if a server {@code false} otherwise.
|
||||
* @return The results of the translation
|
||||
*/
|
||||
static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
|
||||
if (config == null) {
|
||||
return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||
}
|
||||
|
||||
switch(config.protocol()) {
|
||||
case NONE:
|
||||
return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||
case ALPN:
|
||||
if (isServer) {
|
||||
switch(config.selectorFailureBehavior()) {
|
||||
case FATAL_ALERT:
|
||||
return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
||||
case NO_ADVERTISE:
|
||||
return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
||||
.append(config.selectorFailureBehavior()).append(" failure behavior").toString());
|
||||
}
|
||||
} else {
|
||||
switch(config.selectedListenerFailureBehavior()) {
|
||||
case ACCEPT:
|
||||
return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
||||
case FATAL_ALERT:
|
||||
return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
||||
.append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
|
||||
}
|
||||
}
|
||||
case NPN:
|
||||
if (isServer) {
|
||||
switch(config.selectedListenerFailureBehavior()) {
|
||||
case ACCEPT:
|
||||
return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
||||
case FATAL_ALERT:
|
||||
return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
||||
.append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
|
||||
}
|
||||
} else {
|
||||
switch(config.selectorFailureBehavior()) {
|
||||
case FATAL_ALERT:
|
||||
return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
||||
case NO_ADVERTISE:
|
||||
return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
||||
.append(config.selectorFailureBehavior()).append(" failure behavior").toString());
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
||||
.append(config.protocol()).append(" protocol").toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
@ -25,27 +24,24 @@ 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");
|
||||
}
|
||||
class JdkSslEngine extends SSLEngine {
|
||||
private final SSLEngine engine;
|
||||
private final JdkSslSession session;
|
||||
|
||||
JdkSslEngine(SSLEngine engine) {
|
||||
this.engine = engine;
|
||||
session = new JettySslSession(engine);
|
||||
session = new JdkSslSession(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JettySslSession getSession() {
|
||||
public JdkSslSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public SSLEngine getWrappedEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInbound() throws SSLException {
|
||||
engine.closeInbound();
|
@ -43,7 +43,6 @@ 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;
|
||||
|
||||
@ -53,7 +52,6 @@ import javax.net.ssl.SSLSessionContext;
|
||||
public final class JdkSslServerContext extends JdkSslContext {
|
||||
|
||||
private final SSLContext ctx;
|
||||
private final List<String> nextProtocols;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@ -75,7 +73,7 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
*/
|
||||
public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
null, DefaultSslWrapperFactory.INSTANCE, 0, 0);
|
||||
JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,10 +86,7 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* @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 apn Provides a means to configure parameters related to application protocol negotiation.
|
||||
* @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.
|
||||
@ -99,13 +94,34 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
*/
|
||||
public JdkSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
|
||||
Iterable<String> nextProtocols, SslEngineWrapperFactory wrapperFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, ciphers, cipherFilter,
|
||||
toNegotiator(apn, true), sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param certChainFile an X.509 certificate chain file in PEM format
|
||||
* @param keyFile a PKCS#8 private key file in PEM format
|
||||
* @param keyPassword the password of the {@code keyFile}.
|
||||
* {@code null} if it's not password-protected.
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* @param apn Application Protocol Negotiator object.
|
||||
* @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.
|
||||
* {@code 0} to use the default value.
|
||||
*/
|
||||
public JdkSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
super(ciphers, cipherFilter, wrapperFactory);
|
||||
|
||||
this.nextProtocols = translateProtocols(nextProtocols);
|
||||
super(ciphers, cipherFilter, apn);
|
||||
|
||||
if (certChainFile == null) {
|
||||
throw new NullPointerException("certChainFile");
|
||||
@ -183,11 +199,6 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nextProtocols() {
|
||||
return nextProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLContext context() {
|
||||
return ctx;
|
||||
|
@ -24,11 +24,11 @@ import javax.security.cert.X509Certificate;
|
||||
import java.security.Principal;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
final class JettySslSession implements SSLSession {
|
||||
final class JdkSslSession implements SSLSession {
|
||||
private final SSLEngine engine;
|
||||
private volatile String applicationProtocol;
|
||||
|
||||
JettySslSession(SSLEngine engine) {
|
||||
JdkSslSession(SSLEngine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* OpenSSL version of {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
public interface OpenSslApplicationProtocolNegotiator extends ApplicationProtocolNegotiator {
|
||||
// The current need for this interface is primarily for consistency with the JDK provider
|
||||
// How the OpenSsl provider will provide extensibility to control the application selection
|
||||
// and notification algorithms is not yet known (JNI, pure java, tcnative hooks, etc...).
|
||||
// OpenSSL itself is currently not in compliance with the specification for the 2 supported
|
||||
// protocols (ALPN, NPN) with respect to allowing the handshakes to fail during the application
|
||||
// protocol negotiation process. Issue https://github.com/openssl/openssl/issues/188 has been created for this.
|
||||
}
|
@ -15,21 +15,21 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* Factory for not wrapping {@link SSLEngine} object and just returning it.
|
||||
* Provides no {@link ApplicationProtocolNegotiator} functionality for OpenSSL.
|
||||
*/
|
||||
public final class DefaultSslWrapperFactory implements SslEngineWrapperFactory {
|
||||
public static final DefaultSslWrapperFactory INSTANCE = new DefaultSslWrapperFactory();
|
||||
final class OpenSslDefaultApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator {
|
||||
static final OpenSslDefaultApplicationProtocolNegotiator INSTANCE =
|
||||
new OpenSslDefaultApplicationProtocolNegotiator();
|
||||
|
||||
private DefaultSslWrapperFactory() {
|
||||
private OpenSslDefaultApplicationProtocolNegotiator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, List<String> protocols, boolean isServer) {
|
||||
return engine;
|
||||
public List<String> protocols() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -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 static io.netty.handler.ssl.ApplicationProtocolUtil.toList;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* OpenSSL {@link ApplicationProtocolNegotiator} for NPN.
|
||||
*/
|
||||
public final class OpenSslNpnApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator {
|
||||
private final List<String> protocols;
|
||||
|
||||
public OpenSslNpnApplicationProtocolNegotiator(Iterable<String> protocols) {
|
||||
this.protocols = checkNotNull(toList(protocols), "protocols");
|
||||
}
|
||||
|
||||
public OpenSslNpnApplicationProtocolNegotiator(String... protocols) {
|
||||
this.protocols = checkNotNull(toList(protocols), "protocols");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return protocols;
|
||||
}
|
||||
}
|
@ -15,20 +15,23 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import org.apache.tomcat.jni.Pool;
|
||||
import org.apache.tomcat.jni.SSL;
|
||||
import org.apache.tomcat.jni.SSLContext;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.apache.tomcat.jni.Pool;
|
||||
import org.apache.tomcat.jni.SSL;
|
||||
import org.apache.tomcat.jni.SSLContext;
|
||||
|
||||
/**
|
||||
* A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
||||
*/
|
||||
@ -65,7 +68,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> protocols;
|
||||
private final OpenSslApplicationProtocolNegotiator apn;
|
||||
|
||||
/** The OpenSSL SSL_CTX object */
|
||||
private final long ctx;
|
||||
@ -90,7 +93,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
* {@code null} if it's not password-protected.
|
||||
*/
|
||||
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
this(certChainFile, keyFile, keyPassword, null, OpenSslDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,8 +105,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
* {@code null} if it's not password-protected.
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@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 apn Provides a means to configure parameters related to application protocol negotiation.
|
||||
* @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.
|
||||
@ -111,20 +113,39 @@ public final class OpenSslServerContext extends SslContext {
|
||||
*/
|
||||
public OpenSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, ciphers, toNegotiator(apn, false), sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param certChainFile an X.509 certificate chain file in PEM format
|
||||
* @param keyFile a PKCS#8 private key file in PEM format
|
||||
* @param keyPassword the password of the {@code keyFile}.
|
||||
* {@code null} if it's not password-protected.
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param apn Application protocol negotiator.
|
||||
* @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.
|
||||
* {@code 0} to use the default value.
|
||||
*/
|
||||
public OpenSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, OpenSslApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
OpenSsl.ensureAvailability();
|
||||
|
||||
if (certChainFile == null) {
|
||||
throw new NullPointerException("certChainFile");
|
||||
}
|
||||
checkNotNull(certChainFile, "certChainFile");
|
||||
if (!certChainFile.isFile()) {
|
||||
throw new IllegalArgumentException("certChainFile is not a file: " + certChainFile);
|
||||
}
|
||||
if (keyFile == null) {
|
||||
throw new NullPointerException("keyPath");
|
||||
}
|
||||
checkNotNull(keyFile, "keyFile");
|
||||
this.apn = checkNotNull(apn, "apn");
|
||||
if (!keyFile.isFile()) {
|
||||
throw new IllegalArgumentException("keyPath is not a file: " + keyFile);
|
||||
}
|
||||
@ -136,8 +157,6 @@ public final class OpenSslServerContext extends SslContext {
|
||||
keyPassword = "";
|
||||
}
|
||||
|
||||
protocols = translateProtocols(nextProtocols);
|
||||
|
||||
for (String c: ciphers) {
|
||||
if (c == null) {
|
||||
break;
|
||||
@ -209,6 +228,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
}
|
||||
|
||||
/* Set next protocols for next protocol negotiation extension, if specified */
|
||||
List<String> protocols = apn.protocols();
|
||||
if (!protocols.isEmpty()) {
|
||||
// Convert the protocol list into a comma-separated string.
|
||||
StringBuilder nextProtocolBuf = new StringBuilder();
|
||||
@ -274,8 +294,8 @@ public final class OpenSslServerContext extends SslContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nextProtocols() {
|
||||
return protocols;
|
||||
public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
|
||||
return apn;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -297,6 +317,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
*/
|
||||
@Override
|
||||
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||
List<String> protocols = apn.protocols();
|
||||
if (protocols.isEmpty()) {
|
||||
return new OpenSslEngine(ctx, alloc, null);
|
||||
} else {
|
||||
@ -337,4 +358,38 @@ public final class OpenSslServerContext extends SslContext {
|
||||
Pool.destroy(aprPool);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a {@link ApplicationProtocolConfiguration} object to a
|
||||
* {@link OpenSslApplicationProtocolNegotiator} object.
|
||||
* @param config The configuration which defines the translation
|
||||
* @param isServer {@code true} if a server {@code false} otherwise.
|
||||
* @return The results of the translation
|
||||
*/
|
||||
private static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config,
|
||||
boolean isServer) {
|
||||
if (config == null) {
|
||||
return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||
}
|
||||
|
||||
switch(config.protocol()) {
|
||||
case NONE:
|
||||
return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||
case NPN:
|
||||
if (isServer) {
|
||||
switch(config.selectedListenerFailureBehavior()) {
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
return new OpenSslNpnApplicationProtocolNegotiator(config.supportedProtocols());
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.selectedListenerFailureBehavior()).append(" behavior").toString());
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException("OpenSSL provider does not support client mode");
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.protocol()).append(" protocol").toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,18 +20,15 @@ import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
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;
|
||||
|
||||
/**
|
||||
* A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}.
|
||||
* Internally, it is implemented via JDK's {@link SSLContext} or OpenSSL's {@code SSL_CTX}.
|
||||
@ -77,28 +74,6 @@ 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}.
|
||||
*
|
||||
@ -134,11 +109,7 @@ public abstract class SslContext {
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* @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 apn Provides a means to configure parameters related to application protocol negotiation.
|
||||
* @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.
|
||||
@ -147,12 +118,11 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
|
||||
Iterable<String> nextProtocols, SslEngineWrapperFactory wrapperFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newServerContext(
|
||||
null, certChainFile, keyFile, keyPassword,
|
||||
ciphers, cipherFilter, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,7 +153,7 @@ 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, IdentityCipherSuiteFilter.INSTANCE,
|
||||
null, DefaultSslWrapperFactory.INSTANCE, 0, 0);
|
||||
null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,11 +169,7 @@ public abstract class SslContext {
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* Only required if {@code provider} is {@link SslProvider#JDK}
|
||||
* @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 apn Provides a means to configure parameters related to application protocol negotiation.
|
||||
* @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.
|
||||
@ -213,8 +179,7 @@ public abstract class SslContext {
|
||||
public static SslContext newServerContext(
|
||||
SslProvider provider,
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
|
||||
Iterable<String> nextProtocols, SslEngineWrapperFactory wrapperFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
if (provider == null) {
|
||||
@ -225,11 +190,11 @@ public abstract class SslContext {
|
||||
case JDK:
|
||||
return new JdkSslServerContext(
|
||||
certChainFile, keyFile, keyPassword,
|
||||
ciphers, cipherFilter, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
case OPENSSL:
|
||||
return new OpenSslServerContext(
|
||||
certChainFile, keyFile, keyPassword,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
ciphers, apn, sessionCacheSize, sessionTimeout);
|
||||
default:
|
||||
throw new Error(provider.toString());
|
||||
}
|
||||
@ -295,11 +260,7 @@ public abstract class SslContext {
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* @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 apn Provides a means to configure parameters related to application protocol negotiation.
|
||||
* @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.
|
||||
@ -309,12 +270,11 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
|
||||
Iterable<String> nextProtocols, SslEngineWrapperFactory wrapperFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newClientContext(
|
||||
null, certChainFile, trustManagerFactory,
|
||||
ciphers, cipherFilter, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -375,7 +335,7 @@ public abstract class SslContext {
|
||||
public static SslContext newClientContext(
|
||||
SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(provider, certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
null, DefaultSslWrapperFactory.INSTANCE, 0, 0);
|
||||
null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -391,11 +351,7 @@ public abstract class SslContext {
|
||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||
* {@code null} to use the default cipher suites.
|
||||
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* @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 apn Provides a means to configure parameters related to application protocol negotiation.
|
||||
* @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.
|
||||
@ -406,8 +362,7 @@ public abstract class SslContext {
|
||||
public static SslContext newClientContext(
|
||||
SslProvider provider,
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
|
||||
Iterable<String> nextProtocols, SslEngineWrapperFactory wrapperFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
if (provider != null && provider != SslProvider.JDK) {
|
||||
@ -416,7 +371,7 @@ public abstract class SslContext {
|
||||
|
||||
return new JdkSslClientContext(
|
||||
certChainFile, trustManagerFactory,
|
||||
ciphers, cipherFilter, nextProtocols, wrapperFactory, sessionCacheSize, sessionTimeout);
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
SslContext() { }
|
||||
@ -449,12 +404,9 @@ public abstract class SslContext {
|
||||
public abstract long sessionTimeout();
|
||||
|
||||
/**
|
||||
* Returns the list of application layer protocols for the TLS NPN/ALPN extension, in the order of preference.
|
||||
*
|
||||
* @return the list of application layer protocols.
|
||||
* {@code null} if NPN/ALPN extension has been disabled.
|
||||
* Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions.
|
||||
*/
|
||||
public abstract List<String> nextProtocols();
|
||||
public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();
|
||||
|
||||
/**
|
||||
* Creates a new {@link SSLEngine}.
|
||||
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
438
handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java
Normal file
438
handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java
Normal file
@ -0,0 +1,438 @@
|
||||
/*
|
||||
* 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.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
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.ChannelHandlerAdapter;
|
||||
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.JdkApplicationProtocolNegotiator.ProtocolSelector;
|
||||
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectorFactory;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
public class JdkSslEngineTest {
|
||||
private static final String APPLICATION_LEVEL_PROTOCOL = "my-protocol";
|
||||
private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO";
|
||||
|
||||
@Mock
|
||||
private MessageReciever serverReceiver;
|
||||
@Mock
|
||||
private MessageReciever clientReceiver;
|
||||
|
||||
private Throwable serverException;
|
||||
private Throwable clientException;
|
||||
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;
|
||||
private final CountDownLatch latch;
|
||||
|
||||
public MessageDelegatorChannelHandler(MessageReciever receiver, CountDownLatch latch) {
|
||||
super(false);
|
||||
this.receiver = receiver;
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
|
||||
receiver.messageReceived(msg);
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
Future<?> serverGroup = sb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
Future<?> serverChildGroup = sb.childGroup().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
Future<?> clientGroup = cb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
serverGroup.sync();
|
||||
serverChildGroup.sync();
|
||||
clientGroup.sync();
|
||||
}
|
||||
clientChannel = null;
|
||||
serverChannel = null;
|
||||
serverConnectedChannel = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNpn() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkNpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("NPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator apn = new JdkNpnApplicationProtocolNegotiator(true, true,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
mySetup(apn);
|
||||
runTest();
|
||||
} catch (RuntimeException 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNpnNoCompatibleProtocolsNoHandshakeFailure() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkNpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("NPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator clientApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
JdkApplicationProtocolNegotiator serverApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
||||
APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
||||
mySetup(serverApn, clientApn);
|
||||
runTest(null);
|
||||
} catch (Exception e) {
|
||||
// ALPN availability is dependent on the java version. If ALPN is not available because of
|
||||
// java version incompatibility don't fail the test, but instead just skip the test
|
||||
assumeNoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNpnNoCompatibleProtocolsClientHandshakeFailure() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkNpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("NPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator clientApn = new JdkNpnApplicationProtocolNegotiator(true, true,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
JdkApplicationProtocolNegotiator serverApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
||||
APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
||||
mySetup(serverApn, clientApn);
|
||||
assertTrue(clientLatch.await(2, TimeUnit.SECONDS));
|
||||
assertTrue(clientException instanceof SSLHandshakeException);
|
||||
} catch (RuntimeException 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNpnNoCompatibleProtocolsServerHandshakeFailure() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkNpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("NPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator clientApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
JdkApplicationProtocolNegotiator serverApn = new JdkNpnApplicationProtocolNegotiator(true, true,
|
||||
APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
||||
mySetup(serverApn, clientApn);
|
||||
assertTrue(serverLatch.await(2, TimeUnit.SECONDS));
|
||||
assertTrue(serverException instanceof SSLHandshakeException);
|
||||
} catch (RuntimeException 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlpn() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkAlpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("ALPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator apn = new JdkAlpnApplicationProtocolNegotiator(true, true,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
mySetup(apn);
|
||||
runTest();
|
||||
} catch (Exception e) {
|
||||
// ALPN availability is dependent on the java version. If ALPN is not available because of
|
||||
// java version incompatibility don't fail the test, but instead just skip the test
|
||||
assumeNoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlpnNoCompatibleProtocolsNoHandshakeFailure() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkAlpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("ALPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(false, false,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator(false, false,
|
||||
APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
||||
mySetup(serverApn, clientApn);
|
||||
runTest(null);
|
||||
} catch (Exception e) {
|
||||
// ALPN availability is dependent on the java version. If ALPN is not available because of
|
||||
// java version incompatibility don't fail the test, but instead just skip the test
|
||||
assumeNoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlpnNoCompatibleProtocolsServerHandshakeFailure() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkAlpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("ALPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(false, false,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator(true, true,
|
||||
APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
||||
mySetup(serverApn, clientApn);
|
||||
assertTrue(serverLatch.await(2, TimeUnit.SECONDS));
|
||||
assertTrue(serverException instanceof SSLHandshakeException);
|
||||
} catch (Exception e) {
|
||||
// ALPN availability is dependent on the java version. If ALPN is not available because of
|
||||
// java version incompatibility don't fail the test, but instead just skip the test
|
||||
assumeNoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlpnNoCompatibleProtocolsClientHandshakeFailure() throws Exception {
|
||||
try {
|
||||
// Typical code will not have to check this, but will get a initialization error on class load.
|
||||
// Check in this test just in case we have multiple tests that just the class and we already ignored the
|
||||
// initialization error.
|
||||
if (!JdkAlpnSslEngine.isAvailable()) {
|
||||
throw new RuntimeException("ALPN not on classpath");
|
||||
}
|
||||
JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(true, true,
|
||||
APPLICATION_LEVEL_PROTOCOL);
|
||||
JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator(
|
||||
new ProtocolSelectorFactory() {
|
||||
@Override
|
||||
public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
|
||||
return new ProtocolSelector() {
|
||||
@Override
|
||||
public void unsupported() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String select(List<String> protocols) {
|
||||
return APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE;
|
||||
}
|
||||
};
|
||||
}
|
||||
}, JdkBaseApplicationProtocolNegotiator.FAIL_SELECTION_LISTENER_FACTORY,
|
||||
APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
||||
mySetup(serverApn, clientApn);
|
||||
assertTrue(clientLatch.await(2, TimeUnit.SECONDS));
|
||||
assertTrue(clientException instanceof SSLHandshakeException);
|
||||
} catch (Exception e) {
|
||||
// ALPN availability is dependent on the java version. If ALPN is not available because of
|
||||
// java version incompatibility don't fail the test, but instead just skip the test
|
||||
assumeNoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void mySetup(JdkApplicationProtocolNegotiator apn) throws InterruptedException, SSLException,
|
||||
CertificateException {
|
||||
mySetup(apn, apn);
|
||||
}
|
||||
|
||||
private void mySetup(JdkApplicationProtocolNegotiator serverApn, JdkApplicationProtocolNegotiator clientApn)
|
||||
throws InterruptedException, SSLException, CertificateException {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = new JdkSslServerContext(ssc.certificate(), ssc.privateKey(), null, null,
|
||||
IdentityCipherSuiteFilter.INSTANCE, serverApn, 0, 0);
|
||||
clientSslCtx = new JdkSslClientContext(null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
IdentityCipherSuiteFilter.INSTANCE, clientApn, 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, serverLatch));
|
||||
p.addLast(new ChannelHandlerAdapter() {
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (cause.getCause() instanceof SSLHandshakeException) {
|
||||
serverException = cause.getCause();
|
||||
serverLatch.countDown();
|
||||
} else {
|
||||
ctx.fireExceptionCaught(cause);
|
||||
}
|
||||
}
|
||||
});
|
||||
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, clientLatch));
|
||||
p.addLast(new ChannelHandlerAdapter() {
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (cause.getCause() instanceof SSLHandshakeException) {
|
||||
clientException = cause.getCause();
|
||||
clientLatch.countDown();
|
||||
} else {
|
||||
ctx.fireExceptionCaught(cause);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
runTest(APPLICATION_LEVEL_PROTOCOL);
|
||||
}
|
||||
|
||||
private void runTest(String expectedApplicationProtocol) throws Exception {
|
||||
final ByteBuf clientMessage = Unpooled.copiedBuffer("I am a client".getBytes());
|
||||
final ByteBuf serverMessage = Unpooled.copiedBuffer("I am a server".getBytes());
|
||||
try {
|
||||
writeAndVerifyReceived(clientMessage.retain(), clientChannel, serverLatch, serverReceiver);
|
||||
writeAndVerifyReceived(serverMessage.retain(), serverConnectedChannel, clientLatch, clientReceiver);
|
||||
verifyApplicationLevelProtocol(clientChannel, expectedApplicationProtocol);
|
||||
verifyApplicationLevelProtocol(serverConnectedChannel, expectedApplicationProtocol);
|
||||
} finally {
|
||||
clientMessage.release();
|
||||
serverMessage.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyApplicationLevelProtocol(Channel channel, String expectedApplicationProtocol) {
|
||||
SslHandler handler = channel.pipeline().get(SslHandler.class);
|
||||
assertNotNull(handler);
|
||||
String[] protocol = handler.engine().getSession().getProtocol().split(":");
|
||||
assertNotNull(protocol);
|
||||
if (expectedApplicationProtocol != null && !expectedApplicationProtocol.isEmpty()) {
|
||||
assertTrue("protocol.length must be greater than 1 but is " + protocol.length, protocol.length > 1);
|
||||
assertEquals(expectedApplicationProtocol, protocol[1]);
|
||||
} else {
|
||||
assertEquals(1, protocol.length);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeAndVerifyReceived(ByteBuf message, Channel sendChannel, CountDownLatch receiverLatch,
|
||||
MessageReciever receiver) throws Exception {
|
||||
List<ByteBuf> dataCapture = null;
|
||||
try {
|
||||
sendChannel.writeAndFlush(message);
|
||||
receiverLatch.await(5, TimeUnit.SECONDS);
|
||||
message.resetReaderIndex();
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(receiver).messageReceived(captor.capture());
|
||||
dataCapture = captor.getAllValues();
|
||||
assertEquals(message, dataCapture.get(0));
|
||||
} finally {
|
||||
if (dataCapture != null) {
|
||||
for (ByteBuf data : dataCapture) {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
/*
|
||||
* 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.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
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 io.netty.util.concurrent.Future;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
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.ArgumentCaptor;
|
||||
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;
|
||||
private final CountDownLatch latch;
|
||||
|
||||
public MessageDelegatorChannelHandler(MessageReciever receiver, CountDownLatch latch) {
|
||||
super(false);
|
||||
this.receiver = receiver;
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
|
||||
receiver.messageReceived(msg);
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
Future<?> serverGroup = sb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
Future<?> serverChildGroup = sb.childGroup().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
Future<?> clientGroup = cb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
serverGroup.sync();
|
||||
serverChildGroup.sync();
|
||||
clientGroup.sync();
|
||||
}
|
||||
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,
|
||||
IdentityCipherSuiteFilter.INSTANCE, Arrays.asList(APPLICATION_LEVEL_PROTOCOL), wrapper, 0, 0);
|
||||
clientSslCtx = SslContext.newClientContext(SslProvider.JDK, null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
IdentityCipherSuiteFilter.INSTANCE, 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, serverLatch));
|
||||
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, clientLatch));
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
final ByteBuf clientMessage = Unpooled.copiedBuffer("I am a client".getBytes());
|
||||
final 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 {
|
||||
List<ByteBuf> dataCapture = null;
|
||||
try {
|
||||
sendChannel.writeAndFlush(message);
|
||||
receiverLatch.await(5, TimeUnit.SECONDS);
|
||||
message.resetReaderIndex();
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(receiver).messageReceived(captor.capture());
|
||||
dataCapture = captor.getAllValues();
|
||||
assertEquals(message, dataCapture.get(0));
|
||||
} finally {
|
||||
if (dataCapture != null) {
|
||||
for (ByteBuf data : dataCapture) {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
pom.xml
74
pom.xml
@ -109,10 +109,22 @@
|
||||
<!-- 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 -->
|
||||
<jetty.alpn.version>8.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.alpn.version>8.1.0.v20141016</jetty.alpn.version>
|
||||
<argLine.bootcp>-Xbootclasspath/p:${jetty.alpn.path}</argLine.bootcp>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>alpn-8u25</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.8.0_25</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.alpn.version>8.1.1.v20141016</jetty.alpn.version>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>linux</id>
|
||||
<activation>
|
||||
@ -283,7 +295,7 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
@ -297,7 +309,7 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
@ -311,7 +323,7 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
@ -324,8 +336,8 @@
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
@ -338,8 +350,8 @@
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
@ -352,8 +364,8 @@
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
@ -366,8 +378,36 @@
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.alpn.version>7.0.0.v20140317</jetty.alpn.version>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u71</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_71</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.9.v20141016</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u72</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_72</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.9.v20141016</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
@ -394,9 +434,9 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<jboss.marshalling.version>1.3.18.GA</jboss.marshalling.version>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.npn.version>1.1.9.v20141016</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.version>8.1.0.v20141016</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
|
||||
@ -470,7 +510,7 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.npn</groupId>
|
||||
<artifactId>npn-api</artifactId>
|
||||
<version>1.1.0.v20120525</version>
|
||||
<version>1.1.1.v20141010</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
@ -480,7 +520,7 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.1.0.v20141014</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
@ -918,7 +958,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,org.eclipse.jetty.alpn;version="[1,)";resolution=optional,*</Import-Package>
|
||||
<Import-Package>sun.misc.*;resolution:=optional,sun.nio.ch;resolution:=optional,sun.security.*;resolution:=optional,org.eclipse.jetty.npn;version="[1,)";resolution=optional,org.eclipse.jetty.alpn;version="[1,)";resolution=optional,*</Import-Package>
|
||||
<!-- override "internal" private package convention -->
|
||||
<Private-Package>!*</Private-Package>
|
||||
</instructions>
|
||||
|
Loading…
x
Reference in New Issue
Block a user