Fix a few WebSocketHandle.Managed issues (dotnet/corefx#25010)
authorStephen Toub <stoub@microsoft.com>
Thu, 2 Nov 2017 14:35:38 +0000 (10:35 -0400)
committerGitHub <noreply@github.com>
Thu, 2 Nov 2017 14:35:38 +0000 (10:35 -0400)
- At least until socket send/receives respect cancellation, we need to poll for cancellation after the handler's SendAsync completes.
- We're unnecessarily allocating another CTS if no external cancellation was provided.
- We're not disposing of the response object in the case of an error after SendAsync completes.

Commit migrated from https://github.com/dotnet/corefx/commit/baf2128a7cdf4a7638be1e86a6fc18651f3945d3

src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs

index dc4d0ac..0239934 100644 (file)
@@ -108,6 +108,7 @@ namespace System.Net.WebSockets
 
         public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
         {
+            HttpResponseMessage response = null;
             try
             {
                 // Create the request message, including a uri with ws{s} switched to http{s}.
@@ -138,11 +139,25 @@ namespace System.Net.WebSockets
                 }
 
                 // Issue the request.  The response must be status code 101.
-                HttpResponseMessage response;
-                using (var externalAndAbortCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _abortSource.Token))
+                CancellationTokenSource linkedCancellation, externalAndAbortCancellation;
+                if (cancellationToken.CanBeCanceled) // avoid allocating linked source if external token is not cancelable
+                {
+                    linkedCancellation =
+                        externalAndAbortCancellation = 
+                        CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _abortSource.Token);
+                }
+                else
+                {
+                    linkedCancellation = null;
+                    externalAndAbortCancellation = _abortSource;
+                }
+
+                using (linkedCancellation)
                 {
                     response = await handler.SendAsync(request, externalAndAbortCancellation.Token).ConfigureAwait(false);
+                    externalAndAbortCancellation.Token.ThrowIfCancellationRequested(); // poll in case sends/receives in request/response didn't observe cancellation
                 }
+
                 if (response.StatusCode != HttpStatusCode.SwitchingProtocols)
                 {
                     throw new WebSocketException(SR.net_webstatus_ConnectFailure);
@@ -198,6 +213,7 @@ namespace System.Net.WebSockets
                 }
 
                 Abort();
+                response?.Dispose();
 
                 if (exc is WebSocketException)
                 {