diff options
author | Eric Gribkoff <ericgribkoff@google.com> | 2018-03-20 17:29:24 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-20 17:29:24 -0700 |
commit | 52fedb624dfee7b6950eb379bed78b6c2e7ad309 (patch) | |
tree | ea81247644ea429fdc79414f9b0e337bed9919ec /okhttp | |
parent | 524117247530b847157b72d58a25372b87100e2a (diff) | |
download | grpc-grpc-java-52fedb624dfee7b6950eb379bed78b6c2e7ad309.tar.gz |
okhttp: support JDK9 ALPN (#4136)
Diffstat (limited to 'okhttp')
-rw-r--r-- | okhttp/src/test/java/io/grpc/okhttp/OkHttpProtocolNegotiatorTest.java | 4 | ||||
-rw-r--r-- | okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java | 105 |
2 files changed, 108 insertions, 1 deletions
diff --git a/okhttp/src/test/java/io/grpc/okhttp/OkHttpProtocolNegotiatorTest.java b/okhttp/src/test/java/io/grpc/okhttp/OkHttpProtocolNegotiatorTest.java index 2ac31ea51..4e836eec5 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 fb040d01a..d6f88ba0e 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. * * <h3>Server name indication (SNI)</h3> + * * Supported on Android 2.3+. * * <h3>Session Tickets</h3> + * * Supported on Android 2.3+. * * <h3>Android Traffic Stats (Socket Tagging)</h3> + * * Supported on Android 4.0+. * * <h3>ALPN (Application Layer Protocol Negotiation)</h3> + * * 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). + * <p>Supported on OpenJDK 9+. + * + * <p>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<Method>() { + @Override + public Method run() throws Exception { + return SSLEngine.class.getMethod("getApplicationProtocol"); + } + }); + getEngineApplicationProtocol.invoke(engine); + + Method setApplicationProtocols = + AccessController.doPrivileged( + new PrivilegedExceptionAction<Method>() { + @Override + public Method run() throws Exception { + return SSLParameters.class.getMethod("setApplicationProtocols", String[].class); + } + }); + Method getApplicationProtocol = + AccessController.doPrivileged( + new PrivilegedExceptionAction<Method>() { + @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<Protocol> protocols) { + SSLParameters parameters = sslSocket.getSSLParameters(); + List<String> names = new ArrayList<String>(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. */ |