mirror of https://github.com/grpc/grpc-java.git
okhttp: support JDK9 ALPN (#4136)
This commit is contained in:
parent
5241172475
commit
52fedb624d
|
|
@ -36,6 +36,8 @@ import org.junit.runners.Parameterized.Parameters;
|
|||
|
||||
/**
|
||||
* Tests that Channel and Server builders properly hide the static constructors.
|
||||
*
|
||||
* <p>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<Object[]> params() throws Exception {
|
||||
ClassLoader loader = ChannelAndServerBuilderTest.class.getClassLoader();
|
||||
Collection<ClassInfo> 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<Object[]>();
|
||||
}
|
||||
List<Object[]> classes = new ArrayList<Object[]>();
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue