diff --git a/AUTH-README.md b/AUTH-README.md
index 05613d1f02..2ee7328f61 100644
--- a/AUTH-README.md
+++ b/AUTH-README.md
@@ -20,6 +20,7 @@ Note that you must use the release of the Jetty-ALPN jar specific to the version
An option is provided to use GRPC over plaintext without TLS. This is convenient for testing environments, however users must be aware of the secuirty risks of doing so for real production systems.
+### TLS-ALPN on Android
On Android, it is needed to update your security provider to enable ALPN support, especially for Android versions < 5.0. If the provider fails to update, ALPN may not work.
After the update is done, you'll need to pass an SSLSocketFactorty to OkHttpChannelBuilder, like the code snippet below shows.
@@ -29,6 +30,23 @@ OkHttpChannelBuilder channelBuilder = OkHttpChannelBuilder.forAddress(host, port
.sslSocketFactory(SSLContext.getDefault().getSocketFactory());
```
+### TLS-ALPN in Jetty
+Some web containers, such as Jetty restrict access to server classes for web applications. A gRPC client running within such a container must be properly configured to allow access to the ALPN classes.
+
+In Jetty, this is done by including a `WEB-INF/jetty-env.xml` file containing the following:
+
+```xml
+
+
+
+
+
+
+
+ -org.eclipse.jetty.alpn.
+
+
+```
# Using OAuth2
diff --git a/netty/src/main/java/io/grpc/transport/netty/GrpcSslContexts.java b/netty/src/main/java/io/grpc/transport/netty/GrpcSslContexts.java
index 874c6526f5..4adc0a1c74 100644
--- a/netty/src/main/java/io/grpc/transport/netty/GrpcSslContexts.java
+++ b/netty/src/main/java/io/grpc/transport/netty/GrpcSslContexts.java
@@ -32,6 +32,10 @@
package io.grpc.transport.netty;
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.SupportedCipherSuiteFilter;
@@ -43,6 +47,16 @@ 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",
+ "h2-17",
+ "h2-16",
+ "h2-15",
+ "h2-14");
+
/**
* Creates a SslContextBuilder with ciphers and APN appropriate for gRPC.
*
@@ -80,7 +94,6 @@ public class GrpcSslContexts {
*/
public static SslContextBuilder configure(SslContextBuilder builder) {
return builder.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
- // We currently handle ALPN ourselves, so we require ALPN in Netty disabled.
- .applicationProtocolConfig(null);
+ .applicationProtocolConfig(DEFAULT_APN);
}
}
diff --git a/netty/src/main/java/io/grpc/transport/netty/ProtocolNegotiators.java b/netty/src/main/java/io/grpc/transport/netty/ProtocolNegotiators.java
index 3ec2405aeb..ea45e99911 100644
--- a/netty/src/main/java/io/grpc/transport/netty/ProtocolNegotiators.java
+++ b/netty/src/main/java/io/grpc/transport/netty/ProtocolNegotiators.java
@@ -32,7 +32,6 @@
package io.grpc.transport.netty;
import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.SettableFuture;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
@@ -55,17 +54,12 @@ import io.netty.util.ByteString;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
@@ -74,20 +68,6 @@ import javax.net.ssl.SSLParameters;
* Common {@link ProtocolNegotiator}s used by gRPC.
*/
public final class ProtocolNegotiators {
- private static final Logger log = Logger.getLogger(ProtocolNegotiators.class.getName());
-
- // TODO(madongfly): Remove "h2-xx" at a right time.
- private static final List SUPPORTED_PROTOCOLS = Collections.unmodifiableList(
- Arrays.asList(
- "h2",
- Http2OrHttpChooser.SelectedProtocol.HTTP_2.protocolName(),
- "h2-14",
- "h2-15",
- "h2-16"));
-
- // Prefer ALPN to NPN so try it first.
- private static final String[] JETTY_TLS_NEGOTIATION_IMPL =
- {"org.eclipse.jetty.alpn.ALPN", "org.eclipse.jetty.npn.NextProtoNego"};
private ProtocolNegotiators() {
}
@@ -97,12 +77,6 @@ public final class ProtocolNegotiators {
*/
public static ChannelHandler serverTls(SSLEngine sslEngine) {
Preconditions.checkNotNull(sslEngine, "sslEngine");
- if (!isOpenSsl(sslEngine.getClass())) {
- // Using JDK SSL
- if (!installJettyTlsProtocolSelection(sslEngine, SettableFuture.create(), true)) {
- throw new IllegalStateException("NPN/ALPN extensions not installed");
- }
- }
return new SslHandler(sslEngine, false);
}
@@ -126,27 +100,13 @@ public final class ProtocolNegotiators {
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
sslEngine.setSSLParameters(sslParams);
- final SettableFuture completeFuture = SettableFuture.create();
- if (isOpenSsl(sslContext.getClass())) {
- completeFuture.set(null);
- } else {
- // Using JDK SSL
- if (!installJettyTlsProtocolSelection(sslEngine, completeFuture, false)) {
- throw new IllegalStateException("NPN/ALPN extensions not installed");
- }
- }
-
SslHandler sslHandler = new SslHandler(sslEngine, false);
sslHandler.handshakeFuture().addListener(
new GenericFutureListener>() {
@Override
public void operationComplete(Future super Channel> future) throws Exception {
// If an error occurred during the handshake, throw it to the pipeline.
- if (future.isSuccess()) {
- completeFuture.get();
- } else {
- future.get();
- }
+ future.get();
}
});
ctx.pipeline().replace(this, "sslHandler", sslHandler);
@@ -190,13 +150,6 @@ public final class ProtocolNegotiators {
};
}
- /**
- * Returns {@code true} if the given class is for use with Netty OpenSsl.
- */
- private static boolean isOpenSsl(Class> clazz) {
- return clazz.getSimpleName().toLowerCase().contains("openssl");
- }
-
/**
* Buffers all writes until either {@link #writeBufferedAndRemove(ChannelHandlerContext)} or
* {@link #failBufferedAndClose(ChannelHandlerContext)} is called. This handler allows us to
@@ -419,102 +372,4 @@ public final class ProtocolNegotiators {
super.userEventTriggered(ctx, evt);
}
}
-
- /**
- * Find Jetty's TLS NPN/ALPN extensions and attempt to use them
- *
- * @return true if NPN/ALPN support is available.
- */
- private static boolean installJettyTlsProtocolSelection(final SSLEngine engine,
- final SettableFuture protocolNegotiated, boolean server) {
- for (String protocolNegoClassName : JETTY_TLS_NEGOTIATION_IMPL) {
- try {
- Class> negoClass;
- try {
- negoClass = Class.forName(protocolNegoClassName, true, null);
- } catch (ClassNotFoundException ignored) {
- // Not on the classpath.
- log.warning("Jetty extension " + protocolNegoClassName + " not found");
- continue;
- }
- Class> providerClass = Class.forName(protocolNegoClassName + "$Provider", true, null);
- Class> clientProviderClass
- = Class.forName(protocolNegoClassName + "$ClientProvider", true, null);
- Class> serverProviderClass
- = Class.forName(protocolNegoClassName + "$ServerProvider", true, null);
- Method putMethod = negoClass.getMethod("put", SSLEngine.class, providerClass);
- final Method removeMethod = negoClass.getMethod("remove", SSLEngine.class);
- putMethod.invoke(null, engine, Proxy.newProxyInstance(
- null,
- new Class[] {server ? serverProviderClass : clientProviderClass},
- new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- String methodName = method.getName();
- if ("supports".equals(methodName)) {
- // NPN client
- return true;
- }
- if ("unsupported".equals(methodName)) {
- // all
- removeMethod.invoke(null, engine);
- protocolNegotiated.setException(new RuntimeException(
- "Endpoint does not support any of " + SUPPORTED_PROTOCOLS
- + " in ALPN/NPN negotiation"));
- return null;
- }
- if ("protocols".equals(methodName)) {
- // ALPN client, NPN server
- return SUPPORTED_PROTOCOLS;
- }
- if ("selected".equals(methodName) || "protocolSelected".equals(methodName)) {
- // ALPN client, NPN server
- removeMethod.invoke(null, engine);
- String protocol = (String) args[0];
- if (!SUPPORTED_PROTOCOLS.contains(protocol)) {
- RuntimeException e = new RuntimeException(
- "Unsupported protocol selected via ALPN/NPN: " + protocol);
- protocolNegotiated.setException(e);
- if ("selected".equals(methodName)) {
- // ALPN client
- // Throwing exception causes TLS alert.
- throw e;
- } else {
- return null;
- }
- }
- protocolNegotiated.set(null);
- return null;
- }
- if ("select".equals(methodName) || "selectProtocol".equals(methodName)) {
- // ALPN server, NPN client
- removeMethod.invoke(null, engine);
- @SuppressWarnings("unchecked")
- List names = (List) args[0];
- for (String name : names) {
- if (SUPPORTED_PROTOCOLS.contains(name)) {
- protocolNegotiated.set(null);
- return name;
- }
- }
- RuntimeException e =
- new RuntimeException("Protocol not available via ALPN/NPN: " + names);
- protocolNegotiated.setException(e);
- if ("select".equals(methodName)) {
- // ALPN server
- throw e; // Throwing exception causes TLS alert.
- }
- return null;
- }
- throw new IllegalStateException("Unknown method " + methodName);
- }
- }));
- return true;
- } catch (Exception e) {
- log.log(Level.SEVERE,
- "Unable to initialize protocol negotation for " + protocolNegoClassName, e);
- }
- }
- return false;
- }
}