Hello Severin, Thank you for spending time on this. Although that JIRA was raised for in context of MySQL driver, having watched this discussion and looked a bit into the exception stacktrace, I think it's not really specific to MySQL.
So I decided to reproduce this with just plain Java SSLSocket APIs and was able to reproduce this. I have attached a very simple jtreg test case which fails against the latest upstream jdk source repo. The test case has details about what it does, but in short, the test creates a SSLServerSocket (server) and a SSLSocket (client) and the client sends some random data to the server and then calls SSLSocket.shutdownInput(). That call triggers the exception that's being discussed here. Reading the javadoc of SSLSocket.shutdownInput() I don't think the usage of this API is incorrect (this is of course based on my limited knowledge of that API). -Jaikiran On 24/01/19 10:31 PM, Severin Gehwolf wrote: > Hi, > > Thanks for your feedback! > > I've tried to reproduce this on my end too, but failed. At least with > MariaDB 10.2.21 and their recent jdbc driver and JDK 11. > > This looks like a JDBC driver issue on newer JDKs. Hence, I've noted > that in the bug and closed it: > https://bugs.openjdk.java.net/browse/JDK-8215102 > > If somebody manages to reproduce this with recent JDBC drivers I'd be > happy to re-open it. mysql-connector-java-5.1.43.jar seems rather old. > Current is 8.0.14. > > Thanks, > Severin > > On Tue, 2019-01-22 at 18:14 +0530, Jaikiran Pai wrote: >> FWIW - I don't think this is related to WildFly server. So I decided >> to try and reproduce this in a trivial standalone program and I was >> able to reproduce this. Here's the code to reproduce this issue: >> >> import java.sql.*; >> >> public class ConnectionCloseTest { >> >> public static void main(final String[] args) throws Exception { >> final String url = "jdbc:mysql://localhost/?requireSSL=true"; >> final String user = "youruser"; // set the right user >> final String pass = "yourpassword"; // set the right password >> Class.forName("com.mysql.jdbc.Driver"); >> final Connection conn = DriverManager.getConnection(url, >> user, pass); >> System.out.println("Got connection"); >> conn.close(); >> System.out.println("Closed connection"); >> } >> >> } >> It's important to start the MySQL server with ssl enabled. For that I >> just had to set: >> [mysqld] >> ssl=1 >> in my MySQL server configuration. On the client side you will need >> the mysql JDBC driver jar in the classpath. The one I used for this >> test was mysql-connector-java-5.1.43.jar. >> Running this with Java 8 doesn't throw any exceptions or WARN logs. >> However, running it against Java 11 and even Java 12 latest EA build, >> throws an exception, which gets logged as a WARN by the driver (and >> things move on) on connection close: >> >> WARN: Caught while disconnecting... >> >> EXCEPTION STACK TRACE: >> >> >> >> ** BEGIN NESTED EXCEPTION ** >> >> javax.net.ssl.SSLException >> MESSAGE: closing inbound before receiving peer's close_notify >> >> STACKTRACE: >> >> javax.net.ssl.SSLException: closing inbound before receiving peer's >> close_notify >> at >> java.base/sun.security.ssl.Alert.createSSLException(Alert.java:133) >> at >> java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) >> at >> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.ja >> va:307) >> at >> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.ja >> va:263) >> at >> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.ja >> va:254) >> at >> java.base/sun.security.ssl.SSLSocketImpl.shutdownInput(SSLSocketImpl. >> java:650) >> at >> java.base/sun.security.ssl.SSLSocketImpl.shutdownInput(SSLSocketImpl. >> java:629) >> at com.mysql.jdbc.MysqlIO.quit(MysqlIO.java:2246) >> at >> com.mysql.jdbc.ConnectionImpl.realClose(ConnectionImpl.java:4234) >> at com.mysql.jdbc.ConnectionImpl.close(ConnectionImpl.java:1472) >> at ConnectionCloseTest.main(ConnectionCloseTest.java:13) >> at >> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Nativ >> e Method) >> at >> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Native >> MethodAccessorImpl.java:62) >> at >> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(De >> legatingMethodAccessorImpl.java:43) >> at java.base/java.lang.reflect.Method.invoke(Method.java:567) >> at >> jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:415) >> at >> jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:192) >> at >> jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:132) >> >> >> ** END NESTED EXCEPTION ** >> -Jaikiran >> >> On 22/01/19 7:43 AM, Dennis Gesker wrote: >>> Hi Severing: >>> >>> I'll post the generic error when I get to the office. It seems to >>> throw the complaints when it closes a connection. >>> >>> Here is the thing... >>> >>> 1. I'm glad this found its way to you (being Red Hat guy) as we >>> first noticed it in WildFly 15.0.1. (But, wasn't looking for it >>> before as we only need it for a few XA migration transactions.) >>> >>> 2. It MIGHT be the driver as we use PostgreSQL driver mostly -- in >>> the same container -- and no errors there on WildFly 15.0.1 and JDK >>> 11. >>> >>> 3. I will also try to fall back to JDK 8 and see if it continues in >>> WildFly 15.0.1. >>> >>> 4. The error occurs -- it would seem -- as the pool closes idle >>> connections. >>> >>> 5. I'll post the pool/data source config in WildFly as well -- it >>> seems correct and seems to work OK in my application. >>> >>> Oh, yeah... >>> >>> And, I found the form to be a contributor (not comitter) and will >>> fill that out tomorrow as well and submit it to you directly. >>> >>> --drg >>> >>> On Mon, Jan 21, 2019, 09:23 Severin Gehwolf <sgehw...@redhat.com >>> wrote: >>>> On Mon, 2019-01-21 at 07:57 -0700, Dennis Gesker wrote: >>>>> Pasted: >>>>> >>>>> https://paste.fedoraproject.org/paste/vEvp9RwN2rVvIKGiC0IvEQ >>>> Is this the full trace? I don't see any exceptions happening in >>>> the >>>> log. Am I missing something? >>>> >>>> Thanks, >>>> Severin >>>> >>>>> On Mon, Jan 21, 2019 at 2:10 AM Severin Gehwolf < >>>> sgehw...@redhat.com> >>>>> wrote: >>>>>> Hi Dennis, >>>>>> >>>>>> On Sat, 2019-01-19 at 12:08 -0700, Dennis Gesker wrote: >>>>>>> Hi Severin: >>>>>>> >>>>>>> A link to the txt file via Google Drive his here. >>>>>> "Sorry, the file you have requested does not exist." >>>>>> >>>>>> Could you please upload it somewhere less restricted? Maybe >>>>>> https://paste.fedoraproject.org/ or something similar? >>>>>> >>>>>> I guess if you include me directly, a file attachment would >>>> work >>>>>> too... >>>>>> It's the mailing lists which strip attachments. >>>>>> >>>>>>> I appreciate you and Alan taking a look. Especially, >>>> information >>>>>>> submitted from someone who is not a part of openjdk >>>> project. >>>>>>> I do hope the debug info helps. Let me know anything else >>>> you >>>>>> need >>>>>>> and I will do my best to provide it. >>>>>> Sure. I'll be mostly doing the intermediary work: getting the >>>> info >>>>>> added to the bug, though. >>>>>> >>>>>>> And, should your team decide to open a new ticket or reopen >>>> this >>>>>>> original ticket in the JIRA could you add me to the ticket? >>>>>> You'd have to become OpenJDK author for this[1]. It's not >>>> terribly >>>>>> difficult, but it's somewhat of an entry barrier I >>>> understand. >>>>>>> BTW, (off topic), would your recommend submitting a >>>> contributor >>>>>>> application to the openjdk project so bug reports can be >>>>>> submitted >>>>>>> directly? >>>>>> If you intend to submit the occasional bug report and fix >>>> it's >>>>>> easier >>>>>> for you long-term to attempt to become OpenJDK author (which >>>>>> requires >>>>>> signing the OCA[2]). >>>>>> >>>>>>> The dev group at my company is VERY small (and this message >>>> to >>>>>> your >>>>>>> group at the project is from my personal email) but I'd be >>>> glad >>>>>> to >>>>>>> submit bug reports as we come across them in our day to day >>>> use >>>>>> of >>>>>>> Java. >>>>>> If there are good reproducers for bugs this would be very >>>> welcome. >>>>>> Thanks for investing some time in this! >>>>>> >>>>>> Cheers, >>>>>> Severin >>>>>> >>>>>> [1] http://openjdk.java.net/bylaws#author >>>>>> http://openjdk.java.net/projects/#project-author >>>>>> [2] http://oss.oracle.com/oca.pdf >>>>>> >>>>>>> Cordially, >>>>>>> Dennis >>>>>>> den...@gesker.com >>>>>>> >>>>>>> On Fri, Jan 18, 2019 at 10:07 AM Severin Gehwolf < >>>>>> sgehw...@redhat.com >>>>>>>> wrote: >>>>>>>> On Thu, 2019-01-17 at 10:00 -0700, Dennis Gesker wrote: >>>>>>>> [...] >>>>>>>>> Added the -Djavax.net.debug=all option to my Wildfly >>>> startup >>>>>> and >>>>>>>>> waited for the pool to close a connection to MySql at >>>> AWS. >>>>>>>>> TXT file attached. >>>>>>>>> >>>>>>>>> javac 11.0.1 >>>>>>>>> mysql jdbc driver 8.0.13 >>>>>>>>> wildfly 15.0.1 >>>>>>>>> >>>>>>>>> --drg >>>>>>>> Unfortunately the txt file got stripped by the mailing >>>> list. >>>>>> Would >>>>>>>> you >>>>>>>> be able to upload it somewhere and post a link? >>>>>>>> >>>>>>>> Thanks, >>>>>>>> Severin >>>>>>>> >>>>>>> >>>>> >>>>
# HG changeset patch # User Jaikiran Pai <jaikiran....@gmail.com> # Date 1548389397 -19800 # Fri Jan 25 09:39:57 2019 +0530 # Node ID 8b421716e12337a1f669cbb5c06ad4737f715c6f # Parent d02f1f4ff3a637a99885563fcee0fee5d70b9b50 JDK-8215102 Testcase to reproduce the SSLSocket.shutdownInput exception diff --git a/test/jdk/javax/net/ssl/SSLSocket/SSLSocketShutdownInput.java b/test/jdk/javax/net/ssl/SSLSocket/SSLSocketShutdownInput.java new file mode 100644 --- /dev/null +++ b/test/jdk/javax/net/ssl/SSLSocket/SSLSocketShutdownInput.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019, 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. + */ + +/* + * @test + * @bug 8215102 + * @summary Test that SSLSocket.shutdownInput() doesn't throw unexpected SSLException + * @library /javax/net/ssl/templates + */ + +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.InetAddress; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class SSLSocketShutdownInput implements SSLContextTemplate { + + public static void main(String[] args) throws Exception { + new SSLSocketShutdownInput().test(); + } + + /** + * - Starts a server listening on a SSLServerSocket + * - Creates a (client) SSLSocket to communicate with that server + * - Client sends data to the server + * - Client then initiates a SSLSocket.shutdownInput() + * (Note: Server just receives data and doesn't write back or respond back + * to the client, so this is essentially a one-way communication) + * + * @throws Exception + */ + private void test() throws Exception { + final String data = "hello world"; + final Future<String> serverTaskResult; + final SSLServerSocketFactory serversocketfactory = createServerSSLContext().getServerSocketFactory(); + try (final SSLServerSocket serverSocket = (SSLServerSocket) serversocketfactory.createServerSocket(0)) { + serverSocket.setNeedClientAuth(false); + serverSocket.setUseClientMode(false); + // start a thread which waits for client connection + serverTaskResult = Executors.newSingleThreadExecutor().submit(new Server(serverSocket)); + // create a client socket and communicate with the server + final String serverHost = InetAddress.getLocalHost().getHostName(); + final int serverPort = serverSocket.getLocalPort(); + try (final SSLSocket clientSocket = (SSLSocket) createClientSSLContext().getSocketFactory().createSocket( + serverHost, serverPort)) { + // send data to server + final BufferedWriter os = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); + os.write(data); + os.newLine(); + os.flush(); + + // shutdown the input + clientSocket.shutdownInput(); + + } + } + // verify that the server did receive the (right) data + final String dataReceivedByServer = serverTaskResult.get(1, TimeUnit.SECONDS); + if (!dataReceivedByServer.equals(data)) { + throw new Exception("Server did not receive the expected data"); + } + } + + private static final class Server implements Callable<String> { + private final SSLServerSocket listenSocket; + + private Server(final SSLServerSocket listenSocket) { + this.listenSocket = listenSocket; + } + + @Override + public String call() throws Exception { + // wait for connection + try (final SSLSocket socket = (SSLSocket) listenSocket.accept()) { + // read any data from client + final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + return reader.readLine(); + } + } + + } + +} +