diff --git a/interop-testing/src/test/java/io/grpc/ChannelAndServerBuilderTest.java b/interop-testing/src/test/java/io/grpc/ChannelAndServerBuilderTest.java index ac830b9848..15c91d1d13 100644 --- a/interop-testing/src/test/java/io/grpc/ChannelAndServerBuilderTest.java +++ b/interop-testing/src/test/java/io/grpc/ChannelAndServerBuilderTest.java @@ -36,6 +36,8 @@ import org.junit.runners.Parameterized.Parameters; /** * Tests that Channel and Server builders properly hide the static constructors. + * + *

This test does nothing on Java 9. */ @RunWith(Parameterized.class) public class ChannelAndServerBuilderTest { @@ -49,13 +51,19 @@ public class ChannelAndServerBuilderTest { @Parameters(name = "class={0}") public static Collection params() throws Exception { ClassLoader loader = ChannelAndServerBuilderTest.class.getClassLoader(); + Collection classInfos = + ClassPath.from(loader).getTopLevelClassesRecursive("io.grpc"); + // Java 9 doesn't expose the URLClassLoader, which breaks searching through the classpath + if (classInfos.isEmpty()) { + return new ArrayList(); + } List classes = new ArrayList(); - for (ClassInfo classInfo : ClassPath.from(loader).getTopLevelClassesRecursive("io.grpc")) { + for (ClassInfo classInfo : classInfos) { Class clazz = Class.forName(classInfo.getName(), false /*initialize*/, loader); if (ServerBuilder.class.isAssignableFrom(clazz) && clazz != ServerBuilder.class) { classes.add(new Object[]{clazz}); } else if (ManagedChannelBuilder.class.isAssignableFrom(clazz) - && clazz != ManagedChannelBuilder.class ) { + && clazz != ManagedChannelBuilder.class) { classes.add(new Object[]{clazz}); } } diff --git a/okhttp/src/test/java/io/grpc/okhttp/OkHttpProtocolNegotiatorTest.java b/okhttp/src/test/java/io/grpc/okhttp/OkHttpProtocolNegotiatorTest.java index 2ac31ea51f..4e836eec54 100644 --- a/okhttp/src/test/java/io/grpc/okhttp/OkHttpProtocolNegotiatorTest.java +++ b/okhttp/src/test/java/io/grpc/okhttp/OkHttpProtocolNegotiatorTest.java @@ -19,6 +19,7 @@ package io.grpc.okhttp; import static com.google.common.base.Charsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -32,6 +33,7 @@ import io.grpc.okhttp.internal.Platform.TlsExtensionType; import io.grpc.okhttp.internal.Protocol; import java.io.IOException; import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.junit.Rule; @@ -110,7 +112,9 @@ public class OkHttpProtocolNegotiatorTest { @Test public void negotiate_handshakeFails() throws IOException { + SSLParameters parameters = new SSLParameters(); OkHttpProtocolNegotiator negotiator = OkHttpProtocolNegotiator.get(); + doReturn(parameters).when(sock).getSSLParameters(); doThrow(new IOException()).when(sock).startHandshake(); thrown.expect(IOException.class); diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java b/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java index fb040d01a4..d6f88ba0e9 100644 --- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java +++ b/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java @@ -29,7 +29,11 @@ import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; +import java.security.AccessController; +import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.security.Provider; import java.security.Security; import java.util.ArrayList; @@ -37,6 +41,8 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import okio.Buffer; @@ -44,19 +50,25 @@ import okio.Buffer; * Access to platform-specific features. * *

Server name indication (SNI)

+ * * Supported on Android 2.3+. * *

Session Tickets

+ * * Supported on Android 2.3+. * *

Android Traffic Stats (Socket Tagging)

+ * * Supported on Android 4.0+. * *

ALPN (Application Layer Protocol Negotiation)

+ * * Supported on Android 5.0+. The APIs were present in Android 4.4, but that implementation was * unstable. * - * Supported on OpenJDK 7 and 8 (via the JettyALPN-boot library). + *

Supported on OpenJDK 9+. + * + *

Supported on OpenJDK 7 and 8 (via the JettyALPN-boot library). */ public class Platform { public static final Logger logger = Logger.getLogger(Platform.class.getName()); @@ -199,6 +211,47 @@ public class Platform { throw new RuntimeException(nsae); } + // Find JDK9+ ALPN support + try { + // getApplicationProtocol() may throw UnsupportedOperationException, so first construct a + // dummy SSLEngine and verify the method does not throw. + SSLContext context = SSLContext.getInstance("TLS", sslProvider); + context.init(null, null, null); + SSLEngine engine = context.createSSLEngine(); + Method getEngineApplicationProtocol = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Method run() throws Exception { + return SSLEngine.class.getMethod("getApplicationProtocol"); + } + }); + getEngineApplicationProtocol.invoke(engine); + + Method setApplicationProtocols = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Method run() throws Exception { + return SSLParameters.class.getMethod("setApplicationProtocols", String[].class); + } + }); + Method getApplicationProtocol = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Method run() throws Exception { + return SSLSocket.class.getMethod("getApplicationProtocol"); + } + }); + return new JdkAlpnPlatform(sslProvider, setApplicationProtocols, getApplicationProtocol); + } catch (NoSuchAlgorithmException ignored) { + } catch (KeyManagementException ignored) { + } catch (PrivilegedActionException ignored) { + } catch (IllegalAccessException ignored) { + } catch (InvocationTargetException ignored) { + } + // Find Jetty's ALPN extension for OpenJDK. try { String negoClassName = "org.eclipse.jetty.alpn.ALPN"; @@ -375,6 +428,56 @@ public class Platform { } } + /** OpenJDK 9+. */ + private static class JdkAlpnPlatform extends Platform { + private final Method setApplicationProtocols; + private final Method getApplicationProtocol; + + private JdkAlpnPlatform( + Provider provider, Method setApplicationProtocols, Method getApplicationProtocol) { + super(provider); + this.setApplicationProtocols = setApplicationProtocols; + this.getApplicationProtocol = getApplicationProtocol; + } + + @Override + public TlsExtensionType getTlsExtensionType() { + return TlsExtensionType.ALPN_AND_NPN; + } + + @Override + public void configureTlsExtensions( + SSLSocket sslSocket, String hostname, List protocols) { + SSLParameters parameters = sslSocket.getSSLParameters(); + List names = new ArrayList(protocols.size()); + for (Protocol protocol : protocols) { + if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for ALPN. + names.add(protocol.toString()); + } + try { + setApplicationProtocols.invoke( + parameters, new Object[] {names.toArray(new String[names.size()])}); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + sslSocket.setSSLParameters(parameters); + } + + /** Returns the negotiated protocol, or null if no protocol was negotiated. */ + @Override + public String getSelectedProtocol(SSLSocket socket) { + try { + return (String) getApplicationProtocol.invoke(socket); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + } + /** * OpenJDK 7+ with {@code org.mortbay.jetty.alpn/alpn-boot} in the boot class path. */