Found different in behavior of SocketInputStream#read in JDK13
Alan Bateman
Alan.Bateman at oracle.com
Wed Jul 31 12:38:53 UTC 2019
Thanks for additional info. Although it may not be a "a big deal" for
the Zookeeper test, it would be great if you and Enrico have time to
help us diagnose this a big further.
At a high-level, the SocketException: "Connection reset" means the peer
has terminated the connection (hard reset). When you are using new
SocketImpl the peer appears to be closely the connection gracefully,
help -1 is read. Would I be correct to say that UnifiedServerSocketTest
is connecting to the Zookeeper server and it terminates the connection
due to the SSL connection. Is the Zookeeper running on JDK 13 too? I'm
just wondering if the scenario intermittently leads to a hard reset on
some occasions and a graceful close at some times and switching
implementation has changed the timing a bit to cause the latter to be
more likely.
Is there any way to turn this test in a reproducer that we could study
more closely?
-Alan
On 31/07/2019 01:41, Szalay-Bekő Máté wrote:
> Hi Alan,
>
> As Alan mentioned, it is not a big deal in Zookeeper's perspective. We
> don't rely on this specific socket behaviour anywhere in our codebase,
> we only faced it when trying to emulate an erroneous client behaviour
> and try to assert on the way how the connection is not established
> successfully...
>
> Some background info:
>
> The test in Zookeeper
> (https://github.com/apache/zookeeper/blob/e043c322f12d56da0fc88131628edf0731c0f8e4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/UnifiedServerSocketTest.java#L398)
> simulates the case, when a client without using SSL tries to connect
> to the server which requires SSL. Before starting the test, we don't
> set the "jdk.net.usePlainSocketImpl" (so we use the new NioSocket
> while emulating the client behaviour), and also we set
> "jdk.tls.rejectClientInitiatedRenegotiation=true" to disable
> client-initiated TLS renegotiation (although I am not sure if that is
> relevant in this case or not).
>
> Using OpenJDK 13.30 on Xenial ubuntu, we saw that the socket.read()
> returned with -1 without throwing an exception, indicating the end of
> the stream.
>
> The old behaviour (e.g. using openJDK 12.0.2 on xenial) was to get a
> SocketException ("Connection reset") with this stack trace:
>
> [junit] 2019-07-31 08:30:54,457 [myid:] - INFO
> [main:UnifiedServerSocketTest at 419] - We get the expected exception:
> [junit] java.net.SocketException: Connection reset
> [junit] at
> org.apache.zookeeper.server.quorum.UnifiedServerSocketTest$UnifiedServerThread.run(UnifiedServerSocketTest.java:204)
> [junit] at
> java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
> [junit] Caused by: java.net.SocketException: Socket closed
> [junit] at
> java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
> [junit] at java.base/java.net.PlainSocketImpl.socketAccept(Native
> Method)
> [junit] at
> java.base/java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:458)
> [junit] at
> java.base/java.net.ServerSocket.implAccept(ServerSocket.java:556)
> [junit] at
> org.apache.zookeeper.server.quorum.UnifiedServerSocketTest.testConnectWithoutSSLToStrictServer(UnifiedServerSocketTest.java:417)
> [junit] at
> org.apache.zookeeper.server.quorum.UnifiedServerSocket.accept(UnifiedServerSocket.java:146)
> [junit] at
> org.apache.zookeeper.server.quorum.UnifiedServerSocketTest$UnifiedServerThread.run(UnifiedServerSocketTest.java:167)
> [junit] at
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
> Method)
> [junit] at
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> [junit] at
> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> [junit] at java.base/java.lang.reflect.Method.invoke(Method.java:567)
> [junit] at
> org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
> [junit] at
> org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
> [junit] at
> org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
> [junit] at
> org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
> [junit] at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> [junit] at
> org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
> [junit] at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
> [junit] at org.junit.rules.RunRules.evaluate(RunRules.java:20)
> [junit] at
> org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
> [junit] at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
> [junit] at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
> [junit] at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
> [junit] at
> org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
> [junit] at
> org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
> [junit] at
> org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
> [junit] at
> org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
> [junit] at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
> [junit] at org.junit.runners.Suite.runChild(Suite.java:128)
> [junit] at org.junit.runners.Suite.runChild(Suite.java:27)
> [junit] at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
> [junit] at
> org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
> [junit] at
> org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
> [junit] at
> org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
> [junit] at
> org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
> [junit] at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> [junit] at
> org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
> [junit] at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
> [junit] at
> junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:38)
> [junit] at
> org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)
> [junit] at
> org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1182)
> [junit] at
> org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:1033)
> [junit] 2019-07-31 08:30:54,465 [myid:] - INFO
> [main:ZKTestCase$1 at 70] - SUCCEEDED testConnectWithoutSSLToStrictServer[0]
>
>
>
> Interestingly, if I execute the very same test with OpenJDK 13.30 on
> my macbook, I also get a similar exception SocketException using the
> NioSocket implementation. Could this suggests some differences in the
> native code?
>
> [junit] 2019-07-31 10:33:44,926 [myid:] - INFO
> [main:UnifiedServerSocketTest at 419] - We get the expected exception:
> [junit] java.net.SocketException: Connection reset
> [junit] at
> java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:324)
> [junit] at
> java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:351)
> [junit] at
> java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:802)
> [junit] at
> java.base/java.net.Socket$SocketInputStream.read(Socket.java:919)
> [junit] at
> org.apache.zookeeper.server.quorum.UnifiedServerSocketTest.testConnectWithoutSSLToStrictServer(UnifiedServerSocketTest.java:417)
> [junit] at
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
> Method)
> [junit] at
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> [junit] at
> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> [junit] at java.base/java.lang.reflect.Method.invoke(Method.java:567)
> [junit] at
> org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
> [junit] at
> org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
> [junit] at
> org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
> [junit] at
> org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
> [junit] at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> [junit] at
> org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
> [junit] at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
> [junit] at org.junit.rules.RunRules.evaluate(RunRules.java:20)
> [junit] at
> org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
> [junit] at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
> [junit] at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
> [junit] at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
> [junit] at
> org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
> [junit] at
> org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
> [junit] at
> org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
> [junit] at
> org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
> [junit] at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
> [junit] at org.junit.runners.Suite.runChild(Suite.java:128)
> [junit] at org.junit.runners.Suite.runChild(Suite.java:27)
> [junit] at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
> [junit] at
> org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
> [junit] at
> org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
> [junit] at
> org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
> [junit] at
> org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
> [junit] at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> [junit] at
> org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
> [junit] at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
> [junit] at
> junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:38)
> [junit] at
> org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)
> [junit] at
> org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1182)
> [junit] at
> org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:1033)
> [junit] 2019-07-31 10:33:44,931 [myid:] - INFO
> [main:ZKTestCase$1 at 70] - SUCCEEDED testConnectWithoutSSLToStrictServer[0]
>
>
> Cheers,
> Mate
>
>
> >> On Jul 30, 2019, at 18:53 PM, Alan Bateman <Alan.Bateman at
> oracle.com <http://oracle.com> <mailto:Alan.Bateman
> <mailto:Alan.Bateman> at oracle.com <http://oracle.com>>> wrote:
> >> Hello,
> >> in Apache ZooKeeper community we discovered a difference in behavior
> >> in SocketInputStream.
> >>
> >> The problem is that SocketInputStream#read used to throw a
> >> SocketException instead of returning -1.
> >>
> >> I see that this was cited in https://openjdk.java.net/jeps/353
> >>
> >> "The |InputStream| and |OutputStream| returned by the old
> >> implementation tests the stream for EOF and returns -1 before other
> >> checks. The new implementation does |null| and bounds checks before
> >> checking if the stream is at EOF. It is possible, but unlikely, that
> >> there is fragile code that will trip up due to the order of the
> checks."
> >>
> >> We are talking about a unit test, no a big deal.
> >>
> >> This is the fix, provided by Mate Szalay-Beko
> >> https://github.com/apache/zookeeper/pull/1029
> >>
> >Thanks for the mail. What is the exception you see with the legacy
> >implementation? The note you cite would lead to an
> >IndexOutOfBoundException due to the bad input and I don't think this is
> >what you are seeing.
> >
> >-Alan.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/nio-dev/attachments/20190731/058e744a/attachment-0001.html>
More information about the nio-dev
mailing list