diff --git a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java
index 300265cbcd4..2c724f73a67 100644
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java
@@ -27,6 +27,8 @@ package javax.net.ssl;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
+import java.util.List;
+import java.util.function.BiFunction;
/**
@@ -1332,4 +1334,89 @@ public abstract class SSLEngine {
public String getHandshakeApplicationProtocol() {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Registers a callback function that selects an application protocol
+ * value for a SSL/TLS/DTLS handshake.
+ * The function overrides any values set using
+ * {@link SSLParameters#setApplicationProtocols
+ * SSLParameters.setApplicationProtocols} and it supports the following
+ * type parameters:
+ *
+ *
+ * - {@code SSLEngine}
+ *
- The function's first argument allows the current {@code SSLEngine}
+ * to be inspected, including the handshake session and configuration
+ * settings.
+ *
- {@code List}
+ *
- The function's second argument lists the application protocol names
+ * advertised by the TLS peer.
+ *
- {@code String}
+ *
- The function's result is an application protocol name, or null to
+ * indicate that none of the advertised names are acceptable.
+ * If the return value is null (no value chosen) or is a value that
+ * was not advertised by the peer, the underlying protocol will
+ * determine what action to take. (For example, ALPN will send a
+ * "no_application_protocol" alert and terminate the connection.)
+ *
+ *
+ *
+ * For example, the following call registers a callback function that
+ * examines the TLS handshake parameters and selects an application protocol
+ * name:
+ * {@code
+ * serverEngine.setHandshakeApplicationProtocolSelector(
+ * (serverEngine, clientProtocols) -> {
+ * SSLSession session = serverEngine.getHandshakeSession();
+ * return chooseApplicationProtocol(
+ * serverEngine,
+ * clientProtocols,
+ * session.getProtocol(),
+ * session.getCipherSuite());
+ * });
+ * }
+ *
+ * @apiNote
+ * This method should be called by TLS server applications before the TLS
+ * handshake begins. Also, this {@code SSLEngine} should be configured with
+ * parameters that are compatible with the application protocol selected by
+ * the callback function. For example, enabling a poor choice of cipher
+ * suites could result in no suitable application protocol.
+ * See {@link SSLParameters}.
+ *
+ * @implSpec
+ * The implementation in this class throws
+ * {@code UnsupportedOperationException} and performs no other action.
+ *
+ * @param selector the callback function, or null to disable the callback
+ * functionality.
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
+ * @since 9
+ */
+ public void setHandshakeApplicationProtocolSelector(
+ BiFunction, String> selector) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Retrieves the callback function that selects an application protocol
+ * value during a SSL/TLS/DTLS handshake.
+ * See {@link #setHandshakeApplicationProtocolSelector
+ * setHandshakeApplicationProtocolSelector}
+ * for the function's type parameters.
+ *
+ * @implSpec
+ * The implementation in this class throws
+ * {@code UnsupportedOperationException} and performs no other action.
+ *
+ * @return the callback function, or null if none has been set.
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
+ * @since 9
+ */
+ public BiFunction, String>
+ getHandshakeApplicationProtocolSelector() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java
index daaefe08ec7..ebbc9d9eb8d 100644
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java
@@ -28,6 +28,8 @@ package javax.net.ssl;
import java.io.IOException;
import java.net.*;
+import java.util.List;
+import java.util.function.BiFunction;
/**
* This class extends Sockets and provides secure
@@ -742,4 +744,89 @@ public abstract class SSLSocket extends Socket
public String getHandshakeApplicationProtocol() {
throw new UnsupportedOperationException();
}
+
+
+ /**
+ * Registers a callback function that selects an application protocol
+ * value for a SSL/TLS/DTLS handshake.
+ * The function overrides any values set using
+ * {@link SSLParameters#setApplicationProtocols
+ * SSLParameters.setApplicationProtocols} and it supports the following
+ * type parameters:
+ *
+ *
+ * - {@code SSLSocket}
+ *
- The function's first argument allows the current {@code SSLSocket}
+ * to be inspected, including the handshake session and configuration
+ * settings.
+ *
- {@code List}
+ *
- The function's second argument lists the application protocol names
+ * advertised by the TLS peer.
+ *
- {@code String}
+ *
- The function's result is an application protocol name, or null to
+ * indicate that none of the advertised names are acceptable.
+ * If the return value is null (no value chosen) or is a value that
+ * was not advertised by the peer, the underlying protocol will
+ * determine what action to take. (For example, ALPN will send a
+ * "no_application_protocol" alert and terminate the connection.)
+ *
+ *
+ *
+ * For example, the following call registers a callback function that
+ * examines the TLS handshake parameters and selects an application protocol
+ * name:
+ * {@code
+ * serverSocket.setHandshakeApplicationProtocolSelector(
+ * (serverSocket, clientProtocols) -> {
+ * SSLSession session = serverSocket.getHandshakeSession();
+ * return chooseApplicationProtocol(
+ * serverSocket,
+ * clientProtocols,
+ * session.getProtocol(),
+ * session.getCipherSuite());
+ * });
+ * }
+ *
+ * @apiNote
+ * This method should be called by TLS server applications before the TLS
+ * handshake begins. Also, this {@code SSLSocket} should be configured with
+ * parameters that are compatible with the application protocol selected by
+ * the callback function. For example, enabling a poor choice of cipher
+ * suites could result in no suitable application protocol.
+ * See {@link SSLParameters}.
+ *
+ * @implSpec
+ * The implementation in this class throws
+ * {@code UnsupportedOperationException} and performs no other action.
+ *
+ * @param selector the callback function, or null to de-register.
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
+ * @since 9
+ */
+ public void setHandshakeApplicationProtocolSelector(
+ BiFunction, String> selector) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Retrieves the callback function that selects an application protocol
+ * value during a SSL/TLS/DTLS handshake.
+ * See {@link #setHandshakeApplicationProtocolSelector
+ * setHandshakeApplicationProtocolSelector}
+ * for the function's type parameters.
+ *
+ * @implSpec
+ * The implementation in this class throws
+ * {@code UnsupportedOperationException} and performs no other action.
+ *
+ * @return the callback function, or null if none has been set.
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation.
+ * @since 9
+ */
+ public BiFunction, String>
+ getHandshakeApplicationProtocolSelector() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java b/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java
index 2e309dc8bb5..f830bc70ce1 100644
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java
@@ -36,6 +36,7 @@ import java.security.AlgorithmConstraints;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
+import java.util.function.BiFunction;
import javax.crypto.*;
import javax.crypto.spec.*;
@@ -122,6 +123,14 @@ abstract class Handshaker {
// Negotiated ALPN value
String applicationProtocol = null;
+ // Application protocol callback function (for SSLEngine)
+ BiFunction,String>
+ appProtocolSelectorSSLEngine = null;
+
+ // Application protocol callback function (for SSLSocket)
+ BiFunction,String>
+ appProtocolSelectorSSLSocket = null;
+
// The maximum expected network packet size for SSL/TLS/DTLS records.
int maximumPacketSize = 0;
@@ -500,6 +509,22 @@ abstract class Handshaker {
return applicationProtocol;
}
+ /**
+ * Sets the Application Protocol selector function for SSLEngine.
+ */
+ void setApplicationProtocolSelectorSSLEngine(
+ BiFunction,String> selector) {
+ this.appProtocolSelectorSSLEngine = selector;
+ }
+
+ /**
+ * Sets the Application Protocol selector function for SSLSocket.
+ */
+ void setApplicationProtocolSelectorSSLSocket(
+ BiFunction,String> selector) {
+ this.appProtocolSelectorSSLSocket = selector;
+ }
+
/**
* Sets the cipher suites preference.
*/
diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java
index 6aaaf893d80..f9941165771 100644
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java
@@ -27,8 +27,9 @@ package sun.security.ssl;
import java.io.*;
import java.nio.*;
-import java.util.*;
import java.security.*;
+import java.util.*;
+import java.util.function.BiFunction;
import javax.crypto.BadPaddingException;
@@ -206,6 +207,10 @@ public final class SSLEngineImpl extends SSLEngine {
// The value under negotiation will be obtained from handshaker.
String applicationProtocol = null;
+ // Callback function that selects the application protocol value during
+ // the SSL/TLS handshake.
+ BiFunction, String> applicationProtocolSelector;
+
// Have we been told whether we're client or server?
private boolean serverModeSet = false;
private boolean roleIsServer;
@@ -442,6 +447,8 @@ public final class SSLEngineImpl extends SSLEngine {
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
handshaker.setApplicationProtocols(applicationProtocols);
+ handshaker.setApplicationProtocolSelectorSSLEngine(
+ applicationProtocolSelector);
outputRecord.initHandshaker();
}
@@ -2264,6 +2271,21 @@ public final class SSLEngineImpl extends SSLEngine {
return null;
}
+ @Override
+ public synchronized void setHandshakeApplicationProtocolSelector(
+ BiFunction, String> selector) {
+ applicationProtocolSelector = selector;
+ if ((handshaker != null) && !handshaker.activated()) {
+ handshaker.setApplicationProtocolSelectorSSLEngine(selector);
+ }
+ }
+
+ @Override
+ public synchronized BiFunction, String>
+ getHandshakeApplicationProtocolSelector() {
+ return this.applicationProtocolSelector;
+ }
+
/**
* Returns a printable representation of this end of the connection.
*/
diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java
index 880c98a80a4..2e20d538d6e 100644
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java
@@ -37,6 +37,7 @@ import java.security.AlgorithmConstraints;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.BiFunction;
import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
@@ -223,6 +224,10 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl {
// The value under negotiation will be obtained from handshaker.
String applicationProtocol = null;
+ // Callback function that selects the application protocol value during
+ // the SSL/TLS handshake.
+ BiFunction, String> applicationProtocolSelector;
+
/*
* READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
* IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES.
@@ -1370,6 +1375,8 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl {
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
handshaker.setApplicationProtocols(applicationProtocols);
+ handshaker.setApplicationProtocolSelectorSSLSocket(
+ applicationProtocolSelector);
}
/**
@@ -2658,6 +2665,21 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl {
return null;
}
+ @Override
+ public synchronized void setHandshakeApplicationProtocolSelector(
+ BiFunction, String> selector) {
+ applicationProtocolSelector = selector;
+ if ((handshaker != null) && !handshaker.activated()) {
+ handshaker.setApplicationProtocolSelectorSSLSocket(selector);
+ }
+ }
+
+ @Override
+ public synchronized BiFunction, String>
+ getHandshakeApplicationProtocolSelector() {
+ return this.applicationProtocolSelector;
+ }
+
//
// We allocate a separate thread to deliver handshake completion
// events. This ensures that the notifications don't block the
diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
index 17777825f3f..4ad18b163e5 100644
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
@@ -34,6 +34,7 @@ import java.security.cert.*;
import java.security.interfaces.*;
import java.security.spec.ECParameterSpec;
import java.math.BigInteger;
+import java.util.function.BiFunction;
import javax.crypto.SecretKey;
import javax.net.ssl.*;
@@ -532,31 +533,39 @@ final class ServerHandshaker extends Handshaker {
ALPNExtension clientHelloALPN = (ALPNExtension)
mesg.extensions.get(ExtensionType.EXT_ALPN);
- if ((clientHelloALPN != null) && (localApl.length > 0)) {
+ // Use the application protocol callback when provided.
+ // Otherwise use the local list of application protocols.
+ boolean hasAPCallback =
+ ((engine != null && appProtocolSelectorSSLEngine != null) ||
+ (conn != null && appProtocolSelectorSSLSocket != null));
- // Intersect the requested and the locally supported,
- // and save for later.
- String negotiatedValue = null;
- List protocols = clientHelloALPN.getPeerAPs();
+ if (!hasAPCallback) {
+ if ((clientHelloALPN != null) && (localApl.length > 0)) {
- // Use server preference order
- for (String ap : localApl) {
- if (protocols.contains(ap)) {
- negotiatedValue = ap;
- break;
+ // Intersect the requested and the locally supported,
+ // and save for later.
+ String negotiatedValue = null;
+ List protocols = clientHelloALPN.getPeerAPs();
+
+ // Use server preference order
+ for (String ap : localApl) {
+ if (protocols.contains(ap)) {
+ negotiatedValue = ap;
+ break;
+ }
}
- }
- if (negotiatedValue == null) {
- fatalSE(Alerts.alert_no_application_protocol,
- new SSLHandshakeException(
- "No matching ALPN values"));
- }
- applicationProtocol = negotiatedValue;
+ if (negotiatedValue == null) {
+ fatalSE(Alerts.alert_no_application_protocol,
+ new SSLHandshakeException(
+ "No matching ALPN values"));
+ }
+ applicationProtocol = negotiatedValue;
- } else {
- applicationProtocol = "";
- }
+ } else {
+ applicationProtocol = "";
+ }
+ } // Otherwise, applicationProtocol will be set by the callback.
session = null; // forget about the current session
//
@@ -892,8 +901,36 @@ final class ServerHandshaker extends Handshaker {
}
// Prepare the ALPN response
- if (applicationProtocol != null && !applicationProtocol.isEmpty()) {
- m1.extensions.add(new ALPNExtension(applicationProtocol));
+ if (clientHelloALPN != null) {
+ List peerAPs = clientHelloALPN.getPeerAPs();
+
+ // check for a callback function
+ if (hasAPCallback) {
+ if (conn != null) {
+ applicationProtocol =
+ appProtocolSelectorSSLSocket.apply(conn, peerAPs);
+ } else {
+ applicationProtocol =
+ appProtocolSelectorSSLEngine.apply(engine, peerAPs);
+ }
+ }
+
+ // check for no-match and that the selected name was also proposed
+ // by the TLS peer
+ if (applicationProtocol == null ||
+ (!applicationProtocol.isEmpty() &&
+ !peerAPs.contains(applicationProtocol))) {
+
+ fatalSE(Alerts.alert_no_application_protocol,
+ new SSLHandshakeException(
+ "No matching ALPN values"));
+
+ } else if (!applicationProtocol.isEmpty()) {
+ m1.extensions.add(new ALPNExtension(applicationProtocol));
+ }
+ } else {
+ // Nothing was negotiated, returned at end of the handshake
+ applicationProtocol = "";
}
if (debug != null && Debug.isOn("handshake")) {
diff --git a/jdk/test/javax/net/ssl/ALPN/MyX509ExtendedKeyManager.java b/jdk/test/javax/net/ssl/ALPN/MyX509ExtendedKeyManager.java
index d457766d5d4..ecd7dcfc4eb 100644
--- a/jdk/test/javax/net/ssl/ALPN/MyX509ExtendedKeyManager.java
+++ b/jdk/test/javax/net/ssl/ALPN/MyX509ExtendedKeyManager.java
@@ -34,15 +34,17 @@ public class MyX509ExtendedKeyManager extends X509ExtendedKeyManager {
static final String ERROR = "ERROR";
X509ExtendedKeyManager akm;
String expectedAP;
+ boolean doCheck = true;
MyX509ExtendedKeyManager(X509ExtendedKeyManager akm) {
this.akm = akm;
}
public MyX509ExtendedKeyManager(
- X509ExtendedKeyManager akm, String expectedAP) {
+ X509ExtendedKeyManager akm, String expectedAP, boolean doCheck) {
this.akm = akm;
this.expectedAP = expectedAP;
+ this.doCheck = doCheck;
}
@@ -104,6 +106,12 @@ public class MyX509ExtendedKeyManager extends X509ExtendedKeyManager {
private void checkALPN(String ap) {
+ if (!doCheck) {
+ System.out.println("Skipping KeyManager checks " +
+ "because a callback has been registered");
+ return;
+ }
+
if (ERROR.equals(expectedAP)) {
throw new RuntimeException("Should not reach here");
}
diff --git a/jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java b/jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java
index de4ea6cc0b6..b17da745841 100644
--- a/jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java
+++ b/jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java
@@ -26,23 +26,53 @@
/*
* @test
- * @bug 8051498 8145849
+ * @bug 8051498 8145849 8170282
* @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension
* @compile MyX509ExtendedKeyManager.java
- * @run main/othervm SSLEngineAlpnTest h2 h2 h2
- * @run main/othervm SSLEngineAlpnTest h2 h2,http/1.1 h2
- * @run main/othervm SSLEngineAlpnTest h2,http/1.1 h2,http/1.1 h2
- * @run main/othervm SSLEngineAlpnTest http/1.1,h2 h2,http/1.1 http/1.1
- * @run main/othervm SSLEngineAlpnTest h4,h3,h2 h1,h2 h2
- * @run main/othervm SSLEngineAlpnTest EMPTY h2,http/1.1 NONE
- * @run main/othervm SSLEngineAlpnTest h2 EMPTY NONE
- * @run main/othervm SSLEngineAlpnTest H2 h2 ERROR
- * @run main/othervm SSLEngineAlpnTest h2 http/1.1 ERROR
+ *
+ * @run main/othervm SSLEngineAlpnTest h2 UNUSED h2 h2
+ * @run main/othervm SSLEngineAlpnTest h2 UNUSED h2,http/1.1 h2
+ * @run main/othervm SSLEngineAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h2
+ * @run main/othervm SSLEngineAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.1
+ * @run main/othervm SSLEngineAlpnTest h4,h3,h2 UNUSED h1,h2 h2
+ * @run main/othervm SSLEngineAlpnTest EMPTY UNUSED h2,http/1.1 NONE
+ * @run main/othervm SSLEngineAlpnTest h2 UNUSED EMPTY NONE
+ * @run main/othervm SSLEngineAlpnTest H2 UNUSED h2 ERROR
+ * @run main/othervm SSLEngineAlpnTest h2 UNUSED http/1.1 ERROR
+ *
+ * @run main/othervm SSLEngineAlpnTest UNUSED h2 h2 h2
+ * @run main/othervm SSLEngineAlpnTest UNUSED h2 h2,http/1.1 h2
+ * @run main/othervm SSLEngineAlpnTest UNUSED h2 http/1.1,h2 h2
+ * @run main/othervm SSLEngineAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.1
+ * @run main/othervm SSLEngineAlpnTest UNUSED EMPTY h2,http/1.1 NONE
+ * @run main/othervm SSLEngineAlpnTest UNUSED h2 EMPTY NONE
+ * @run main/othervm SSLEngineAlpnTest UNUSED H2 h2 ERROR
+ * @run main/othervm SSLEngineAlpnTest UNUSED h2 http/1.1 ERROR
+ *
+ * @run main/othervm SSLEngineAlpnTest h2 h2 h2 h2
+ * @run main/othervm SSLEngineAlpnTest H2 h2 h2,http/1.1 h2
+ * @run main/othervm SSLEngineAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.1
+ * @run main/othervm SSLEngineAlpnTest http/1.1,h2 h2 h2,http/1.1 h2
+ * @run main/othervm SSLEngineAlpnTest EMPTY h2 h2 h2
+ * @run main/othervm SSLEngineAlpnTest h2,http/1.1 EMPTY http/1.1 NONE
+ * @run main/othervm SSLEngineAlpnTest h2,http/1.1 h2 EMPTY NONE
+ * @run main/othervm SSLEngineAlpnTest UNUSED UNUSED http/1.1,h2 NONE
+ * @run main/othervm SSLEngineAlpnTest h2 h2 http/1.1 ERROR
+ * @run main/othervm SSLEngineAlpnTest h2,http/1.1 H2 http/1.1 ERROR
*/
/**
* A simple SSLEngine-based client/server that demonstrates the proposed API
* changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).
*
+ * Usage:
+ * java SSLEngineAlpnTest
+ *
+ * where:
+ * EMPTY indicates that ALPN is disabled
+ * UNUSED indicates that no ALPN values are supplied (server-side only)
+ * ERROR indicates that an exception is expected
+ * NONE indicates that no ALPN is expected
+ *
* This example is based on our standard SSLEngineTemplate.
*
* The immediate consumer of ALPN will be HTTP/2 (RFC 7540), aka H2. The H2 IETF
@@ -98,6 +128,7 @@ import javax.net.ssl.SSLEngineResult.*;
import java.io.*;
import java.security.*;
import java.nio.*;
+import java.util.Arrays;
public class SSLEngineAlpnTest {
@@ -117,6 +148,9 @@ public class SSLEngineAlpnTest {
*/
private static final boolean debug = false;
+ private static boolean hasServerAPs; // whether server APs are present
+ private static boolean hasCallback; // whether a callback is present
+
private final SSLContext sslc;
private SSLEngine clientEngine; // client Engine
@@ -157,17 +191,21 @@ public class SSLEngineAlpnTest {
if (debug) {
System.setProperty("javax.net.debug", "all");
}
+ System.out.println("Test args: " + Arrays.toString(args));
// Validate parameters
- if (args.length != 3) {
+ if (args.length != 4) {
throw new Exception("Invalid number of test parameters");
}
- SSLEngineAlpnTest test = new SSLEngineAlpnTest(args[2]);
+ hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?
+ hasCallback = !args[1].equals("UNUSED"); // is callback being used?
+
+ SSLEngineAlpnTest test = new SSLEngineAlpnTest(args[3]);
try {
- test.runTest(convert(args[0]), convert(args[1]), args[2]);
+ test.runTest(convert(args[0]), args[1], convert(args[2]), args[3]);
} catch (SSLHandshakeException she) {
- if (args[2].equals("ERROR")) {
+ if (args[3].equals("ERROR")) {
System.out.println("Caught the expected exception: " + she);
} else {
throw she;
@@ -199,7 +237,8 @@ public class SSLEngineAlpnTest {
}
kms = new KeyManager[] { new MyX509ExtendedKeyManager(
- (X509ExtendedKeyManager) kms[0], expectedAP) };
+ (X509ExtendedKeyManager) kms[0], expectedAP,
+ !hasCallback && hasServerAPs) };
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
@@ -215,12 +254,15 @@ public class SSLEngineAlpnTest {
* Convert a comma-separated list into an array of strings.
*/
private static String[] convert(String list) {
- String[] strings = null;
+ if (list.equals("UNUSED")) {
+ return null;
+ }
if (list.equals("EMPTY")) {
return new String[0];
}
+ String[] strings;
if (list.indexOf(',') > 0) {
strings = list.split(",");
} else {
@@ -247,12 +289,12 @@ public class SSLEngineAlpnTest {
* One could easily separate these phases into separate
* sections of code.
*/
- private void runTest(String[] serverAPs, String[] clientAPs,
- String expectedAP) throws Exception {
+ private void runTest(String[] serverAPs, String callbackAP,
+ String[] clientAPs, String expectedAP) throws Exception {
boolean dataDone = false;
- createSSLEngines(serverAPs, clientAPs);
+ createSSLEngines(serverAPs, callbackAP, clientAPs);
createBuffers();
SSLEngineResult clientResult; // results from client's last operation
@@ -364,8 +406,8 @@ public class SSLEngineAlpnTest {
* Using the SSLContext created during object creation,
* create/configure the SSLEngines we'll use for this test.
*/
- private void createSSLEngines(String[] serverAPs, String[] clientAPs)
- throws Exception {
+ private void createSSLEngines(String[] serverAPs, String callbackAP,
+ String[] clientAPs) throws Exception {
/*
* Configure the serverEngine to act as a server in the SSL/TLS
* handshake. Also, require SSL client authentication.
@@ -385,18 +427,42 @@ public class SSLEngineAlpnTest {
*/
String[] suites = sslp.getCipherSuites();
sslp.setCipherSuites(suites);
- sslp.setApplicationProtocols(serverAPs);
+ if (serverAPs != null) {
+ sslp.setApplicationProtocols(serverAPs);
+ }
sslp.setUseCipherSuitesOrder(true); // Set server side order
serverEngine.setSSLParameters(sslp);
+ // check that no callback has been registered
+ if (serverEngine.getHandshakeApplicationProtocolSelector() != null) {
+ throw new Exception("getHandshakeApplicationProtocolSelector() " +
+ "should return null");
+ }
+
+ if (hasCallback) {
+ serverEngine.setHandshakeApplicationProtocolSelector(
+ (sslEngine, clientProtocols) -> {
+ return callbackAP.equals("EMPTY") ? "" : callbackAP;
+ });
+
+ // check that the callback can be retrieved
+ if (serverEngine.getHandshakeApplicationProtocolSelector()
+ == null) {
+ throw new Exception("getHandshakeApplicationProtocolSelector()"
+ + " should return non-null");
+ }
+ }
+
/*
* Similar to above, but using client mode instead.
*/
clientEngine = sslc.createSSLEngine("client", 80);
clientEngine.setUseClientMode(true);
sslp = clientEngine.getSSLParameters();
- sslp.setApplicationProtocols(clientAPs);
+ if (clientAPs != null) {
+ sslp.setApplicationProtocols(clientAPs);
+ }
clientEngine.setSSLParameters(sslp);
if ((clientEngine.getHandshakeApplicationProtocol() != null) ||
diff --git a/jdk/test/javax/net/ssl/ALPN/SSLServerSocketAlpnTest.java b/jdk/test/javax/net/ssl/ALPN/SSLServerSocketAlpnTest.java
index a310cbb0336..a9373ed09f9 100644
--- a/jdk/test/javax/net/ssl/ALPN/SSLServerSocketAlpnTest.java
+++ b/jdk/test/javax/net/ssl/ALPN/SSLServerSocketAlpnTest.java
@@ -26,22 +26,61 @@
/*
* @test
- * @bug 8051498 8145849 8158978
+ * @bug 8051498 8145849 8158978 8170282
* @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension
* @compile MyX509ExtendedKeyManager.java
- * @run main/othervm SSLServerSocketAlpnTest h2 h2 h2
- * @run main/othervm SSLServerSocketAlpnTest h2 h2,http/1.1 h2
- * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 h2,http/1.1 h2
- * @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 h2,http/1.1 http/1.1
- * @run main/othervm SSLServerSocketAlpnTest h4,h3,h2 h1,h2 h2
- * @run main/othervm SSLServerSocketAlpnTest EMPTY h2,http/1.1 NONE
- * @run main/othervm SSLServerSocketAlpnTest h2 EMPTY NONE
- * @run main/othervm SSLServerSocketAlpnTest H2 h2 ERROR
- * @run main/othervm SSLServerSocketAlpnTest h2 http/1.1 ERROR
+ *
+ * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED h2 h2
+ * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED h2,http/1.1 h2
+ * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h2
+ * @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.1
+ * @run main/othervm SSLServerSocketAlpnTest h4,h3,h2 UNUSED h1,h2 h2
+ * @run main/othervm SSLServerSocketAlpnTest EMPTY UNUSED h2,http/1.1 NONE
+ * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED EMPTY NONE
+ * @run main/othervm SSLServerSocketAlpnTest H2 UNUSED h2 ERROR
+ * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED http/1.1 ERROR
+ *
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 h2 h2
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 h2,http/1.1 h2
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 http/1.1,h2 h2
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.1
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED EMPTY h2,http/1.1 NONE
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 EMPTY NONE
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED H2 h2 ERROR
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 http/1.1 ERROR
+ *
+ * @run main/othervm SSLServerSocketAlpnTest h2 h2 h2 h2
+ * @run main/othervm SSLServerSocketAlpnTest H2 h2 h2,http/1.1 h2
+ * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.1
+ * @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 h2 h2,http/1.1 h2
+ * @run main/othervm SSLServerSocketAlpnTest EMPTY h2 h2 h2
+ * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 EMPTY http/1.1 NONE
+ * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 h2 EMPTY NONE
+ * @run main/othervm SSLServerSocketAlpnTest UNUSED UNUSED http/1.1,h2 NONE
+ * @run main/othervm SSLServerSocketAlpnTest h2 h2 http/1.1 ERROR
+ * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 H2 http/1.1 ERROR
+ *
* @author Brad Wetmore
*/
+/**
+ * A simple SSLSocket-based client/server that demonstrates the proposed API
+ * changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).
+ *
+ * Usage:
+ * java SSLServerSocketAlpnTest
+ *
+ *
+ * where:
+ * EMPTY indicates that ALPN is disabled
+ * UNUSED indicates that no ALPN values are supplied (server-side only)
+ * ERROR indicates that an exception is expected
+ * NONE indicates that no ALPN is expected
+ *
+ * This example is based on our standard SSLSocketTemplate.
+ */
import java.io.*;
import java.security.KeyStore;
+import java.util.Arrays;
import javax.net.ssl.*;
@@ -73,6 +112,9 @@ public class SSLServerSocketAlpnTest {
static String trustFilename = System.getProperty("test.src", ".") + "/"
+ pathToStores + "/" + trustStoreFile;
+ private static boolean hasServerAPs; // whether server APs are present
+ private static boolean hasCallback; // whether a callback is present
+
/*
* SSLContext
*/
@@ -89,6 +131,7 @@ public class SSLServerSocketAlpnTest {
static boolean debug = false;
static String[] serverAPs;
+ static String callbackAP;
static String[] clientAPs;
static String expectedAP;
@@ -129,7 +172,9 @@ public class SSLServerSocketAlpnTest {
sslp.setUseCipherSuitesOrder(true); // Set server side order
// Set the ALPN selection.
- sslp.setApplicationProtocols(serverAPs);
+ if (serverAPs != null) {
+ sslp.setApplicationProtocols(serverAPs);
+ }
sslServerSocket.setSSLParameters(sslp);
serverPort = sslServerSocket.getLocalPort();
@@ -146,6 +191,25 @@ public class SSLServerSocketAlpnTest {
+ "return null before the handshake starts");
}
+ // check that no callback has been registered
+ if (sslSocket.getHandshakeApplicationProtocolSelector() != null) {
+ throw new Exception("getHandshakeApplicationProtocolSelector() " +
+ "should return null");
+ }
+
+ if (hasCallback) {
+ sslSocket.setHandshakeApplicationProtocolSelector(
+ (serverSocket, clientProtocols) -> {
+ return callbackAP.equals("EMPTY") ? "" : callbackAP;
+ });
+
+ // check that the callback can be retrieved
+ if (sslSocket.getHandshakeApplicationProtocolSelector() == null) {
+ throw new Exception("getHandshakeApplicationProtocolSelector()"
+ + " should return non-null");
+ }
+ }
+
sslSocket.startHandshake();
if (sslSocket.getHandshakeApplicationProtocol() != null) {
@@ -276,14 +340,19 @@ public class SSLServerSocketAlpnTest {
if (debug) {
System.setProperty("javax.net.debug", "all");
}
+ System.out.println("Test args: " + Arrays.toString(args));
// Validate parameters
- if (args.length != 3) {
+ if (args.length != 4) {
throw new Exception("Invalid number of test parameters");
}
serverAPs = convert(args[0]);
- clientAPs = convert(args[1]);
- expectedAP = args[2];
+ callbackAP = args[1];
+ clientAPs = convert(args[2]);
+ expectedAP = args[3];
+
+ hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?
+ hasCallback = !callbackAP.equals("UNUSED"); // is callback being used?
/*
* Start the tests.
@@ -291,7 +360,7 @@ public class SSLServerSocketAlpnTest {
try {
new SSLServerSocketAlpnTest();
} catch (SSLHandshakeException she) {
- if (args[2].equals("ERROR")) {
+ if (args[3].equals("ERROR")) {
System.out.println("Caught the expected exception: " + she);
} else {
throw she;
@@ -322,7 +391,8 @@ public class SSLServerSocketAlpnTest {
}
kms = new KeyManager[] { new MyX509ExtendedKeyManager(
- (X509ExtendedKeyManager) kms[0], expectedAP) };
+ (X509ExtendedKeyManager) kms[0], expectedAP,
+ !hasCallback && hasServerAPs) };
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustKS);
@@ -338,12 +408,15 @@ public class SSLServerSocketAlpnTest {
* Convert a comma-separated list into an array of strings.
*/
private static String[] convert(String list) {
- String[] strings;
+ if (list.equals("UNUSED")) {
+ return null;
+ }
if (list.equals("EMPTY")) {
return new String[0];
}
+ String[] strings;
if (list.indexOf(',') > 0) {
strings = list.split(",");
} else {
diff --git a/jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.java b/jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.java
index fd52f251f9d..ef72474f417 100644
--- a/jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.java
+++ b/jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.java
@@ -26,22 +26,60 @@
/*
* @test
- * @bug 8051498 8145849
+ * @bug 8051498 8145849 8170282
* @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension
* @compile MyX509ExtendedKeyManager.java
- * @run main/othervm SSLSocketAlpnTest h2 h2 h2
- * @run main/othervm SSLSocketAlpnTest h2 h2,http/1.1 h2
- * @run main/othervm SSLSocketAlpnTest h2,http/1.1 h2,http/1.1 h2
- * @run main/othervm SSLSocketAlpnTest http/1.1,h2 h2,http/1.1 http/1.1
- * @run main/othervm SSLSocketAlpnTest h4,h3,h2 h1,h2 h2
- * @run main/othervm SSLSocketAlpnTest EMPTY h2,http/1.1 NONE
- * @run main/othervm SSLSocketAlpnTest h2 EMPTY NONE
- * @run main/othervm SSLSocketAlpnTest H2 h2 ERROR
- * @run main/othervm SSLSocketAlpnTest h2 http/1.1 ERROR
+ *
+ * @run main/othervm SSLSocketAlpnTest h2 UNUSED h2 h2
+ * @run main/othervm SSLSocketAlpnTest h2 UNUSED h2,http/1.1 h2
+ * @run main/othervm SSLSocketAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h2
+ * @run main/othervm SSLSocketAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.1
+ * @run main/othervm SSLSocketAlpnTest h4,h3,h2 UNUSED h1,h2 h2
+ * @run main/othervm SSLSocketAlpnTest EMPTY UNUSED h2,http/1.1 NONE
+ * @run main/othervm SSLSocketAlpnTest h2 UNUSED EMPTY NONE
+ * @run main/othervm SSLSocketAlpnTest H2 UNUSED h2 ERROR
+ * @run main/othervm SSLSocketAlpnTest h2 UNUSED http/1.1 ERROR
+ *
+ * @run main/othervm SSLSocketAlpnTest UNUSED h2 h2 h2
+ * @run main/othervm SSLSocketAlpnTest UNUSED h2 h2,http/1.1 h2
+ * @run main/othervm SSLSocketAlpnTest UNUSED h2 http/1.1,h2 h2
+ * @run main/othervm SSLSocketAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.1
+ * @run main/othervm SSLSocketAlpnTest UNUSED EMPTY h2,http/1.1 NONE
+ * @run main/othervm SSLSocketAlpnTest UNUSED h2 EMPTY NONE
+ * @run main/othervm SSLSocketAlpnTest UNUSED H2 h2 ERROR
+ * @run main/othervm SSLSocketAlpnTest UNUSED h2 http/1.1 ERROR
+ *
+ * @run main/othervm SSLSocketAlpnTest h2 h2 h2 h2
+ * @run main/othervm SSLSocketAlpnTest H2 h2 h2,http/1.1 h2
+ * @run main/othervm SSLSocketAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.1
+ * @run main/othervm SSLSocketAlpnTest http/1.1,h2 h2 h2,http/1.1 h2
+ * @run main/othervm SSLSocketAlpnTest EMPTY h2 h2 h2
+ * @run main/othervm SSLSocketAlpnTest h2,http/1.1 EMPTY http/1.1 NONE
+ * @run main/othervm SSLSocketAlpnTest h2,http/1.1 h2 EMPTY NONE
+ * @run main/othervm SSLSocketAlpnTest UNUSED UNUSED http/1.1,h2 NONE
+ * @run main/othervm SSLSocketAlpnTest h2 h2 http/1.1 ERROR
+ * @run main/othervm SSLSocketAlpnTest h2,http/1.1 H2 http/1.1 ERROR
+ *
* @author Brad Wetmore
*/
+/**
+ * A simple SSLSocket-based client/server that demonstrates the proposed API
+ * changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).
+ *
+ * Usage:
+ * java SSLSocketAlpnTest
+ *
+ * where:
+ * EMPTY indicates that ALPN is disabled
+ * UNUSED indicates that no ALPN values are supplied (server-side only)
+ * ERROR indicates that an exception is expected
+ * NONE indicates that no ALPN is expected
+ *
+ * This example is based on our standard SSLSocketTemplate.
+ */
import java.io.*;
import java.security.KeyStore;
+import java.util.Arrays;
import javax.net.ssl.*;
@@ -73,6 +111,9 @@ public class SSLSocketAlpnTest {
static String trustFilename = System.getProperty("test.src", ".") + "/"
+ pathToStores + "/" + trustStoreFile;
+ private static boolean hasServerAPs; // whether server APs are present
+ private static boolean hasCallback; // whether a callback is present
+
/*
* SSLContext
*/
@@ -89,6 +130,7 @@ public class SSLSocketAlpnTest {
static boolean debug = false;
static String[] serverAPs;
+ static String callbackAP;
static String[] clientAPs;
static String expectedAP;
@@ -136,7 +178,9 @@ public class SSLSocketAlpnTest {
sslp.setUseCipherSuitesOrder(true); // Set server side order
// Set the ALPN selection.
- sslp.setApplicationProtocols(serverAPs);
+ if (serverAPs != null) {
+ sslp.setApplicationProtocols(serverAPs);
+ }
sslSocket.setSSLParameters(sslp);
if (sslSocket.getHandshakeApplicationProtocol() != null) {
@@ -144,6 +188,24 @@ public class SSLSocketAlpnTest {
+ "return null before the handshake starts");
}
+ // check that no callback has been registered
+ if (sslSocket.getHandshakeApplicationProtocolSelector() != null) {
+ throw new Exception("getHandshakeApplicationProtocolSelector() " +
+ "should return null");
+ }
+
+ if (hasCallback) {
+ sslSocket.setHandshakeApplicationProtocolSelector(
+ (serverSocket, clientProtocols) -> {
+ return callbackAP.equals("EMPTY") ? "" : callbackAP;
+ });
+
+ // check that the callback can be retrieved
+ if (sslSocket.getHandshakeApplicationProtocolSelector() == null) {
+ throw new Exception("getHandshakeApplicationProtocolSelector()" + " should return non-null");
+ }
+ }
+
sslSocket.startHandshake();
if (sslSocket.getHandshakeApplicationProtocol() != null) {
@@ -274,14 +336,19 @@ public class SSLSocketAlpnTest {
if (debug) {
System.setProperty("javax.net.debug", "all");
}
+ System.out.println("Test args: " + Arrays.toString(args));
// Validate parameters
- if (args.length != 3) {
+ if (args.length != 4) {
throw new Exception("Invalid number of test parameters");
}
serverAPs = convert(args[0]);
- clientAPs = convert(args[1]);
- expectedAP = args[2];
+ callbackAP = args[1];
+ clientAPs = convert(args[2]);
+ expectedAP = args[3];
+
+ hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?
+ hasCallback = !callbackAP.equals("UNUSED"); // is callback being used?
/*
* Start the tests.
@@ -289,7 +356,7 @@ public class SSLSocketAlpnTest {
try {
new SSLSocketAlpnTest();
} catch (SSLHandshakeException she) {
- if (args[2].equals("ERROR")) {
+ if (args[3].equals("ERROR")) {
System.out.println("Caught the expected exception: " + she);
} else {
throw she;
@@ -320,7 +387,8 @@ public class SSLSocketAlpnTest {
}
kms = new KeyManager[] { new MyX509ExtendedKeyManager(
- (X509ExtendedKeyManager) kms[0], expectedAP) };
+ (X509ExtendedKeyManager) kms[0], expectedAP,
+ !hasCallback && hasServerAPs) };
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustKS);
@@ -336,12 +404,15 @@ public class SSLSocketAlpnTest {
* Convert a comma-separated list into an array of strings.
*/
private static String[] convert(String list) {
- String[] strings;
+ if (list.equals("UNUSED")) {
+ return null;
+ }
if (list.equals("EMPTY")) {
return new String[0];
}
+ String[] strings;
if (list.indexOf(',') > 0) {
strings = list.split(",");
} else {