Skip to content

Commit

Permalink
Merge pull request square#2494 from yschimke/alpn_java9_support
Browse files Browse the repository at this point in the history
Java 9 support for ALPN without alpn-boot
  • Loading branch information
swankjesse committed Apr 22, 2016
2 parents bd76ea2 + d8552f0 commit 081c744
Showing 1 changed file with 75 additions and 6 deletions.
81 changes: 75 additions & 6 deletions okhttp/src/main/java/okhttp3/internal/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
Expand Down Expand Up @@ -60,6 +61,8 @@
*
* Supported on OpenJDK 7 and 8 (via the JettyALPN-boot library).
*
* Supported on OpenJDK 9 via SSLParameters and SSLSocket features.
*
* <h3>Trust Manager Extraction</h3>
*
* <p>Supported on Android 2.3+ and OpenJDK 7+. There are no public APIs to recover the trust
Expand Down Expand Up @@ -125,6 +128,16 @@ public void log(String message) {
System.out.println(message);
}

public static List<String> alpnProtocolNames(List<Protocol> protocols) {
List<String> names = new ArrayList<>(protocols.size());
for (int i = 0, size = protocols.size(); i < size; i++) {
Protocol protocol = protocols.get(i);
if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for ALPN.
names.add(protocol.toString());
}
return names;
}

/** Attempt to match the host runtime to a capable Platform implementation. */
private static Platform findPlatform() {
// Attempt to find Android 2.3+ APIs.
Expand Down Expand Up @@ -159,6 +172,18 @@ private static Platform findPlatform() {
// This isn't an Android runtime.
}


// Find JDK 9 new methods
try {
Method setProtocolMethod =
SSLParameters.class.getMethod("setApplicationProtocols", String[].class);
Method getProtocolMethod = SSLSocket.class.getMethod("getApplicationProtocol");

return new Jdk9Platform(setProtocolMethod, getProtocolMethod);
} catch (NoSuchMethodException ignored) {
// pre JDK 9
}

// Find Jetty's ALPN extension for OpenJDK.
try {
String negoClassName = "org.eclipse.jetty.alpn.ALPN";
Expand Down Expand Up @@ -275,6 +300,54 @@ public Android(Class<?> sslParametersClass, OptionalMethod<Socket> setUseSession
}
}

/**
* OpenJDK 9+.
*/
private static final class Jdk9Platform extends Platform {
private final Method setProtocolMethod;
private final Method getProtocolMethod;

public Jdk9Platform(Method setProtocolMethod, Method getProtocolMethod) {
this.setProtocolMethod = setProtocolMethod;
this.getProtocolMethod = getProtocolMethod;
}

@Override
public void configureTlsExtensions(SSLSocket sslSocket, String hostname,
List<Protocol> protocols) {
try {
SSLParameters sslParameters = sslSocket.getSSLParameters();

List<String> names = alpnProtocolNames(protocols);

setProtocolMethod.invoke(sslParameters,
new Object[]{names.toArray(new String[names.size()])});

sslSocket.setSSLParameters(sslParameters);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new AssertionError();
}
}

@Override
public String getSelectedProtocol(SSLSocket socket) {
try {
String protocol = (String) getProtocolMethod.invoke(socket);

// SSLSocket.getApplicationProtocol returns "" if application protocols values will not
// be used. Observed if you didn't specify SSLParameters.setApplicationProtocols
if (protocol == null || protocol.equals("")) {
return null;
}

return protocol;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new AssertionError();
}
}
}


/**
* OpenJDK 7+ with {@code org.mortbay.jetty.alpn/alpn-boot} in the boot class path.
*/
Expand All @@ -296,12 +369,8 @@ public JdkWithJettyBootPlatform(Method putMethod, Method getMethod, Method remov

@Override public void configureTlsExtensions(
SSLSocket sslSocket, String hostname, List<Protocol> protocols) {
List<String> names = new ArrayList<>(protocols.size());
for (int i = 0, size = protocols.size(); i < size; i++) {
Protocol protocol = protocols.get(i);
if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for ALPN.
names.add(protocol.toString());
}
List<String> names = alpnProtocolNames(protocols);

try {
Object provider = Proxy.newProxyInstance(Platform.class.getClassLoader(),
new Class[] {clientProviderClass, serverProviderClass}, new JettyNegoProvider(names));
Expand Down

0 comments on commit 081c744

Please sign in to comment.