diff --git a/okhttp/src/main/java/okhttp3/internal/Platform.java b/okhttp/src/main/java/okhttp3/internal/Platform.java
index c2bd05c660cf..423a6aa9c076 100644
--- a/okhttp/src/main/java/okhttp3/internal/Platform.java
+++ b/okhttp/src/main/java/okhttp3/internal/Platform.java
@@ -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;
@@ -60,6 +61,8 @@
*
* Supported on OpenJDK 7 and 8 (via the JettyALPN-boot library).
*
+ * Supported on OpenJDK 9 via SSLParameters and SSLSocket features.
+ *
*
Trust Manager Extraction
*
* Supported on Android 2.3+ and OpenJDK 7+. There are no public APIs to recover the trust
@@ -125,6 +128,16 @@ public void log(String message) {
System.out.println(message);
}
+ public static List alpnProtocolNames(List protocols) {
+ List 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.
@@ -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";
@@ -275,6 +300,54 @@ public Android(Class> sslParametersClass, OptionalMethod 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 protocols) {
+ try {
+ SSLParameters sslParameters = sslSocket.getSSLParameters();
+
+ List 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.
*/
@@ -296,12 +369,8 @@ public JdkWithJettyBootPlatform(Method putMethod, Method getMethod, Method remov
@Override public void configureTlsExtensions(
SSLSocket sslSocket, String hostname, List protocols) {
- List 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 names = alpnProtocolNames(protocols);
+
try {
Object provider = Proxy.newProxyInstance(Platform.class.getClassLoader(),
new Class[] {clientProviderClass, serverProviderClass}, new JettyNegoProvider(names));