mirror of https://github.com/grpc/grpc-java.git
Adding support for NPN fallback.
This takes some steps towards #525, but it won't be fixed until we officially support OpenSSL. This is due to the fact that ALPN->NPN fallback isn't supported with Jetty (since only one of the bootstrap plugins can be provided).
This commit is contained in:
parent
1b1c646ccd
commit
247ffb1377
|
|
@ -44,6 +44,7 @@ import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.STREAMING_R
|
|||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TESTCA;
|
||||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TLS;
|
||||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TRANSPORT;
|
||||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.USE_DEFAULT_CIPHERS;
|
||||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.WARMUP_DURATION;
|
||||
import static io.grpc.benchmarks.qps.Utils.HISTOGRAM_MAX_VALUE;
|
||||
import static io.grpc.benchmarks.qps.Utils.HISTOGRAM_PRECISION;
|
||||
|
|
@ -325,7 +326,7 @@ public class AsyncClient {
|
|||
public static void main(String... args) throws Exception {
|
||||
ClientConfiguration.Builder configBuilder = ClientConfiguration.newBuilder(
|
||||
ADDRESS, CHANNELS, OUTSTANDING_RPCS, CLIENT_PAYLOAD, SERVER_PAYLOAD,
|
||||
TLS, TESTCA, TRANSPORT, DURATION, WARMUP_DURATION, DIRECTEXECUTOR,
|
||||
TLS, TESTCA, USE_DEFAULT_CIPHERS, TRANSPORT, DURATION, WARMUP_DURATION, DIRECTEXECUTOR,
|
||||
SAVE_HISTOGRAM, STREAMING_RPCS, FLOW_CONTROL_WINDOW);
|
||||
ClientConfiguration config;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import io.netty.channel.ServerChannel;
|
|||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -107,10 +108,17 @@ public class AsyncServer {
|
|||
|
||||
File cert = TestUtils.loadCert("server1.pem");
|
||||
File key = TestUtils.loadCert("server1.key");
|
||||
boolean useJdkSsl = config.transport == ServerConfiguration.Transport.NETTY_NIO;
|
||||
sslContext = GrpcSslContexts.forServer(cert, key)
|
||||
.sslProvider(useJdkSsl ? SslProvider.JDK : SslProvider.OPENSSL)
|
||||
.build();
|
||||
SslContextBuilder sslContextBuilder = GrpcSslContexts.forServer(cert, key);
|
||||
if (config.transport == ServerConfiguration.Transport.NETTY_NIO) {
|
||||
sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder, SslProvider.JDK);
|
||||
} else {
|
||||
// Native transport with OpenSSL
|
||||
sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder, SslProvider.OPENSSL);
|
||||
}
|
||||
if (config.useDefaultCiphers) {
|
||||
sslContextBuilder.ciphers(null);
|
||||
}
|
||||
sslContext = sslContextBuilder.build();
|
||||
}
|
||||
|
||||
final EventLoopGroup boss;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ class ClientConfiguration implements Configuration {
|
|||
Transport transport = Transport.NETTY_NIO;
|
||||
boolean tls;
|
||||
boolean testca;
|
||||
boolean useDefaultCiphers;
|
||||
boolean directExecutor;
|
||||
SocketAddress address;
|
||||
int channels = 4;
|
||||
|
|
@ -233,6 +234,13 @@ class ClientConfiguration implements Configuration {
|
|||
config.testca = parseBoolean(value);
|
||||
}
|
||||
},
|
||||
USE_DEFAULT_CIPHERS("", "Use the default JDK ciphers for TLS (Used to support Java 7).",
|
||||
"" + DEFAULT.useDefaultCiphers) {
|
||||
@Override
|
||||
protected void setClientValue(ClientConfiguration config, String value) {
|
||||
config.useDefaultCiphers = parseBoolean(value);
|
||||
}
|
||||
},
|
||||
TRANSPORT("STR", Transport.getDescriptionString(), DEFAULT.transport.name().toLowerCase()) {
|
||||
@Override
|
||||
protected void setClientValue(ClientConfiguration config, String value) {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TARGET_QPS;
|
|||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TESTCA;
|
||||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TLS;
|
||||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TRANSPORT;
|
||||
import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.USE_DEFAULT_CIPHERS;
|
||||
import static io.grpc.benchmarks.qps.Utils.HISTOGRAM_MAX_VALUE;
|
||||
import static io.grpc.benchmarks.qps.Utils.HISTOGRAM_PRECISION;
|
||||
import static io.grpc.benchmarks.qps.Utils.newClientChannel;
|
||||
|
|
@ -85,7 +86,7 @@ public class OpenLoopClient {
|
|||
public static void main(String... args) throws Exception {
|
||||
ClientConfiguration.Builder configBuilder = ClientConfiguration.newBuilder(
|
||||
ADDRESS, TARGET_QPS, CLIENT_PAYLOAD, SERVER_PAYLOAD, TLS,
|
||||
TESTCA, TRANSPORT, DURATION, SAVE_HISTOGRAM, FLOW_CONTROL_WINDOW);
|
||||
TESTCA, USE_DEFAULT_CIPHERS, TRANSPORT, DURATION, SAVE_HISTOGRAM, FLOW_CONTROL_WINDOW);
|
||||
ClientConfiguration config;
|
||||
try {
|
||||
config = configBuilder.build(args);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class ServerConfiguration implements Configuration {
|
|||
|
||||
Transport transport = Transport.NETTY_NIO;
|
||||
boolean tls;
|
||||
boolean useDefaultCiphers;
|
||||
boolean directExecutor;
|
||||
SocketAddress address;
|
||||
int flowControlWindow = NettyChannelBuilder.DEFAULT_FLOW_CONTROL_WINDOW;
|
||||
|
|
@ -172,6 +173,13 @@ class ServerConfiguration implements Configuration {
|
|||
config.tls = parseBoolean(value);
|
||||
}
|
||||
},
|
||||
USE_DEFAULT_CIPHERS("", "Use the default JDK ciphers for TLS (Used to support Java 7).",
|
||||
"false") {
|
||||
@Override
|
||||
protected void setServerValue(ServerConfiguration config, String value) {
|
||||
config.useDefaultCiphers = parseBoolean(value);
|
||||
}
|
||||
},
|
||||
TRANSPORT("STR", Transport.getDescriptionString(), DEFAULT.transport.name().toLowerCase()) {
|
||||
@Override
|
||||
protected void setServerValue(ServerConfiguration config, String value) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
|||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
|
||||
import org.HdrHistogram.Histogram;
|
||||
|
|
@ -153,14 +154,21 @@ final class Utils {
|
|||
}
|
||||
|
||||
// It's a Netty transport.
|
||||
SslContext context = null;
|
||||
SslContext sslContext = null;
|
||||
NegotiationType negotiationType = config.tls ? NegotiationType.TLS : NegotiationType.PLAINTEXT;
|
||||
if (config.tls && config.testca) {
|
||||
File cert = TestUtils.loadCert("ca.pem");
|
||||
boolean useJdkSsl = config.transport == ClientConfiguration.Transport.NETTY_NIO;
|
||||
context = GrpcSslContexts.forClient().trustManager(cert)
|
||||
.sslProvider(useJdkSsl ? SslProvider.JDK : SslProvider.OPENSSL)
|
||||
.build();
|
||||
SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient().trustManager(cert);
|
||||
if (config.transport == ClientConfiguration.Transport.NETTY_NIO) {
|
||||
sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder, SslProvider.JDK);
|
||||
} else {
|
||||
// Native transport with OpenSSL
|
||||
sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder, SslProvider.OPENSSL);
|
||||
}
|
||||
if (config.useDefaultCiphers) {
|
||||
sslContextBuilder.ciphers(null);
|
||||
}
|
||||
sslContext = sslContextBuilder.build();
|
||||
}
|
||||
final EventLoopGroup group;
|
||||
final Class<? extends io.netty.channel.Channel> channelType;
|
||||
|
|
@ -192,7 +200,7 @@ final class Utils {
|
|||
.channelType(channelType)
|
||||
.negotiationType(negotiationType)
|
||||
.executor(config.directExecutor ? MoreExecutors.newDirectExecutorService() : null)
|
||||
.sslContext(context)
|
||||
.sslContext(sslContext)
|
||||
.flowControlWindow(config.flowControlWindow)
|
||||
.build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ subprojects {
|
|||
// on the Java version.
|
||||
def alpnboot_version = '8.1.2.v20141202'
|
||||
if (JavaVersion.current().ordinal() < JavaVersion.VERSION_1_8.ordinal()) {
|
||||
alpnboot_version = '7.1.2.v20141202'
|
||||
alpnboot_version = '7.1.3.v20150130'
|
||||
}
|
||||
|
||||
alpnboot_package_name = 'org.mortbay.jetty.alpn:alpn-boot:' + alpnboot_version
|
||||
|
|
|
|||
|
|
@ -31,12 +31,14 @@
|
|||
|
||||
package io.grpc.netty;
|
||||
|
||||
import io.grpc.internal.ExperimentalApi;
|
||||
import io.netty.handler.codec.http2.Http2SecurityUtil;
|
||||
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.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -47,11 +49,25 @@ import java.io.File;
|
|||
public class GrpcSslContexts {
|
||||
private GrpcSslContexts() {}
|
||||
|
||||
private static ApplicationProtocolConfig DEFAULT_APN = new ApplicationProtocolConfig(
|
||||
Protocol.ALPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
"h2");
|
||||
private static String[] HTTP2_VERSIONS = {"h2"};
|
||||
|
||||
private static ApplicationProtocolConfig ALPN = new ApplicationProtocolConfig(
|
||||
Protocol.ALPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
HTTP2_VERSIONS);
|
||||
|
||||
private static ApplicationProtocolConfig NPN = new ApplicationProtocolConfig(
|
||||
Protocol.NPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
HTTP2_VERSIONS);
|
||||
|
||||
private static ApplicationProtocolConfig NPN_AND_ALPN = new ApplicationProtocolConfig(
|
||||
Protocol.NPN_AND_ALPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
HTTP2_VERSIONS);
|
||||
|
||||
/**
|
||||
* Creates a SslContextBuilder with ciphers and APN appropriate for gRPC.
|
||||
|
|
@ -89,7 +105,59 @@ public class GrpcSslContexts {
|
|||
* an application requires particular settings it should override the options set here.
|
||||
*/
|
||||
public static SslContextBuilder configure(SslContextBuilder builder) {
|
||||
return builder.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
||||
.applicationProtocolConfig(DEFAULT_APN);
|
||||
return configure(builder, defaultSslProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ciphers and APN appropriate for gRPC. Precisely what is set is permitted to change, so if
|
||||
* an application requires particular settings it should override the options set here.
|
||||
*/
|
||||
@ExperimentalApi
|
||||
public static SslContextBuilder configure(SslContextBuilder builder, SslProvider provider) {
|
||||
return builder.sslProvider(provider)
|
||||
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
||||
.applicationProtocolConfig(selectApplicationProtocolConfig(provider));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns OpenSSL if available, otherwise returns the JDK provider.
|
||||
*/
|
||||
private static SslProvider defaultSslProvider() {
|
||||
return SslProvider.JDK;
|
||||
// TODO(nmittler): use this once we support OpenSSL.
|
||||
// return OpenSsl.isAvailable() ? SslProvider.OPENSSL : SslProvider.JDK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to select the best {@link ApplicationProtocolConfig} for the given
|
||||
* {@link SslProvider}.
|
||||
*/
|
||||
private static ApplicationProtocolConfig selectApplicationProtocolConfig(SslProvider provider) {
|
||||
switch (provider) {
|
||||
case JDK: {
|
||||
if (JettyTlsUtil.isJettyAlpnConfigured()) {
|
||||
return ALPN;
|
||||
}
|
||||
if (JettyTlsUtil.isJettyNpnConfigured()) {
|
||||
return NPN;
|
||||
}
|
||||
throw new IllegalArgumentException("Jetty ALPN/NPN has not been properly configured.");
|
||||
}
|
||||
case OPENSSL: {
|
||||
throw new IllegalArgumentException("OpenSSL is not currently supported.");
|
||||
// TODO(nmittler): use this once we support OpenSSL.
|
||||
/*if (!OpenSsl.isAvailable()) {
|
||||
throw new IllegalArgumentException("OpenSSL is not installed on the system.");
|
||||
}
|
||||
|
||||
if (OpenSsl.isAlpnSupported()) {
|
||||
return NPN_AND_ALPN;
|
||||
} else {
|
||||
return NPN;
|
||||
}*/
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported provider: " + provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,31 +32,33 @@
|
|||
package io.grpc.netty;
|
||||
|
||||
/**
|
||||
* Utility class that verifies that Jetty ALPN is properly configured for the system.
|
||||
* Utility class for determining support for Jetty TLS ALPN/NPN.
|
||||
*/
|
||||
final class JettyAlpnVerifier {
|
||||
private JettyAlpnVerifier() {
|
||||
final class JettyTlsUtil {
|
||||
private JettyTlsUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when Jetty ALPN was not found in the boot classloader.
|
||||
* Indicates whether or not the Jetty ALPN jar is installed in the boot classloader.
|
||||
*/
|
||||
static final class NotFoundException extends Exception {
|
||||
public NotFoundException(Throwable cause) {
|
||||
super("Jetty ALPN not found in boot classloader.", cause);
|
||||
static boolean isJettyAlpnConfigured() {
|
||||
try {
|
||||
Class.forName("org.eclipse.jetty.alpn.ALPN", true, null);
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that Jetty ALPN is configured properly on this system.
|
||||
* @throws NotFoundException thrown if Jetty ALPN is missing from the boot classloader.
|
||||
* Indicates whether or not the Jetty NPN jar is installed in the boot classloader.
|
||||
*/
|
||||
static void verifyJettyAlpn() throws NotFoundException {
|
||||
static boolean isJettyNpnConfigured() {
|
||||
try {
|
||||
// Check the boot classloader for the ALPN class.
|
||||
Class.forName("org.eclipse.jetty.alpn.ALPN", true, null);
|
||||
Class.forName("org.eclipse.jetty.npn.NextProtoNego", true, null);
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new NotFoundException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,6 @@ import io.netty.handler.codec.http.HttpVersion;
|
|||
import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionHandler;
|
||||
import io.netty.handler.ssl.OpenSslContext;
|
||||
import io.netty.handler.ssl.OpenSslEngine;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
|
||||
|
|
@ -74,15 +73,6 @@ public final class ProtocolNegotiators {
|
|||
public static ChannelHandler serverTls(SSLEngine sslEngine) {
|
||||
Preconditions.checkNotNull(sslEngine, "sslEngine");
|
||||
|
||||
// If we're using Jetty ALPN, verify that it is configured properly.
|
||||
if (!(sslEngine instanceof OpenSslEngine)) {
|
||||
try {
|
||||
JettyAlpnVerifier.verifyJettyAlpn();
|
||||
} catch (JettyAlpnVerifier.NotFoundException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return new SslHandler(sslEngine, false);
|
||||
}
|
||||
|
||||
|
|
@ -96,24 +86,23 @@ public final class ProtocolNegotiators {
|
|||
Preconditions.checkNotNull(sslContext, "sslContext");
|
||||
Preconditions.checkNotNull(inetAddress, "inetAddress");
|
||||
|
||||
// If we're using Jetty ALPN, verify that it is configured properly.
|
||||
if (!(sslContext instanceof OpenSslContext)) {
|
||||
try {
|
||||
JettyAlpnVerifier.verifyJettyAlpn();
|
||||
} catch (JettyAlpnVerifier.NotFoundException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return new ProtocolNegotiator() {
|
||||
@Override
|
||||
public Handler newHandler(Http2ConnectionHandler handler) {
|
||||
ChannelHandler sslBootstrap = new ChannelHandlerAdapter() {
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
// TODO(nmittler): Unsupported for OpenSSL in Netty < 4.1.Beta6.
|
||||
SSLEngine sslEngine = sslContext.newEngine(ctx.alloc(),
|
||||
inetAddress.getHostName(), inetAddress.getPort());
|
||||
final SSLEngine sslEngine;
|
||||
if (sslContext instanceof OpenSslContext) {
|
||||
// TODO(nmittler): Unsupported for OpenSSL in Netty < 4.1.Beta6.
|
||||
// Until we upgrade Netty, uncomment the line below when testing with OpenSSL.
|
||||
//sslEngine = sslContext.newEngine(ctx.alloc());
|
||||
sslEngine = sslContext.newEngine(ctx.alloc(),
|
||||
inetAddress.getHostName(), inetAddress.getPort());
|
||||
} else {
|
||||
sslEngine = sslContext.newEngine(ctx.alloc(),
|
||||
inetAddress.getHostName(), inetAddress.getPort());
|
||||
}
|
||||
SSLParameters sslParams = new SSLParameters();
|
||||
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
sslEngine.setSSLParameters(sslParams);
|
||||
|
|
|
|||
Loading…
Reference in New Issue