diff options
author | Johan Lorensson <lateralusx.github@gmail.com> | 2019-09-26 15:34:28 +0300 |
---|---|---|
committer | Alexander Köplinger <alex.koeplinger@outlook.com> | 2019-09-26 15:34:28 +0300 |
commit | 9b094e660c89eb43ee7d24368711694279bd3b9e (patch) | |
tree | 1e5a56e8d495f4a801dc7c3ce9e90eb3495d578e /mcs/class/System | |
parent | e5b7473a21735699f638699d573ab01b183db0e9 (diff) |
BeginConnect complete to early when not using callback. (#17035)
* BeginConnect complete to early when not using callback.
The following commit, https://github.com/mono/mono/commit/068e8fe0424806753d7ab72a7b9bf0e54b58408b#diff-33b28a9dc0b8032650ed77213481a99f
changed the way we do DNS resolve in BeginConnect using host name. Instead
of doing a blocking DNS resolve, it switched to an async DNS resolve, followed
by an async connect. The returned IAsyncResult was just representing the
completion of the DNS resolve task and not the underlying connect. It
also turns out that part of doing EndConnect, we did some validation
of the used IAsyncResult making sure it was an instance of SocketAsyncResult,
meaning that if EndConnect was called on the returned IAsyncResult from
BeginConnect, EndConnect would throw and exception.
NOTE, this only happens if you use pattern like this:
var result = sock.BeginConnect (hostName, port, null, null);
result.AsyncWaitHandle.WaitOne (2000);
sock.EndConnect (result);
It did not happened if you used callbacks, since then EndConnect would
be called as part of callback and that IAsyncResult was of correct type,
or if you did a BeginConnect with an EndPoint or IPAddress[] since that
won't trigger the DNS resolve.
Fix is to make sure we hand out the SocketAsyncResult instance that will
be used in our internal connect calls from BeginConnect. We also make
sure, we chain the DNS resolve task together with a task doing the async
connect (using the same SocketAsyncResult instance). This will make sure
the IAsyncResult returned from BeginConnect is the same one as used in
internal connect call, so it will only complete when the connect call
completes.
* Adding test for BeginConnect using host name.
* Bump API snapshot submodule
Diffstat (limited to 'mcs/class/System')
-rw-r--r-- | mcs/class/System/System.Net.Sockets/Socket.cs | 37 | ||||
-rwxr-xr-x | mcs/class/System/Test/System.Net.Sockets/SocketTest.cs | 64 |
2 files changed, 77 insertions, 24 deletions
diff --git a/mcs/class/System/System.Net.Sockets/Socket.cs b/mcs/class/System/System.Net.Sockets/Socket.cs index 3c369158934..985075412fc 100644 --- a/mcs/class/System/System.Net.Sockets/Socket.cs +++ b/mcs/class/System/System.Net.Sockets/Socket.cs @@ -999,7 +999,7 @@ namespace System.Net.Sockets } }); - public IAsyncResult BeginConnect (string host, int port, AsyncCallback requestCallback, object state) + public IAsyncResult BeginConnect (string host, int port, AsyncCallback callback, object state) { ThrowIfDisposedAndClosed (); @@ -1012,32 +1012,23 @@ namespace System.Net.Sockets if (is_listening) throw new InvalidOperationException (); - var callback = new AsyncCallback ((result) => { - var resultTask = ((Task<IPAddress[]>)result); - BeginConnect (resultTask.Result, port, requestCallback, resultTask.AsyncState); - }); - return ConvertToApm<IPAddress[]> (Dns.GetHostAddressesAsync (host), callback, state); - } - - private static IAsyncResult ConvertToApm<T> (Task<T> task, AsyncCallback callback, object state) - { - if (task == null) - throw new ArgumentNullException ("task"); + var sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) { + Port = port + }; - var tcs = new TaskCompletionSource<T> (state); - task.ContinueWith (t => - { + var dnsRequest = Dns.GetHostAddressesAsync (host); + dnsRequest.ContinueWith (t => { if (t.IsFaulted) - tcs.TrySetException (t.Exception.InnerExceptions); + sockares.Complete (t.Exception.InnerException); else if (t.IsCanceled) - tcs.TrySetCanceled (); - else - tcs.TrySetResult (t.Result); - - if (callback != null) - callback (tcs.Task); + sockares.Complete (new OperationCanceledException ()); + else { + sockares.Addresses = t.Result; + BeginMConnect (sockares); + } }, TaskScheduler.Default); - return tcs.Task; + + return sockares; } public IAsyncResult BeginConnect (EndPoint remoteEP, AsyncCallback callback, object state) diff --git a/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs b/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs index 49bafdc0066..cc8d3b2e69c 100755 --- a/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs +++ b/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs @@ -512,7 +512,7 @@ namespace MonoTests.System.Net.Sockets sock.BeginConnect (ep, new AsyncCallback(SocketError_callback), sock); - if (SocketError_event.WaitOne (2000, false) == false) { + if (SocketError_event.WaitOne (5000, false) == false) { Assert.Fail ("SocketError wait timed out"); } @@ -1720,6 +1720,68 @@ namespace MonoTests.System.Net.Sockets #if FEATURE_NO_BSD_SOCKETS [ExpectedException (typeof (PlatformNotSupportedException))] #endif + public void BeginConnectHostNamePortWithoutCallback () + { + Socket sock = new Socket (AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.Tcp); + Socket listen = new Socket (AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.Tcp); + + listen.Bind (IPAddress.Loopback, out IPEndPoint ep); + listen.Listen (1); + + IAsyncResult result = sock.BeginConnect (IPAddress.Loopback.ToString (), ep.Port, null, null); + + if (result.AsyncWaitHandle.WaitOne (2000, false) == false) { + Assert.Fail ("BeginConnectHostNamePortWithoutCallback wait timed out"); + } + + Assert.AreEqual (true, result.IsCompleted, "BeginConnectHostNamePortWithoutCallback #1"); + + sock.EndConnect (result); + + sock.Close (); + listen.Close (); + } + + [Test] +#if FEATURE_NO_BSD_SOCKETS + [ExpectedException (typeof (PlatformNotSupportedException))] +#endif + public void BeginConnectHostNamePortWithCallback () + { + Socket sock = new Socket (AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.Tcp); + Socket listen = new Socket (AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.Tcp); + + listen.Bind (IPAddress.Loopback, out IPEndPoint ep); + listen.Listen (1); + + BCCalledBack.Reset (); + + BCConnected = false; + + IAsyncResult result = sock.BeginConnect (IPAddress.Loopback.ToString (), ep.Port, BCCallback, sock); + + if (BCCalledBack.WaitOne (2000, false) == false) { + Assert.Fail ("BeginConnectHostNamePortWithCallback wait timed out"); + } + + Assert.AreEqual (true, BCConnected, "BeginConnectHostNamePortWithCallback #1"); + + sock.Close (); + listen.Close (); + } + + [Test] +#if FEATURE_NO_BSD_SOCKETS + [ExpectedException (typeof (PlatformNotSupportedException))] +#endif public void BeginConnectAddressPortNull () { var port = NetworkHelpers.FindFreePort (); |