Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohan Lorensson <lateralusx.github@gmail.com>2019-09-26 15:34:28 +0300
committerAlexander Köplinger <alex.koeplinger@outlook.com>2019-09-26 15:34:28 +0300
commit9b094e660c89eb43ee7d24368711694279bd3b9e (patch)
tree1e5a56e8d495f4a801dc7c3ce9e90eb3495d578e /mcs/class/System
parente5b7473a21735699f638699d573ab01b183db0e9 (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.cs37
-rwxr-xr-xmcs/class/System/Test/System.Net.Sockets/SocketTest.cs64
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 ();