Future compatibility with TLS ALPN

Motivation:

According to TLS ALPN draft-05, a client sends the list of the supported
protocols and a server responds with the selected protocol, which is
different from NPN.  Therefore, ApplicationProtocolSelector won't work
with ALPN

Modifications:

- Use Iterable<String> to list the supported protocols on the client
  side, rather than using ApplicationProtocolSelector
- Remove ApplicationProtocolSelector

Result:

Future compatibility with TLS ALPN
This commit is contained in:
Trustin Lee 2014-05-22 09:59:58 +09:00
parent 2fb7af0163
commit cb66866730
9 changed files with 96 additions and 248 deletions

View File

@ -14,10 +14,6 @@
*/ */
package io.netty.example.http2.client; package io.netty.example.http2.client;
import static io.netty.example.http2.Http2ExampleUtil.parseEndpointConfig;
import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
@ -25,16 +21,21 @@ import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.example.http2.Http2ExampleUtil.EndpointConfig; import io.netty.example.http2.Http2ExampleUtil.*;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http2.Http2OrHttpChooser.SelectedProtocol; import io.netty.handler.codec.http2.Http2OrHttpChooser.SelectedProtocol;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.net.InetSocketAddress;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import java.net.InetSocketAddress;
import java.util.Arrays;
import static io.netty.example.http2.Http2ExampleUtil.*;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static io.netty.handler.codec.http.HttpMethod.*;
import static io.netty.handler.codec.http.HttpVersion.*;
/** /**
* An HTTP2 client that allows you to send HTTP2 frames to a server. Inbound and outbound frames are * An HTTP2 client that allows you to send HTTP2 frames to a server. Inbound and outbound frames are
@ -58,9 +59,7 @@ public class Http2Client {
if (config.isSsl()) { if (config.isSsl()) {
sslCtx = SslContext.newClientContext( sslCtx = SslContext.newClientContext(
null, InsecureTrustManagerFactory.INSTANCE, null, null, InsecureTrustManagerFactory.INSTANCE, null,
SslContext.newApplicationProtocolSelector( Arrays.asList(SelectedProtocol.HTTP_2.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
SelectedProtocol.HTTP_2.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0); 0, 0);
} else { } else {
sslCtx = null; sslCtx = null;

View File

@ -33,6 +33,7 @@ import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import static java.util.concurrent.TimeUnit.*; import static java.util.concurrent.TimeUnit.*;
@ -62,9 +63,7 @@ public class SpdyClient {
public SpdyClient(String host, int port) throws SSLException { public SpdyClient(String host, int port) throws SSLException {
sslCtx = SslContext.newClientContext( sslCtx = SslContext.newClientContext(
null, InsecureTrustManagerFactory.INSTANCE, null, null, InsecureTrustManagerFactory.INSTANCE, null,
SslContext.newApplicationProtocolSelector( Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
SelectedProtocol.SPDY_3_1.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0); 0, 0);
this.host = host; this.host = host;

View File

@ -1,34 +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;
/**
* Selects an application layer protocol in TLS <a href="http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04">NPN
* (Next Protocol Negotiation)</a> or <a href="https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-05">ALPN
* (Application Layer Protocol Negotiation)</a>.
*/
public interface ApplicationProtocolSelector {
/**
* Invoked to select a protocol from the list of specified application layer protocols.
*
* @param protocols the list of application layer protocols sent by the server.
* The list is empty if the server supports neither NPN nor ALPM.
* @return the selected protocol. {@code null} if no protocol was selected.
*/
String selectProtocol(List<String> protocols) throws Exception;
}

View File

@ -20,7 +20,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufInputStream;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
@ -30,6 +29,7 @@ import java.io.File;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -39,7 +39,7 @@ import java.util.List;
public final class JdkSslClientContext extends JdkSslContext { public final class JdkSslClientContext extends JdkSslContext {
private final SSLContext ctx; private final SSLContext ctx;
private final ApplicationProtocolSelector nextProtocolSelector; private final List<String> nextProtocols;
/** /**
* Creates a new instance. * Creates a new instance.
@ -92,9 +92,8 @@ public final class JdkSslClientContext extends JdkSslContext {
* {@code null} to use the default. * {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference. * @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites. * {@code null} to use the default cipher suites.
* @param nextProtocolSelector the {@link ApplicationProtocolSelector} that chooses one of the application layer * @param nextProtocols the application layer protocols to accept, in the order of preference.
* protocols returned by a TLS server. * {@code null} to disable TLS NPN/ALPN extension.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects. * @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
@ -102,16 +101,23 @@ public final class JdkSslClientContext extends JdkSslContext {
*/ */
public JdkSslClientContext( public JdkSslClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory, File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector, Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers); super(ciphers);
if (nextProtocolSelector != null && !JettyNpnSslEngine.isAvailable()) { if (nextProtocols != null && nextProtocols.iterator().hasNext() && !JettyNpnSslEngine.isAvailable()) {
throw new SSLException("NPN/ALPN unsupported: " + nextProtocolSelector); throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
} }
this.nextProtocolSelector = nextProtocolSelector; List<String> nextProtoList = new ArrayList<String>();
for (String p: nextProtocols) {
if (p == null) {
break;
}
nextProtoList.add(p);
}
this.nextProtocols = Collections.unmodifiableList(nextProtoList);
try { try {
if (certChainFile == null) { if (certChainFile == null) {
@ -168,27 +174,13 @@ public final class JdkSslClientContext extends JdkSslContext {
return true; return true;
} }
@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return nextProtocolSelector;
}
@Override @Override
public List<String> nextProtocols() { public List<String> nextProtocols() {
return Collections.emptyList(); return nextProtocols;
} }
@Override @Override
public SSLContext context() { public SSLContext context() {
return ctx; return ctx;
} }
@Override
SSLEngine wrapEngine(SSLEngine engine) {
if (nextProtocolSelector == null) {
return engine;
} else {
return new JettyNpnSslEngine(engine, nextProtocolSelector);
}
}
} }

View File

@ -165,7 +165,13 @@ public abstract class JdkSslContext extends SslContext {
return wrapEngine(engine); return wrapEngine(engine);
} }
abstract SSLEngine wrapEngine(SSLEngine engine); private SSLEngine wrapEngine(SSLEngine engine) {
if (nextProtocols().isEmpty()) {
return engine;
} else {
return new JettyNpnSslEngine(engine, nextProtocols(), isServer());
}
}
private static String[] toCipherSuiteArray(Iterable<String> ciphers) { private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
if (ciphers == null) { if (ciphers == null) {

View File

@ -21,7 +21,6 @@ import io.netty.buffer.ByteBufInputStream;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSessionContext;
import java.io.File; import java.io.File;
@ -182,11 +181,6 @@ public final class JdkSslServerContext extends JdkSslContext {
return false; return false;
} }
@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return null;
}
@Override @Override
public List<String> nextProtocols() { public List<String> nextProtocols() {
return nextProtocols; return nextProtocols;
@ -196,13 +190,4 @@ public final class JdkSslServerContext extends JdkSslContext {
public SSLContext context() { public SSLContext context() {
return ctx; return ctx;
} }
@Override
SSLEngine wrapEngine(SSLEngine engine) {
if (nextProtocols.isEmpty()) {
return engine;
} else {
return new JettyNpnSslEngine(engine, nextProtocols);
}
}
} }

View File

@ -16,8 +16,6 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.eclipse.jetty.npn.NextProtoNego; import org.eclipse.jetty.npn.NextProtoNego;
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider; import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider; import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
@ -33,8 +31,6 @@ import java.util.List;
final class JettyNpnSslEngine extends SSLEngine { final class JettyNpnSslEngine extends SSLEngine {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(JettyNpnSslEngine.class);
private static boolean available; private static boolean available;
static boolean isAvailable() { static boolean isAvailable() {
@ -64,57 +60,55 @@ final class JettyNpnSslEngine extends SSLEngine {
private final SSLEngine engine; private final SSLEngine engine;
private final JettyNpnSslSession session; private final JettyNpnSslSession session;
JettyNpnSslEngine(SSLEngine engine, final List<String> nextProtocols) { JettyNpnSslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
assert !nextProtocols.isEmpty(); assert !nextProtocols.isEmpty();
this.engine = engine; this.engine = engine;
session = new JettyNpnSslSession(engine); session = new JettyNpnSslSession(engine);
NextProtoNego.put(engine, new ServerProvider() { if (server) {
@Override NextProtoNego.put(engine, new ServerProvider() {
public void unsupported() { @Override
getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1)); public void unsupported() {
} getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1));
@Override
public List<String> protocols() {
return nextProtocols;
}
@Override
public void protocolSelected(String protocol) {
getSession().setApplicationProtocol(protocol);
}
});
}
JettyNpnSslEngine(SSLEngine engine, final ApplicationProtocolSelector nextProtocolSelector) {
this.engine = engine;
session = new JettyNpnSslSession(engine);
NextProtoNego.put(engine, new ClientProvider() {
@Override
public boolean supports() {
return true;
}
@Override
public void unsupported() {
session.setApplicationProtocol(null);
}
@Override
public String selectProtocol(List<String> protocols) {
String p = null;
try {
p = nextProtocolSelector.selectProtocol(protocols);
} catch (Exception e) {
logger.warn("Failed to select the next protocol:", e);
} }
session.setApplicationProtocol(p);
return p; @Override
} public List<String> protocols() {
}); return nextProtocols;
}
@Override
public void protocolSelected(String protocol) {
getSession().setApplicationProtocol(protocol);
}
});
} else {
final String[] list = nextProtocols.toArray(new String[nextProtocols.size()]);
final String fallback = list[list.length - 1];
NextProtoNego.put(engine, new ClientProvider() {
@Override
public boolean supports() {
return true;
}
@Override
public void unsupported() {
session.setApplicationProtocol(null);
}
@Override
public String selectProtocol(List<String> protocols) {
for (String p: list) {
if (protocols.contains(p)) {
return p;
}
}
return fallback;
}
});
}
} }
@Override @Override

View File

@ -65,8 +65,7 @@ public final class OpenSslServerContext extends SslContext {
private final List<String> unmodifiableCiphers = Collections.unmodifiableList(ciphers); private final List<String> unmodifiableCiphers = Collections.unmodifiableList(ciphers);
private final long sessionCacheSize; private final long sessionCacheSize;
private final long sessionTimeout; private final long sessionTimeout;
private final List<String> nextProtocols = new ArrayList<String>(); private final List<String> nextProtocols;
private final List<String> unmodifiableNextProtocols = Collections.unmodifiableList(nextProtocols);
/** The OpenSSL SSL_CTX object */ /** The OpenSSL SSL_CTX object */
private final long ctx; private final long ctx;
@ -147,12 +146,14 @@ public final class OpenSslServerContext extends SslContext {
this.ciphers.add(c); this.ciphers.add(c);
} }
List<String> nextProtoList = new ArrayList<String>();
for (String p: nextProtocols) { for (String p: nextProtocols) {
if (p == null) { if (p == null) {
break; break;
} }
this.nextProtocols.add(p); nextProtoList.add(p);
} }
this.nextProtocols = Collections.unmodifiableList(nextProtoList);
// Allocate a new APR pool. // Allocate a new APR pool.
aprPool = Pool.create(0); aprPool = Pool.create(0);
@ -217,10 +218,10 @@ public final class OpenSslServerContext extends SslContext {
} }
/* Set next protocols for next protocol negotiation extension, if specified */ /* Set next protocols for next protocol negotiation extension, if specified */
if (!this.nextProtocols.isEmpty()) { if (!nextProtoList.isEmpty()) {
// Convert the protocol list into a comma-separated string. // Convert the protocol list into a comma-separated string.
StringBuilder nextProtocolBuf = new StringBuilder(); StringBuilder nextProtocolBuf = new StringBuilder();
for (String p: this.nextProtocols) { for (String p: nextProtoList) {
nextProtocolBuf.append(p); nextProtocolBuf.append(p);
nextProtocolBuf.append(','); nextProtocolBuf.append(',');
} }
@ -281,14 +282,9 @@ public final class OpenSslServerContext extends SslContext {
return sessionTimeout; return sessionTimeout;
} }
@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return null;
}
@Override @Override
public List<String> nextProtocols() { public List<String> nextProtocols() {
return unmodifiableNextProtocols; return nextProtocols;
} }
/** /**
@ -310,10 +306,10 @@ public final class OpenSslServerContext extends SslContext {
*/ */
@Override @Override
public SSLEngine newEngine(ByteBufAllocator alloc) { public SSLEngine newEngine(ByteBufAllocator alloc) {
if (unmodifiableNextProtocols.isEmpty()) { if (nextProtocols.isEmpty()) {
return new OpenSslEngine(ctx, alloc, null); return new OpenSslEngine(ctx, alloc, null);
} else { } else {
return new OpenSslEngine(ctx, alloc, unmodifiableNextProtocols.get(unmodifiableNextProtocols.size() - 1)); return new OpenSslEngine(ctx, alloc, nextProtocols.get(nextProtocols.size() - 1));
} }
} }

View File

@ -26,7 +26,6 @@ import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -258,9 +257,8 @@ public abstract class SslContext {
* {@code null} to use the default. * {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference. * @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites. * {@code null} to use the default cipher suites.
* @param nextProtocolSelector the {@link ApplicationProtocolSelector} that chooses one of the application layer * @param nextProtocols the application layer protocols to accept, in the order of preference.
* protocols returned by a TLS server. * {@code null} to disable TLS NPN/ALPN extension.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects. * @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
@ -270,11 +268,11 @@ public abstract class SslContext {
*/ */
public static SslContext newClientContext( public static SslContext newClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory, File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector, Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContext( return newClientContext(
null, certChainFile, trustManagerFactory, null, certChainFile, trustManagerFactory,
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout); ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
} }
/** /**
@ -349,9 +347,8 @@ public abstract class SslContext {
* {@code null} to use the default. * {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference. * @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites. * {@code null} to use the default cipher suites.
* @param nextProtocolSelector the {@link ApplicationProtocolSelector} that chooses one of the application layer * @param nextProtocols the application layer protocols to accept, in the order of preference.
* protocols returned by a TLS server. * {@code null} to disable TLS NPN/ALPN extension.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects. * @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
@ -362,7 +359,7 @@ public abstract class SslContext {
public static SslContext newClientContext( public static SslContext newClientContext(
SslProvider provider, SslProvider provider,
File certChainFile, TrustManagerFactory trustManagerFactory, File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector, Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
if (provider != null && provider != SslProvider.JDK) { if (provider != null && provider != SslProvider.JDK) {
@ -371,84 +368,7 @@ public abstract class SslContext {
return new JdkSslClientContext( return new JdkSslClientContext(
certChainFile, trustManagerFactory, certChainFile, trustManagerFactory,
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout); ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
}
/**
* Creates a simple client-side {@link ApplicationProtocolSelector} that selects the most preferred protocol
* among the application protocols sent by the server. If there is no match, it chooses the least preferred one.
*
* @param nextProtocols the list of the supported client-side application protocols, in the order of preference
* @return the new {@link ApplicationProtocolSelector}.
* {@code null} if the specified {@code nextProtocols} does not contain any elements.
*
*/
public static ApplicationProtocolSelector newApplicationProtocolSelector(String... nextProtocols) {
if (nextProtocols == null) {
throw new NullPointerException("nextProtocols");
}
final List<String> list = new ArrayList<String>();
for (String p: nextProtocols) {
if (p == null) {
break;
}
list.add(p);
}
if (list.isEmpty()) {
return null;
}
return newApplicationProtocolSelector(list);
}
private static ApplicationProtocolSelector newApplicationProtocolSelector(final List<String> list) {
return new ApplicationProtocolSelector() {
@Override
public String selectProtocol(List<String> protocols) throws Exception {
for (String p: list) {
if (protocols.contains(p)) {
return p;
}
}
return list.get(list.size() - 1);
}
@Override
public String toString() {
return "ApplicationProtocolSelector(" + list + ')';
}
};
}
/**
* Creates a simple client-side {@link ApplicationProtocolSelector} that selects the most preferred protocol
* among the application protocols sent by the server. If there is no match, it chooses the least preferred one.
*
* @param nextProtocols the list of the supported client-side application protocols, in the order of preference
* @return the new {@link ApplicationProtocolSelector}.
* {@code null} if the specified {@code nextProtocols} does not contain any elements.
*
*/
public static ApplicationProtocolSelector newApplicationProtocolSelector(Iterable<String> nextProtocols) {
if (nextProtocols == null) {
throw new NullPointerException("nextProtocols");
}
final List<String> list = new ArrayList<String>();
for (String p: nextProtocols) {
if (p == null) {
break;
}
list.add(p);
}
if (list.isEmpty()) {
return null;
}
return newApplicationProtocolSelector(list);
} }
SslContext() { } SslContext() { }
@ -481,18 +401,9 @@ public abstract class SslContext {
public abstract long sessionTimeout(); public abstract long sessionTimeout();
/** /**
* Returns the client-side {@link ApplicationProtocolSelector} for the TLS NPN/ALPN extension. * Returns the list of application layer protocols for the TLS NPN/ALPN extension, in the order of preference.
* *
* @return the client-side {@link ApplicationProtocolSelector}. * @return the list of application layer protocols.
* {@code null} if NPN/ALPN extension has been disabled.
*/
public abstract ApplicationProtocolSelector nextProtocolSelector();
/**
* Returns the list of server-side application layer protocols for the TLS NPN/ALPN extension,
* in the order of preference.
*
* @return the list of server-side application layer protocols.
* {@code null} if NPN/ALPN extension has been disabled. * {@code null} if NPN/ALPN extension has been disabled.
*/ */
public abstract List<String> nextProtocols(); public abstract List<String> nextProtocols();