mirror of https://github.com/grpc/grpc-java.git
Support NPN fallback for Android.
This commit is contained in:
parent
f6121165f9
commit
e8086b5351
|
|
@ -35,6 +35,8 @@ import com.squareup.okhttp.Protocol;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.Security;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
|
|
@ -112,12 +114,57 @@ public class OkHttpProtocolNegotiator {
|
||||||
// byte[] getAlpnSelectedProtocol()
|
// byte[] getAlpnSelectedProtocol()
|
||||||
private static final OptionalMethod<Socket> GET_ALPN_SELECTED_PROTOCOL =
|
private static final OptionalMethod<Socket> GET_ALPN_SELECTED_PROTOCOL =
|
||||||
new OptionalMethod<Socket>(byte[].class, "getAlpnSelectedProtocol");
|
new OptionalMethod<Socket>(byte[].class, "getAlpnSelectedProtocol");
|
||||||
// setAlpnSelectedProtocol(byte[])
|
// setAlpnProtocol(byte[])
|
||||||
private static final OptionalMethod<Socket> SET_ALPN_PROTOCOLS =
|
private static final OptionalMethod<Socket> SET_ALPN_PROTOCOLS =
|
||||||
new OptionalMethod<Socket>(null, "setAlpnProtocols", byte[].class);
|
new OptionalMethod<Socket>(null, "setAlpnProtocols", byte[].class);
|
||||||
// byte[] getNpnSelectedProtocol()
|
// byte[] getNpnSelectedProtocol()
|
||||||
private static final OptionalMethod<Socket> GET_NPN_SELECTED_PROTOCOL =
|
private static final OptionalMethod<Socket> GET_NPN_SELECTED_PROTOCOL =
|
||||||
new OptionalMethod<Socket>(byte[].class, "getNpnSelectedProtocol");
|
new OptionalMethod<Socket>(byte[].class, "getNpnSelectedProtocol");
|
||||||
|
// setNpnProtocol(byte[])
|
||||||
|
private static final OptionalMethod<Socket> SET_NPN_PROTOCOLS =
|
||||||
|
new OptionalMethod<Socket>(null, "setNpnProtocols", byte[].class);
|
||||||
|
|
||||||
|
private enum TlsExtensionType {
|
||||||
|
ALPN_AND_NPN,
|
||||||
|
NPN,
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TlsExtensionType tlsExtensionType;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Decide which TLS Extension (APLN and NPN) we will use, follow the rules:
|
||||||
|
// 1. If Google Play Services Security Provider is installed, use both
|
||||||
|
// 2. If on Android 5.0 or later, use both, else
|
||||||
|
// 3. If on Android 4.1 or later, use NPN, else
|
||||||
|
// 4. Fail.
|
||||||
|
// TODO(madongfly): Logging.
|
||||||
|
|
||||||
|
// Check if Google Play Services Security Provider is installed.
|
||||||
|
Provider provider = Security.getProvider("GmsCore_OpenSSL");
|
||||||
|
if (provider != null) {
|
||||||
|
tlsExtensionType = TlsExtensionType.ALPN_AND_NPN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if on Android 5.0 or later.
|
||||||
|
if (tlsExtensionType == null) {
|
||||||
|
try {
|
||||||
|
Class.forName("android.net.Network"); // Arbitrary class added in Android 5.0.
|
||||||
|
tlsExtensionType = TlsExtensionType.ALPN_AND_NPN;
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
// making checkstyle happy.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if on Android 4.1 or later.
|
||||||
|
if (tlsExtensionType == null) {
|
||||||
|
try {
|
||||||
|
Class.forName("android.app.ActivityOptions"); // Arbitrary class added in Android 4.1.
|
||||||
|
tlsExtensionType = TlsExtensionType.NPN;
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
// making checkstyle happy.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String negotiate(SSLSocket sslSocket, String hostname, List<Protocol> protocols)
|
public String negotiate(SSLSocket sslSocket, String hostname, List<Protocol> protocols)
|
||||||
|
|
@ -144,16 +191,22 @@ public class OkHttpProtocolNegotiator {
|
||||||
SET_HOSTNAME.invokeOptionalWithoutCheckedException(sslSocket, hostname);
|
SET_HOSTNAME.invokeOptionalWithoutCheckedException(sslSocket, hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable ALPN.
|
|
||||||
if (SET_ALPN_PROTOCOLS.isSupported(sslSocket)) {
|
|
||||||
Object[] parameters = {Platform.concatLengthPrefixed(protocols)};
|
Object[] parameters = {Platform.concatLengthPrefixed(protocols)};
|
||||||
|
if (tlsExtensionType == TlsExtensionType.ALPN_AND_NPN) {
|
||||||
SET_ALPN_PROTOCOLS.invokeWithoutCheckedException(sslSocket, parameters);
|
SET_ALPN_PROTOCOLS.invokeWithoutCheckedException(sslSocket, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tlsExtensionType != null) {
|
||||||
|
SET_NPN_PROTOCOLS.invokeWithoutCheckedException(sslSocket, parameters);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("We can not do TLS handshake on this Android version, please"
|
||||||
|
+ " install the Google Play Services Dynamic Security Provider to use TLS");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSelectedProtocol(SSLSocket socket) {
|
public String getSelectedProtocol(SSLSocket socket) {
|
||||||
if (GET_ALPN_SELECTED_PROTOCOL.isSupported(socket)) {
|
if (tlsExtensionType == TlsExtensionType.ALPN_AND_NPN) {
|
||||||
try {
|
try {
|
||||||
byte[] alpnResult =
|
byte[] alpnResult =
|
||||||
(byte[]) GET_ALPN_SELECTED_PROTOCOL.invokeWithoutCheckedException(socket);
|
(byte[]) GET_ALPN_SELECTED_PROTOCOL.invokeWithoutCheckedException(socket);
|
||||||
|
|
@ -166,7 +219,7 @@ public class OkHttpProtocolNegotiator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GET_NPN_SELECTED_PROTOCOL.isSupported(socket)) {
|
if (tlsExtensionType != null) {
|
||||||
try {
|
try {
|
||||||
byte[] npnResult =
|
byte[] npnResult =
|
||||||
(byte[]) GET_NPN_SELECTED_PROTOCOL.invokeWithoutCheckedException(socket);
|
(byte[]) GET_NPN_SELECTED_PROTOCOL.invokeWithoutCheckedException(socket);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue