diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index 336acd500a2fb..3a3b8ad8b8a08 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -354,7 +354,7 @@ public void setEnabledProtocols(String[] protocols) { public SSLSession getSession() { try { // start handshaking, if failed, the connection will be closed. - ensureNegotiated(); + ensureNegotiated(false); } catch (IOException ioe) { if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { SSLLogger.severe("handshake failed", ioe); @@ -409,6 +409,10 @@ public void removeHandshakeCompletedListener( @Override public void startHandshake() throws IOException { + startHandshake(true); + } + + private void startHandshake(boolean resumable) throws IOException { if (!isConnected) { throw new SocketException("Socket is not connected"); } @@ -437,7 +441,12 @@ public void startHandshake() throws IOException { readHandshakeRecord(); } } catch (InterruptedIOException iioe) { - handleException(iioe); + if(resumable){ + handleException(iioe); + } else{ + throw conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Couldn't kickstart handshaking", iioe); + } } catch (IOException ioe) { throw conContext.fatal(Alert.HANDSHAKE_FAILURE, "Couldn't kickstart handshaking", ioe); @@ -867,7 +876,7 @@ public InputStream getInputStream() throws IOException { } } - private void ensureNegotiated() throws IOException { + private void ensureNegotiated(boolean resumable) throws IOException { if (conContext.isNegotiated || conContext.isBroken || conContext.isInboundClosed() || conContext.isOutboundClosed()) { return; @@ -882,7 +891,7 @@ private void ensureNegotiated() throws IOException { return; } - startHandshake(); + startHandshake(resumable); } finally { handshakeLock.unlock(); } @@ -973,7 +982,7 @@ public int read(byte[] b, int off, int len) throws IOException { if (!conContext.isNegotiated && !conContext.isBroken && !conContext.isInboundClosed() && !conContext.isOutboundClosed()) { - ensureNegotiated(); + ensureNegotiated(true); } // Check if the Socket is invalid (error or closed). @@ -1252,7 +1261,7 @@ public void write(byte[] b, if (!conContext.isNegotiated && !conContext.isBroken && !conContext.isInboundClosed() && !conContext.isOutboundClosed()) { - ensureNegotiated(); + ensureNegotiated(true); } // Check if the Socket is invalid (error or closed). diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SetSoTimeout.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SetSoTimeout.java new file mode 100644 index 0000000000000..148a7712a992f --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SetSoTimeout.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @bug 8246031 + * @summary Socket timeouts for getSession. + * @run main/othervm SetSoTimeout + */ + +import java.io.*; +import java.net.*; +import java.util.*; +import java.security.*; +import javax.net.ssl.*; + +public class SetSoTimeout { + + /* + * ============================================================= + * Set the various variables needed for the tests, then + * specify what tests to run on each side. + */ + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + static boolean separateServerThread = true; + + /* + * Where do we find the keystores? + */ + static String pathToStores = "../../../../javax/net/ssl/etc"; + static String keyStoreFile = "keystore"; + static String trustStoreFile = "truststore"; + static String passwd = "passphrase"; + + /* + * Is the server ready to serve? + */ + volatile static boolean serverReady = false; + + /* + * Turn on SSL debugging? + */ + static boolean debug = false; + + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + SSLServerSocketFactory sslssf = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslssf.createServerSocket(serverPort); + + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal Client, we're ready for his connect. + */ + serverReady = true; + + SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + + //Make server busy. + Thread.sleep(500); + + sslSocket.close(); + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doClientSide() throws Exception { + boolean caught = false; + + /* + * Wait for server to get started. + */ + while (!serverReady) { + Thread.sleep(50); + } + + SSLSocketFactory sslsf = (SSLSocketFactory)SSLSocketFactory.getDefault(); + SSLSocket sslSocket = (SSLSocket)sslsf.createSocket( + InetAddress.getLocalHost(), + serverPort ); + + sslSocket.setSoTimeout(100); // The stall timeout. + + sslSocket.getSession(); + //Check connection state. + if (!sslSocket.isClosed()) { + throw new RuntimeException("Expected connection to be closed"); + } + + sslSocket.close(); + + } + + + // use any free port by default + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + + public static void main(String[] args) throws Exception { + String keyFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile; + String trustFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + trustStoreFile; + + System.setProperty("javax.net.ssl.keyStore", keyFilename); + System.setProperty("javax.net.ssl.keyStorePassword", passwd); + System.setProperty("javax.net.ssl.trustStore", trustFilename); + System.setProperty("javax.net.ssl.trustStorePassword", passwd); + + if (debug) + System.setProperty("javax.net.debug", "all"); + + /* + * Start the tests. + */ + new SetSoTimeout(); + } + + Thread clientThread = null; + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + SetSoTimeout() throws Exception { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + serverThread.join(); + } else { + clientThread.join(); + } + + /* + * When we get here, the test is pretty much over. + * + * If the main thread excepted, that propagates back + * immediately. If the other thread threw an exception, we + * should report back. + */ + if (serverException != null) { + System.out.print("Server Exception:"); + throw serverException; + } + if (clientException != null) { + System.out.print("Client Exception:"); + throw clientException; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..."); + System.err.println(e); + serverReady = true; + serverException = e; + } + } + }; + serverThread.start(); + } else { + doServerSide(); + } + } + + void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..."); + clientException = e; + } + } + }; + clientThread.start(); + } else { + doClientSide(); + } + } +}