diff --git a/okhttp/src/main/java/com/squareup/okhttp/OkHttpTlsUpgrader.java b/okhttp/src/main/java/com/squareup/okhttp/OkHttpTlsUpgrader.java index a1235b14ba..bfb4b34821 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/OkHttpTlsUpgrader.java +++ b/okhttp/src/main/java/com/squareup/okhttp/OkHttpTlsUpgrader.java @@ -34,6 +34,7 @@ package com.squareup.okhttp; import com.google.common.base.Preconditions; import com.squareup.okhttp.internal.Platform; +import com.squareup.okhttp.internal.SelectedProtocolQuerier; import java.io.IOException; import java.net.InetSocketAddress; @@ -75,22 +76,15 @@ public final class OkHttpTlsUpgrader { Platform platform = Platform.get(); - String negotiatedProtocol = null; - try { - // It's possible that the user provided SSLSocketFactory has already done the handshake - // when creates the SSLSocket. - negotiatedProtocol = platform.getSelectedProtocol(sslSocket); - } catch (Exception e) { - // In some implementations, querying selected protocol before the handshake will fail with - // exception. - } + // It's possible that the user provided SSLSocketFactory has already done the handshake + // when creates the SSLSocket. + String negotiatedProtocol = SelectedProtocolQuerier.getSelectedProtocol(sslSocket); if (negotiatedProtocol == null) { - try { // Force handshake. sslSocket.startHandshake(); - negotiatedProtocol = platform.getSelectedProtocol(sslSocket); + negotiatedProtocol = SelectedProtocolQuerier.getSelectedProtocol(sslSocket); if (negotiatedProtocol == null) { throw new RuntimeException("protocol negotiation failed"); } diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/SelectedProtocolQuerier.java b/okhttp/src/main/java/com/squareup/okhttp/internal/SelectedProtocolQuerier.java new file mode 100644 index 0000000000..79d954c534 --- /dev/null +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/SelectedProtocolQuerier.java @@ -0,0 +1,82 @@ +/* + * Copyright 2014, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.squareup.okhttp.internal; + +import java.net.Socket; + +import javax.net.ssl.SSLSocket; + +/** + * A helper class located in package com.squareup.okhttp.internal for getting protocol + * selected by NPN on Android. + */ +public class SelectedProtocolQuerier { + private static Platform platform = Platform.get(); + + // byte[] getNpnSelectedProtocol() + private static final OptionalMethod GET_NPN_SELECTED_PROTOCOL = + new OptionalMethod(byte[].class, "getNpnSelectedProtocol"); + private static boolean android; + + /** Returns the negotiated protocol, or null if no protocol was negotiated. */ + public static String getSelectedProtocol(SSLSocket socket) { + String protocol = null; + try { + protocol = platform.getSelectedProtocol(socket); + } catch (Exception e) { + // In some implementations, querying selected protocol before the handshake will fail with + // exception. + } + if (protocol == null && android && GET_NPN_SELECTED_PROTOCOL.isSupported(socket)) { + byte[] result = (byte[]) GET_NPN_SELECTED_PROTOCOL.invokeWithoutCheckedException(socket); + if (result != null) { + protocol = new String(result, Util.UTF_8); + } + } + return protocol; + } + + static { + android = true; + try { + // Attempt to find Android 2.3+ APIs. + Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl"); + } catch (ClassNotFoundException ignored) { + try { + // Older platform before being unbundled. + Class.forName("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl"); + } catch (ClassNotFoundException ignored2) { + android = false; + } + } + } +}