12231db3e6bb54447a9b14cf004a6cb03c373651cjwilson/*
22231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Copyright (C) 2012 Square, Inc.
32231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Copyright (C) 2012 The Android Open Source Project
42231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *
52231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Licensed under the Apache License, Version 2.0 (the "License");
62231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * you may not use this file except in compliance with the License.
72231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * You may obtain a copy of the License at
82231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *
92231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *      http://www.apache.org/licenses/LICENSE-2.0
102231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *
112231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Unless required by applicable law or agreed to in writing, software
122231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * distributed under the License is distributed on an "AS IS" BASIS,
132231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
142231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * See the License for the specific language governing permissions and
152231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * limitations under the License.
162231db3e6bb54447a9b14cf004a6cb03c373651cjwilson */
172231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal;
182231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
192231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.OkHttpClient;
202231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.io.OutputStream;
212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.io.UnsupportedEncodingException;
222231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.lang.reflect.Constructor;
232231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.lang.reflect.InvocationHandler;
242231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.lang.reflect.InvocationTargetException;
252231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.lang.reflect.Method;
262231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.lang.reflect.Proxy;
272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.Socket;
282231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.SocketException;
292231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.URI;
302231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.URISyntaxException;
312231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.URL;
322231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.util.ArrayList;
332231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.util.List;
342231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.util.logging.Level;
352231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.util.logging.Logger;
362231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.util.zip.Deflater;
372231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.util.zip.DeflaterOutputStream;
382231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLSocket;
392231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
402231db3e6bb54447a9b14cf004a6cb03c373651cjwilson/**
412231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Access to Platform-specific features necessary for SPDY and advanced TLS.
422231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *
432231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * <h3>SPDY</h3>
442231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * SPDY requires a TLS extension called NPN (Next Protocol Negotiation) that's
452231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * available in Android 4.1+ and OpenJDK 7+ (with the npn-boot extension). It
462231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * also requires a recent version of {@code DeflaterOutputStream} that is
472231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * public API in Java 7 and callable via reflection in Android 4.1+.
482231db3e6bb54447a9b14cf004a6cb03c373651cjwilson */
492231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpublic class Platform {
5054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final Platform PLATFORM = findPlatform();
512231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private Constructor<DeflaterOutputStream> deflaterConstructor;
532231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
5454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static Platform get() {
5554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return PLATFORM;
5654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
572231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
5854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void logW(String warning) {
5954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    System.out.println(warning);
6054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
612231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void tagSocket(Socket socket) throws SocketException {
6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
642231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
6554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void untagSocket(Socket socket) throws SocketException {
6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
672231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
6854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public URI toUriLenient(URL url) throws URISyntaxException {
6954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return url.toURI(); // this isn't as good as the built-in toUriLenient
7054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
7154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
7254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
7354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Attempt a TLS connection with useful extensions enabled. This mode
7454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * supports more features, but is less likely to be compatible with older
7554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * HTTPS servers.
7654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
7754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void enableTlsExtensions(SSLSocket socket, String uriHost) {
7854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
7954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
8154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Attempt a secure connection with basic functionality to maximize
8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * compatibility. Currently this uses SSL 3.0.
8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void supportTlsIntolerantServer(SSLSocket socket) {
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    socket.setEnabledProtocols(new String[] {"SSLv3"});
8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns the negotiated protocol, or null if no protocol was negotiated. */
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public byte[] getNpnSelectedProtocol(SSLSocket socket) {
9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return null;
9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
9454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Sets client-supported protocols on a socket to send to a server. The
9554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * protocols are only sent if the socket implementation supports NPN.
9654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
992231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns a deflater output stream that supports SYNC_FLUSH for SPDY name
10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * value blocks. This throws an {@link UnsupportedOperationException} on
10354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Java 6 and earlier where there is no built-in API to do SYNC_FLUSH.
10454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public OutputStream newDeflaterOutputStream(OutputStream out, Deflater deflater,
10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      boolean syncFlush) {
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Constructor<DeflaterOutputStream> constructor = deflaterConstructor;
10954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (constructor == null) {
11054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        constructor = deflaterConstructor = DeflaterOutputStream.class.getConstructor(
11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            OutputStream.class, Deflater.class, boolean.class);
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return constructor.newInstance(out, deflater, syncFlush);
11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NoSuchMethodException e) {
11554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new UnsupportedOperationException("Cannot SPDY; no SYNC_FLUSH available");
11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (InvocationTargetException e) {
11754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw e.getCause() instanceof RuntimeException ? (RuntimeException) e.getCause()
11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          : new RuntimeException(e.getCause());
11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (InstantiationException e) {
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new RuntimeException(e);
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalAccessException e) {
12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new AssertionError();
1232231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
1252231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Attempt to match the host runtime to a capable Platform implementation. */
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static Platform findPlatform() {
12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Attempt to find Android 2.3+ APIs.
12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Class<?> openSslSocketClass;
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Method setUseSessionTickets;
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Method setHostname;
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      openSslSocketClass = Class.forName("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      setUseSessionTickets = openSslSocketClass.getMethod("setUseSessionTickets", boolean.class);
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      setHostname = openSslSocketClass.getMethod("setHostname", String.class);
13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Attempt to find Android 4.1+ APIs.
13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Method setNpnProtocols = openSslSocketClass.getMethod("setNpnProtocols", byte[].class);
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Method getNpnSelectedProtocol = openSslSocketClass.getMethod("getNpnSelectedProtocol");
14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return new Android41(openSslSocketClass, setUseSessionTickets, setHostname, setNpnProtocols,
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            getNpnSelectedProtocol);
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (NoSuchMethodException ignored) {
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return new Android23(openSslSocketClass, setUseSessionTickets, setHostname);
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ClassNotFoundException ignored) {
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // This isn't an Android runtime.
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NoSuchMethodException ignored) {
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // This isn't Android 2.3 or better.
1502231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
1512231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Attempt to find the Jetty's NPN extension for OpenJDK.
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      String npnClassName = "org.eclipse.jetty.npn.NextProtoNego";
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Class<?> nextProtoNegoClass = Class.forName(npnClassName);
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Class<?> providerClass = Class.forName(npnClassName + "$Provider");
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Class<?> clientProviderClass = Class.forName(npnClassName + "$ClientProvider");
15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Class<?> serverProviderClass = Class.forName(npnClassName + "$ServerProvider");
15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Method putMethod = nextProtoNegoClass.getMethod("put", SSLSocket.class, providerClass);
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Method getMethod = nextProtoNegoClass.getMethod("get", SSLSocket.class);
16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return new JdkWithJettyNpnPlatform(putMethod, getMethod, clientProviderClass,
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          serverProviderClass);
16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ClassNotFoundException ignored) {
16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return new Platform(); // NPN isn't on the classpath.
16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NoSuchMethodException ignored) {
16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return new Platform(); // The NPN version isn't what we expect.
1672231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Android version 2.3 and newer support TLS session tickets and server name
17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * indication (SNI).
17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static class Android23 extends Platform {
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    protected final Class<?> openSslSocketClass;
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Method setUseSessionTickets;
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Method setHostname;
1782231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private Android23(Class<?> openSslSocketClass, Method setUseSessionTickets,
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Method setHostname) {
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.openSslSocketClass = openSslSocketClass;
18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.setUseSessionTickets = setUseSessionTickets;
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.setHostname = setHostname;
1842231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
1852231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public void enableTlsExtensions(SSLSocket socket, String uriHost) {
18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      super.enableTlsExtensions(socket, uriHost);
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (openSslSocketClass.isInstance(socket)) {
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        // This is Android: use reflection on OpenSslSocketImpl.
1902231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        try {
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          setUseSessionTickets.invoke(socket, true);
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          setHostname.invoke(socket, uriHost);
1932231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        } catch (InvocationTargetException e) {
19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throw new RuntimeException(e);
1952231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        } catch (IllegalAccessException e) {
19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throw new AssertionError(e);
1972231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        }
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
1992231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
2012231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Android version 4.1 and newer support NPN. */
20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static class Android41 extends Android23 {
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Method setNpnProtocols;
20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Method getNpnSelectedProtocol;
2062231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private Android41(Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname,
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Method setNpnProtocols, Method getNpnSelectedProtocol) {
20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      super(openSslSocketClass, setUseSessionTickets, setHostname);
21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.setNpnProtocols = setNpnProtocols;
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.getNpnSelectedProtocol = getNpnSelectedProtocol;
2122231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
2132231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (!openSslSocketClass.isInstance(socket)) {
21654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return;
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        setNpnProtocols.invoke(socket, new Object[] {npnProtocols});
22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (IllegalAccessException e) {
22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError(e);
22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (InvocationTargetException e) {
22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new RuntimeException(e);
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
2252231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
2262231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public byte[] getNpnSelectedProtocol(SSLSocket socket) {
22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (!openSslSocketClass.isInstance(socket)) {
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
23054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return (byte[]) getNpnSelectedProtocol.invoke(socket);
23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (InvocationTargetException e) {
23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new RuntimeException(e);
23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (IllegalAccessException e) {
23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError(e);
23754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
2382231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
2402231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class
24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * path.
24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static class JdkWithJettyNpnPlatform extends Platform {
24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Method getMethod;
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Method putMethod;
24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Class<?> clientProviderClass;
24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Class<?> serverProviderClass;
2502231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public JdkWithJettyNpnPlatform(Method putMethod, Method getMethod, Class<?> clientProviderClass,
25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Class<?> serverProviderClass) {
25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.putMethod = putMethod;
25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.getMethod = getMethod;
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.clientProviderClass = clientProviderClass;
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.serverProviderClass = serverProviderClass;
25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2582231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        List<String> strings = new ArrayList<String>();
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int i = 0; i < npnProtocols.length; ) {
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          int length = npnProtocols[i++];
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          strings.add(new String(npnProtocols, i, length, "US-ASCII"));
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          i += length;
2662231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        }
26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Object provider = Proxy.newProxyInstance(Platform.class.getClassLoader(),
26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            new Class[] {clientProviderClass, serverProviderClass},
26954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            new JettyNpnProvider(strings));
27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        putMethod.invoke(null, socket, provider);
27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (UnsupportedEncodingException e) {
27254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError(e);
27354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (InvocationTargetException e) {
27454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError(e);
27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (IllegalAccessException e) {
27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError(e);
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
27854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2792231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
28054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public byte[] getNpnSelectedProtocol(SSLSocket socket) {
28154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
28254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        JettyNpnProvider provider =
28354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            (JettyNpnProvider) Proxy.getInvocationHandler(getMethod.invoke(null, socket));
28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        if (!provider.unsupported && provider.selected == null) {
28554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          Logger logger = Logger.getLogger(OkHttpClient.class.getName());
28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          logger.log(Level.INFO,
28754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson              "NPN callback dropped so SPDY is disabled. " + "Is npn-boot on the boot class path?");
28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          return null;
2892231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        }
29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return provider.unsupported ? null : provider.selected.getBytes("US-ASCII");
29154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (UnsupportedEncodingException e) {
29254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError();
29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (InvocationTargetException e) {
29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError();
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (IllegalAccessException e) {
29654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new AssertionError();
29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
2982231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
3002231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
30154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Handle the methods of NextProtoNego's ClientProvider and ServerProvider
30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * without a compile-time dependency on those interfaces.
30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static class JettyNpnProvider implements InvocationHandler {
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final List<String> protocols;
30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private boolean unsupported;
30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private String selected;
3092231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public JettyNpnProvider(List<String> protocols) {
31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.protocols = protocols;
31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
3132231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
31554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      String methodName = method.getName();
31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Class<?> returnType = method.getReturnType();
31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (args == null) {
31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        args = Util.EMPTY_STRING_ARRAY;
31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (methodName.equals("supports") && boolean.class == returnType) {
32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return true;
32254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else if (methodName.equals("unsupported") && void.class == returnType) {
32354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        this.unsupported = true;
32454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
32554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else if (methodName.equals("protocols") && args.length == 0) {
32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return protocols;
32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else if (methodName.equals("selectProtocol")
32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          && String.class == returnType
32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          && args.length == 1
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          && (args[0] == null || args[0] instanceof List)) {
33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        // TODO: use OpenSSL's algorithm which uses both lists
33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        List<?> serverProtocols = (List) args[0];
33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        this.selected = protocols.get(0);
33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return selected;
33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else if (methodName.equals("protocolSelected") && args.length == 1) {
33654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        this.selected = (String) args[0];
33754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return method.invoke(this, args);
34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
3412231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
3432231db3e6bb54447a9b14cf004a6cb03c373651cjwilson}
344