Repository: square/okhttp
Issue: OkHttp changes the global SSL context, breaks other HTTP clients

We're enabling SPDY for the shared SSL context, and other HTTP clients like HttpURLConnection don't anticipate this, causing them to freak out and crash the app.

@skyisle's original report...

Here is backtrace.

DEBUG  I  backtrace:
DEBUG  I      #00  pc 00022430  /system/lib/libssl.so (SSL_select_next_proto+25)
DEBUG  I      #01  pc 000222ef  /system/lib/libjavacore.so
DEBUG  I      #02  pc 0002905f  /system/lib/libssl.so (ssl_parse_serverhello_tlsext+458)
DEBUG  I      #03  pc 00015957  /system/lib/libssl.so (ssl3_get_server_hello+894)
DEBUG  I      #04  pc 00018193  /system/lib/libssl.so (ssl3_connect+618)
DEBUG  I      #05  pc 000235d7  /system/lib/libssl.so (SSL_connect+18)
DEBUG  I      #06  pc 0001126b  /system/lib/libssl.so (ssl23_connect+1970)
DEBUG  I      #07  pc 0002350f  /system/lib/libssl.so (SSL_do_handshake+66)
DEBUG  I      #08  pc 00024bc5  /system/lib/libjavacore.so
DEBUG  I      #09  pc 0001e490  /system/lib/libdvm.so (dvmPlatformInvoke+112)
DEBUG  I      #10  pc 0004d2b1  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const_, JValue_, Method const_, Thread_)+396)
DEBUG  I      #11  pc 000278a0  /system/lib/libdvm.so
DEBUG  I      #12  pc 0002b77c  /system/lib/libdvm.so (dvmInterpret(Thread_, Method const_, JValue_)+176)
DEBUG  I      #13  pc 0005fae5  /system/lib/libdvm.so (dvmCallMethodV(Thread_, Method const_, Object_, bool, JValue_, std::__va_list)+272)
DEBUG  I      #14  pc 0005fb0f  /system/lib/libdvm.so (dvmCallMethod(Thread_, Method const_, Object_, JValue*, ...)+20)
DEBUG  I      #15  pc 0005466f  /system/lib/libdvm.so
DEBUG  I      #16  pc 0000e418  /system/lib/libc.so (__thread_entry+72)
DEBUG  I      #17  pc 0000db0c  /system/lib/libc.so (pthread_create+168)
DEBUG  I      #18  pc 00052f34  <unknown>


Changed files:
okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java

diff --git a/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java b/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java
index 68e1cfadb..b76b5cd45 100644
--- a/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java
+++ b/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java
@@ -15,8 +15,8 @@
  */
 package com.squareup.okhttp;
 
-import com.squareup.okhttp.internal.bytes.ByteString;
 import com.squareup.okhttp.internal.Util;
+import com.squareup.okhttp.internal.bytes.ByteString;
 import com.squareup.okhttp.internal.http.HttpAuthenticator;
 import com.squareup.okhttp.internal.http.HttpURLConnectionImpl;
 import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl;
@@ -31,11 +31,12 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLStreamHandler;
 import java.net.URLStreamHandlerFactory;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocketFactory;
 
 /** Configures and creates HTTP connections. */
@@ -202,8 +203,7 @@ public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable {
   /**
    * Sets the socket factory used to secure HTTPS connections.
    *
-   * <p>If unset, the {@link HttpsURLConnection#getDefaultSSLSocketFactory()
-   * system-wide default} SSL socket factory will be used.
+   * <p>If unset, a lazily created SSL socket factory will be used.
    */
   public OkHttpClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
     this.sslSocketFactory = sslSocketFactory;
@@ -218,7 +218,8 @@ public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable {
    * Sets the verifier used to confirm that response certificates apply to
    * requested hostnames for HTTPS connections.
    *
-   * <p>If unset, the {@link HttpsURLConnection#getDefaultHostnameVerifier()
+   * <p>If unset, the
+   * {@link javax.net.ssl.HttpsURLConnection#getDefaultHostnameVerifier()
    * system-wide default} hostname verifier will be used.
    */
   public OkHttpClient setHostnameVerifier(HostnameVerifier hostnameVerifier) {
@@ -422,7 +423,7 @@ public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable {
       result.responseCache = toOkResponseCacheOrNull(ResponseCache.getDefault());
     }
     if (result.sslSocketFactory == null) {
-      result.sslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+      result.sslSocketFactory = getDefaultSSLSocketFactory();
     }
     if (result.hostnameVerifier == null) {
       result.hostnameVerifier = OkHostnameVerifier.INSTANCE;
@@ -439,6 +440,30 @@ public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable {
     return result;
   }
 
+  /**
+   * Java and Android programs default to using a single global SSL context,
+   * accessible to HTTP clients as {@link SSLSocketFactory#getDefault()}. If we
+   * used the shared SSL context, when OkHttp enables NPN for its SPDY-related
+   * stuff, it would also enable NPN for other usages, which might crash them
+   * because NPN is enabled when it isn't expected to be.
+   * <p>
+   * This code avoids that by defaulting to an OkHttp created SSL context. The
+   * significant drawback of this approach is that apps that customize the
+   * global SSL context will lose these customizations.
+   */
+  private synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
+    if (sslSocketFactory == null) {
+      try {
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, null, null);
+        sslSocketFactory = sslContext.getSocketFactory();
+      } catch (GeneralSecurityException e) {
+        throw new AssertionError(); // The system has no TLS. Just give up.
+      }
+    }
+    return sslSocketFactory;
+  }
+
   /** Returns a shallow copy of this OkHttpClient. */
   @Override public OkHttpClient clone() {
     try {