From e4f33dff577b10b2568ecb5559ab61eb9bfced80 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 2 Sep 2020 17:34:29 +0200 Subject: [PATCH 1/3] add tests --- .../tests/FunctionalTests/TcpListenerTest.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs index eeab701bb96c54..323861b0372b61 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs @@ -252,6 +252,36 @@ public void ExclusiveAddressUse_SetStartAndStopListenerThenRead_ReadSuccessfully Assert.True(listener.ExclusiveAddressUse); } + [Fact] + public void EndAcceptSocket_WhenStopped_ThrowsObjectDisposedException() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + + IAsyncResult iar = listener.BeginAcceptSocket(callback: null, state: null); + + // Give some time for the connect operation to kick in: + Thread.Sleep(50); + listener.Stop(); + + Assert.Throws(() => listener.EndAcceptSocket(iar)); + } + + [Fact] + public void EndAcceptTcpClient_WhenStopped_ThrowsObjectDisposedException() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + + IAsyncResult iar = listener.BeginAcceptTcpClient(callback: null, state: null); + + // Give some time for the connect operation to kick in: + Thread.Sleep(50); + listener.Stop(); + + Assert.Throws(() => listener.EndAcceptTcpClient(iar)); + } + private sealed class DerivedTcpListener : TcpListener { #pragma warning disable 0618 From 56fd523173f6442d7d53721dc4aaf42c1cfbf8eb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 2 Sep 2020 17:46:17 +0200 Subject: [PATCH 2/3] fix code --- .../src/System/Net/Sockets/TCPListener.cs | 18 ++++++++++++++++-- .../tests/FunctionalTests/TcpListenerTest.cs | 6 +++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs index 6c623fec1cfb99..23ad4e9ae6ce20 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs @@ -211,13 +211,13 @@ public IAsyncResult BeginAcceptSocket(AsyncCallback? callback, object? state) => TaskToApm.Begin(AcceptSocketAsync(), callback, state); public Socket EndAcceptSocket(IAsyncResult asyncResult) => - TaskToApm.End(asyncResult); + EndAcceptCore(asyncResult); public IAsyncResult BeginAcceptTcpClient(AsyncCallback? callback, object? state) => TaskToApm.Begin(AcceptTcpClientAsync(), callback, state); public TcpClient EndAcceptTcpClient(IAsyncResult asyncResult) => - TaskToApm.End(asyncResult); + EndAcceptCore(asyncResult); public Task AcceptSocketAsync() { @@ -280,5 +280,19 @@ private void CreateNewSocketIfNeeded() _allowNatTraversal = null; // Reset value to avoid affecting more sockets } } + + private static TResult EndAcceptCore(IAsyncResult asyncResult) + { + try + { + return TaskToApm.End(asyncResult); + } + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) + { + // Socket.EndAccept(iar) throws ObjectDisposedException when the underlying socket gets closed. + // TcpClient's documented behavior was to propagate that exception, we need to emulate it for compatibility: + throw new ObjectDisposedException(typeof(Socket).FullName); + } + } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs index 323861b0372b61..e442a33ade7e20 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs @@ -259,8 +259,8 @@ public void EndAcceptSocket_WhenStopped_ThrowsObjectDisposedException() listener.Start(); IAsyncResult iar = listener.BeginAcceptSocket(callback: null, state: null); - - // Give some time for the connect operation to kick in: + + // Give some time for the underlying OS operation to start: Thread.Sleep(50); listener.Stop(); @@ -275,7 +275,7 @@ public void EndAcceptTcpClient_WhenStopped_ThrowsObjectDisposedException() IAsyncResult iar = listener.BeginAcceptTcpClient(callback: null, state: null); - // Give some time for the connect operation to kick in: + // Give some time for the underlying OS operation to start: Thread.Sleep(50); listener.Stop(); From 1dc46ed0d1bac5fd8fb82d7c3671b5bb4296562e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 3 Sep 2020 11:44:44 +0200 Subject: [PATCH 3/3] address review comment --- .../System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs index 23ad4e9ae6ce20..34b0cbd858f13a 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs @@ -281,13 +281,13 @@ private void CreateNewSocketIfNeeded() } } - private static TResult EndAcceptCore(IAsyncResult asyncResult) + private TResult EndAcceptCore(IAsyncResult asyncResult) { try { return TaskToApm.End(asyncResult); } - catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) + catch (SocketException) when (!_active) { // Socket.EndAccept(iar) throws ObjectDisposedException when the underlying socket gets closed. // TcpClient's documented behavior was to propagate that exception, we need to emulate it for compatibility: