From dd09bdb0de1dfdcbd3d3d076e27f75608ddb0ca3 Mon Sep 17 00:00:00 2001 From: jwilson Date: Tue, 9 Feb 2016 22:46:33 -0500 Subject: [PATCH] Hack in limited support for Robolectric. This gets trust manager extraction working, but not ALPN or other platform features. Also remove some unused socket tagging features. --- .../main/java/okhttp3/internal/Platform.java | 140 +++++------------- 1 file changed, 39 insertions(+), 101 deletions(-) diff --git a/okhttp/src/main/java/okhttp3/internal/Platform.java b/okhttp/src/main/java/okhttp3/internal/Platform.java index 3b370c8b5..91568ab4f 100644 --- a/okhttp/src/main/java/okhttp3/internal/Platform.java +++ b/okhttp/src/main/java/okhttp3/internal/Platform.java @@ -25,7 +25,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.Socket; -import java.net.SocketException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -83,14 +82,18 @@ public class Platform { System.out.println(warning); } - public void tagSocket(Socket socket) throws SocketException { - } - - public void untagSocket(Socket socket) throws SocketException { - } - public X509TrustManager trustManager(SSLSocketFactory sslSocketFactory) { - return null; + // Attempt to get the trust manager from an OpenJDK socket factory. We attempt this on all + // platforms in order to support Robolectric, which mixes classes from both Android and the + // Oracle JDK. Note that we don't support HTTP/2 or other nice features on Robolectric. + try { + Class sslContextClass = Class.forName("sun.security.ssl.SSLContextImpl"); + Object context = readFieldOrNull(sslSocketFactory, sslContextClass, "context"); + if (context == null) return null; + return readFieldOrNull(context, X509TrustManager.class, "trustManager"); + } catch (ClassNotFoundException e) { + return null; + } } public TrustRootIndex trustRootIndex(X509TrustManager trustManager) { @@ -140,61 +143,43 @@ public class Platform { "org.apache.harmony.xnet.provider.jsse.SSLParametersImpl"); } - OptionalMethod setUseSessionTickets - = new OptionalMethod<>(null, "setUseSessionTickets", boolean.class); - OptionalMethod setHostname - = new OptionalMethod<>(null, "setHostname", String.class); - Method trafficStatsTagSocket = null; - Method trafficStatsUntagSocket = null; + OptionalMethod setUseSessionTickets = new OptionalMethod<>( + null, "setUseSessionTickets", boolean.class); + OptionalMethod setHostname = new OptionalMethod<>( + null, "setHostname", String.class); OptionalMethod getAlpnSelectedProtocol = null; OptionalMethod setAlpnProtocols = null; - // Attempt to find Android 4.0+ APIs. + // Attempt to find Android 5.0+ APIs. try { - Class trafficStats = Class.forName("android.net.TrafficStats"); - trafficStatsTagSocket = trafficStats.getMethod("tagSocket", Socket.class); - trafficStatsUntagSocket = trafficStats.getMethod("untagSocket", Socket.class); - - // Attempt to find Android 5.0+ APIs. - try { - Class.forName("android.net.Network"); // Arbitrary class added in Android 5.0. - getAlpnSelectedProtocol = new OptionalMethod<>(byte[].class, "getAlpnSelectedProtocol"); - setAlpnProtocols = new OptionalMethod<>(null, "setAlpnProtocols", byte[].class); - } catch (ClassNotFoundException ignored) { - } - } catch (ClassNotFoundException | NoSuchMethodException ignored) { + Class.forName("android.net.Network"); // Arbitrary class added in Android 5.0. + getAlpnSelectedProtocol = new OptionalMethod<>(byte[].class, "getAlpnSelectedProtocol"); + setAlpnProtocols = new OptionalMethod<>(null, "setAlpnProtocols", byte[].class); + } catch (ClassNotFoundException ignored) { } return new Android(sslParametersClass, setUseSessionTickets, setHostname, - trafficStatsTagSocket, trafficStatsUntagSocket, getAlpnSelectedProtocol, - setAlpnProtocols); + getAlpnSelectedProtocol, setAlpnProtocols); } catch (ClassNotFoundException ignored) { // This isn't an Android runtime. } - // Find an Oracle JDK. + // Find Jetty's ALPN extension for OpenJDK. try { - Class sslContextClass = Class.forName("sun.security.ssl.SSLContextImpl"); - - // Find Jetty's ALPN extension for OpenJDK. - try { - String negoClassName = "org.eclipse.jetty.alpn.ALPN"; - Class negoClass = Class.forName(negoClassName); - Class providerClass = Class.forName(negoClassName + "$Provider"); - Class clientProviderClass = Class.forName(negoClassName + "$ClientProvider"); - Class serverProviderClass = Class.forName(negoClassName + "$ServerProvider"); - Method putMethod = negoClass.getMethod("put", SSLSocket.class, providerClass); - Method getMethod = negoClass.getMethod("get", SSLSocket.class); - Method removeMethod = negoClass.getMethod("remove", SSLSocket.class); - return new JdkWithJettyBootPlatform(sslContextClass, - putMethod, getMethod, removeMethod, clientProviderClass, serverProviderClass); - } catch (ClassNotFoundException | NoSuchMethodException ignored) { - } - - return new JdkPlatform(sslContextClass); - } catch (ClassNotFoundException ignored) { + String negoClassName = "org.eclipse.jetty.alpn.ALPN"; + Class negoClass = Class.forName(negoClassName); + Class providerClass = Class.forName(negoClassName + "$Provider"); + Class clientProviderClass = Class.forName(negoClassName + "$ClientProvider"); + Class serverProviderClass = Class.forName(negoClassName + "$ServerProvider"); + Method putMethod = negoClass.getMethod("put", SSLSocket.class, providerClass); + Method getMethod = negoClass.getMethod("get", SSLSocket.class); + Method removeMethod = negoClass.getMethod("remove", SSLSocket.class); + return new JdkWithJettyBootPlatform( + putMethod, getMethod, removeMethod, clientProviderClass, serverProviderClass); + } catch (ClassNotFoundException | NoSuchMethodException ignored) { } + // Probably an Oracle JDK like OpenJDK. return new Platform(); } @@ -206,23 +191,16 @@ public class Platform { private final OptionalMethod setUseSessionTickets; private final OptionalMethod setHostname; - // Non-null on Android 4.0+. - private final Method trafficStatsTagSocket; - private final Method trafficStatsUntagSocket; - // Non-null on Android 5.0+. private final OptionalMethod getAlpnSelectedProtocol; private final OptionalMethod setAlpnProtocols; public Android(Class sslParametersClass, OptionalMethod setUseSessionTickets, - OptionalMethod setHostname, Method trafficStatsTagSocket, - Method trafficStatsUntagSocket, OptionalMethod getAlpnSelectedProtocol, + OptionalMethod setHostname, OptionalMethod getAlpnSelectedProtocol, OptionalMethod setAlpnProtocols) { this.sslParametersClass = sslParametersClass; this.setUseSessionTickets = setUseSessionTickets; this.setHostname = setHostname; - this.trafficStatsTagSocket = trafficStatsTagSocket; - this.trafficStatsUntagSocket = trafficStatsUntagSocket; this.getAlpnSelectedProtocol = getAlpnSelectedProtocol; this.setAlpnProtocols = setAlpnProtocols; } @@ -254,7 +232,7 @@ public class Platform { sslSocketFactory.getClass().getClassLoader()); context = readFieldOrNull(sslSocketFactory, gmsSslParametersClass, "sslParameters"); } catch (ClassNotFoundException e) { - return null; + return super.trustManager(sslSocketFactory); } } @@ -294,30 +272,6 @@ public class Platform { return alpnResult != null ? new String(alpnResult, Util.UTF_8) : null; } - @Override public void tagSocket(Socket socket) throws SocketException { - if (trafficStatsTagSocket == null) return; - - try { - trafficStatsTagSocket.invoke(null, socket); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e.getCause()); - } - } - - @Override public void untagSocket(Socket socket) throws SocketException { - if (trafficStatsUntagSocket == null) return; - - try { - trafficStatsUntagSocket.invoke(null, socket); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e.getCause()); - } - } - @Override public void log(String message) { // Split by line, then ensure each line can fit into Log's maximum length. for (int i = 0, length = message.length(); i < length; i++) { @@ -332,34 +286,18 @@ public class Platform { } } - /** JDK 1.7 or better. */ - private static class JdkPlatform extends Platform { - private final Class sslContextClass; - - public JdkPlatform(Class sslContextClass) { - this.sslContextClass = sslContextClass; - } - - @Override public X509TrustManager trustManager(SSLSocketFactory sslSocketFactory) { - Object context = readFieldOrNull(sslSocketFactory, sslContextClass, "context"); - if (context == null) return null; - return readFieldOrNull(context, X509TrustManager.class, "trustManager"); - } - } - /** * OpenJDK 7+ with {@code org.mortbay.jetty.alpn/alpn-boot} in the boot class path. */ - private static class JdkWithJettyBootPlatform extends JdkPlatform { + private static class JdkWithJettyBootPlatform extends Platform { private final Method putMethod; private final Method getMethod; private final Method removeMethod; private final Class clientProviderClass; private final Class serverProviderClass; - public JdkWithJettyBootPlatform(Class sslContextClass, Method putMethod, Method getMethod, - Method removeMethod, Class clientProviderClass, Class serverProviderClass) { - super(sslContextClass); + public JdkWithJettyBootPlatform(Method putMethod, Method getMethod, Method removeMethod, + Class clientProviderClass, Class serverProviderClass) { this.putMethod = putMethod; this.getMethod = getMethod; this.removeMethod = removeMethod;