/pushWithPythonServer/pushnotification/platforms/android/CordovaLib/src/com/squareup/okhttp/internal/Platform.java
Java | 370 lines | 270 code | 35 blank | 65 comment | 36 complexity | f21b67c48f83bfe2fb015beb5c69b193 MD5 | raw file
- /*
- * Copyright (C) 2012 Square, Inc.
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.squareup.okhttp.internal;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.io.UnsupportedEncodingException;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketException;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.net.URL;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import java.util.zip.Deflater;
- import java.util.zip.DeflaterOutputStream;
- import javax.net.ssl.SSLSocket;
- /**
- * Access to Platform-specific features necessary for SPDY and advanced TLS.
- *
- * <h3>SPDY</h3>
- * SPDY requires a TLS extension called NPN (Next Protocol Negotiation) that's
- * available in Android 4.1+ and OpenJDK 7+ (with the npn-boot extension). It
- * also requires a recent version of {@code DeflaterOutputStream} that is
- * public API in Java 7 and callable via reflection in Android 4.1+.
- */
- public class Platform {
- private static final Platform PLATFORM = findPlatform();
- private Constructor<DeflaterOutputStream> deflaterConstructor;
- public static Platform get() {
- return PLATFORM;
- }
- /** Prefix used on custom headers. */
- public String getPrefix() {
- return "OkHttp";
- }
- public void logW(String warning) {
- System.out.println(warning);
- }
- public void tagSocket(Socket socket) throws SocketException {
- }
- public void untagSocket(Socket socket) throws SocketException {
- }
- public URI toUriLenient(URL url) throws URISyntaxException {
- return url.toURI(); // this isn't as good as the built-in toUriLenient
- }
- /**
- * Attempt a TLS connection with useful extensions enabled. This mode
- * supports more features, but is less likely to be compatible with older
- * HTTPS servers.
- */
- public void enableTlsExtensions(SSLSocket socket, String uriHost) {
- }
- /**
- * Attempt a secure connection with basic functionality to maximize
- * compatibility. Currently this uses SSL 3.0.
- */
- public void supportTlsIntolerantServer(SSLSocket socket) {
- socket.setEnabledProtocols(new String[] {"SSLv3"});
- }
- /** Returns the negotiated protocol, or null if no protocol was negotiated. */
- public byte[] getNpnSelectedProtocol(SSLSocket socket) {
- return null;
- }
- /**
- * Sets client-supported protocols on a socket to send to a server. The
- * protocols are only sent if the socket implementation supports NPN.
- */
- public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
- }
- public void connectSocket(Socket socket, InetSocketAddress address,
- int connectTimeout) throws IOException {
- socket.connect(address, connectTimeout);
- }
- /**
- * Returns a deflater output stream that supports SYNC_FLUSH for SPDY name
- * value blocks. This throws an {@link UnsupportedOperationException} on
- * Java 6 and earlier where there is no built-in API to do SYNC_FLUSH.
- */
- public OutputStream newDeflaterOutputStream(OutputStream out, Deflater deflater,
- boolean syncFlush) {
- try {
- Constructor<DeflaterOutputStream> constructor = deflaterConstructor;
- if (constructor == null) {
- constructor = deflaterConstructor = DeflaterOutputStream.class.getConstructor(
- OutputStream.class, Deflater.class, boolean.class);
- }
- return constructor.newInstance(out, deflater, syncFlush);
- } catch (NoSuchMethodException e) {
- throw new UnsupportedOperationException("Cannot SPDY; no SYNC_FLUSH available");
- } catch (InvocationTargetException e) {
- throw e.getCause() instanceof RuntimeException ? (RuntimeException) e.getCause()
- : new RuntimeException(e.getCause());
- } catch (InstantiationException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError();
- }
- }
- /** Attempt to match the host runtime to a capable Platform implementation. */
- private static Platform findPlatform() {
- // Attempt to find Android 2.3+ APIs.
- Class<?> openSslSocketClass;
- Method setUseSessionTickets;
- Method setHostname;
- try {
- try {
- openSslSocketClass = Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl");
- } catch (ClassNotFoundException ignored) {
- // Older platform before being unbundled.
- openSslSocketClass = Class.forName(
- "org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
- }
- setUseSessionTickets = openSslSocketClass.getMethod("setUseSessionTickets", boolean.class);
- setHostname = openSslSocketClass.getMethod("setHostname", String.class);
- // Attempt to find Android 4.1+ APIs.
- try {
- Method setNpnProtocols = openSslSocketClass.getMethod("setNpnProtocols", byte[].class);
- Method getNpnSelectedProtocol = openSslSocketClass.getMethod("getNpnSelectedProtocol");
- return new Android41(openSslSocketClass, setUseSessionTickets, setHostname,
- setNpnProtocols, getNpnSelectedProtocol);
- } catch (NoSuchMethodException ignored) {
- return new Android23(openSslSocketClass, setUseSessionTickets, setHostname);
- }
- } catch (ClassNotFoundException ignored) {
- // This isn't an Android runtime.
- } catch (NoSuchMethodException ignored) {
- // This isn't Android 2.3 or better.
- }
- // Attempt to find the Jetty's NPN extension for OpenJDK.
- try {
- String npnClassName = "org.eclipse.jetty.npn.NextProtoNego";
- Class<?> nextProtoNegoClass = Class.forName(npnClassName);
- Class<?> providerClass = Class.forName(npnClassName + "$Provider");
- Class<?> clientProviderClass = Class.forName(npnClassName + "$ClientProvider");
- Class<?> serverProviderClass = Class.forName(npnClassName + "$ServerProvider");
- Method putMethod = nextProtoNegoClass.getMethod("put", SSLSocket.class, providerClass);
- Method getMethod = nextProtoNegoClass.getMethod("get", SSLSocket.class);
- return new JdkWithJettyNpnPlatform(
- putMethod, getMethod, clientProviderClass, serverProviderClass);
- } catch (ClassNotFoundException ignored) {
- // NPN isn't on the classpath.
- } catch (NoSuchMethodException ignored) {
- // The NPN version isn't what we expect.
- }
- return new Platform();
- }
- /** Android version 2.3 and newer support TLS session tickets and server name indication (SNI). */
- private static class Android23 extends Platform {
- protected final Class<?> openSslSocketClass;
- private final Method setUseSessionTickets;
- private final Method setHostname;
- private Android23(
- Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname) {
- this.openSslSocketClass = openSslSocketClass;
- this.setUseSessionTickets = setUseSessionTickets;
- this.setHostname = setHostname;
- }
- @Override public void connectSocket(Socket socket, InetSocketAddress address,
- int connectTimeout) throws IOException {
- try {
- socket.connect(address, connectTimeout);
- } catch (SecurityException se) {
- // Before android 4.3, socket.connect could throw a SecurityException
- // if opening a socket resulted in an EACCES error.
- IOException ioException = new IOException("Exception in connect");
- ioException.initCause(se);
- throw ioException;
- }
- }
- @Override public void enableTlsExtensions(SSLSocket socket, String uriHost) {
- super.enableTlsExtensions(socket, uriHost);
- if (openSslSocketClass.isInstance(socket)) {
- // This is Android: use reflection on OpenSslSocketImpl.
- try {
- setUseSessionTickets.invoke(socket, true);
- setHostname.invoke(socket, uriHost);
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }
- }
- }
- /** Android version 4.1 and newer support NPN. */
- private static class Android41 extends Android23 {
- private final Method setNpnProtocols;
- private final Method getNpnSelectedProtocol;
- private Android41(Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname,
- Method setNpnProtocols, Method getNpnSelectedProtocol) {
- super(openSslSocketClass, setUseSessionTickets, setHostname);
- this.setNpnProtocols = setNpnProtocols;
- this.getNpnSelectedProtocol = getNpnSelectedProtocol;
- }
- @Override public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
- if (!openSslSocketClass.isInstance(socket)) {
- return;
- }
- try {
- setNpnProtocols.invoke(socket, new Object[] {npnProtocols});
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e);
- }
- }
- @Override public byte[] getNpnSelectedProtocol(SSLSocket socket) {
- if (!openSslSocketClass.isInstance(socket)) {
- return null;
- }
- try {
- return (byte[]) getNpnSelectedProtocol.invoke(socket);
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }
- }
- /** OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class path. */
- private static class JdkWithJettyNpnPlatform extends Platform {
- private final Method getMethod;
- private final Method putMethod;
- private final Class<?> clientProviderClass;
- private final Class<?> serverProviderClass;
- public JdkWithJettyNpnPlatform(Method putMethod, Method getMethod, Class<?> clientProviderClass,
- Class<?> serverProviderClass) {
- this.putMethod = putMethod;
- this.getMethod = getMethod;
- this.clientProviderClass = clientProviderClass;
- this.serverProviderClass = serverProviderClass;
- }
- @Override public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
- try {
- List<String> strings = new ArrayList<String>();
- for (int i = 0; i < npnProtocols.length; ) {
- int length = npnProtocols[i++];
- strings.add(new String(npnProtocols, i, length, "US-ASCII"));
- i += length;
- }
- Object provider = Proxy.newProxyInstance(Platform.class.getClassLoader(),
- new Class[] {clientProviderClass, serverProviderClass},
- new JettyNpnProvider(strings));
- putMethod.invoke(null, socket, provider);
- } catch (UnsupportedEncodingException e) {
- throw new AssertionError(e);
- } catch (InvocationTargetException e) {
- throw new AssertionError(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }
- @Override public byte[] getNpnSelectedProtocol(SSLSocket socket) {
- try {
- JettyNpnProvider provider =
- (JettyNpnProvider) Proxy.getInvocationHandler(getMethod.invoke(null, socket));
- if (!provider.unsupported && provider.selected == null) {
- Logger logger = Logger.getLogger("com.squareup.okhttp.OkHttpClient");
- logger.log(Level.INFO,
- "NPN callback dropped so SPDY is disabled. " + "Is npn-boot on the boot class path?");
- return null;
- }
- return provider.unsupported ? null : provider.selected.getBytes("US-ASCII");
- } catch (UnsupportedEncodingException e) {
- throw new AssertionError();
- } catch (InvocationTargetException e) {
- throw new AssertionError();
- } catch (IllegalAccessException e) {
- throw new AssertionError();
- }
- }
- }
- /**
- * Handle the methods of NextProtoNego's ClientProvider and ServerProvider
- * without a compile-time dependency on those interfaces.
- */
- private static class JettyNpnProvider implements InvocationHandler {
- private final List<String> protocols;
- private boolean unsupported;
- private String selected;
- public JettyNpnProvider(List<String> protocols) {
- this.protocols = protocols;
- }
- @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- String methodName = method.getName();
- Class<?> returnType = method.getReturnType();
- if (args == null) {
- args = Util.EMPTY_STRING_ARRAY;
- }
- if (methodName.equals("supports") && boolean.class == returnType) {
- return true;
- } else if (methodName.equals("unsupported") && void.class == returnType) {
- this.unsupported = true;
- return null;
- } else if (methodName.equals("protocols") && args.length == 0) {
- return protocols;
- } else if (methodName.equals("selectProtocol")
- && String.class == returnType
- && args.length == 1
- && (args[0] == null || args[0] instanceof List)) {
- // TODO: use OpenSSL's algorithm which uses both lists
- List<?> serverProtocols = (List) args[0];
- this.selected = protocols.get(0);
- return selected;
- } else if (methodName.equals("protocolSelected") && args.length == 1) {
- this.selected = (String) args[0];
- return null;
- } else {
- return method.invoke(this, args);
- }
- }
- }
- }