Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 42b4772

Browse files
authored
Fix ManagedWebSocket cancellation race condition (#27407)
When a cancelable cancellation token is passed to Receive/SendAsync, the code registers with the token to abort the web socket. The resulting exception is then translated into an OperationCanceledException if _abort is true. But this logic doesn't count on the possibility that the underlying Read/WriteAsync on the transport stream could actually throw a cancellation exception. If it does, there's a race condition as to whether the web socket's abort flag will be set in time; if it is, we wrap the cancellation exception in another cancellation exception, and if it's not, we treat it as a non-cancellation exception. This commit fixes that, by explicitly allowing OperationCanceledExceptions to proceed uninterrupted. This should fix the ReceiveAsync_AfterCancellationDoReceiveAsync_ThrowsWebSocketException test, which recently started failing sporadically, I believe because we improved the cancellation support in SocketsHttpHandler, on top of which ClientWebSocket now sits.
1 parent 517238c commit 42b4772

File tree

1 file changed

+6
-5
lines changed

1 file changed

+6
-5
lines changed

src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,9 @@ private Task SendFrameLockAcquiredNonCancelableAsync(MessageOpcode opcode, bool
415415
}
416416
catch (Exception exc)
417417
{
418-
return Task.FromException(_state == WebSocketState.Aborted ?
419-
CreateOperationCanceledException(exc) :
418+
return Task.FromException(
419+
exc is OperationCanceledException ? exc :
420+
_state == WebSocketState.Aborted ? CreateOperationCanceledException(exc) :
420421
new WebSocketException(WebSocketError.ConnectionClosedPrematurely, exc));
421422
}
422423
finally
@@ -437,7 +438,7 @@ private Task SendFrameLockAcquiredNonCancelableAsync(MessageOpcode opcode, bool
437438
thisRef.ReleaseSendBuffer();
438439

439440
try { t.GetAwaiter().GetResult(); }
440-
catch (Exception exc)
441+
catch (Exception exc) when (!(exc is OperationCanceledException))
441442
{
442443
throw thisRef._state == WebSocketState.Aborted ?
443444
CreateOperationCanceledException(exc) :
@@ -457,7 +458,7 @@ private async Task SendFrameFallbackAsync(MessageOpcode opcode, bool endOfMessag
457458
await _stream.WriteAsync(_sendBuffer, 0, sendBytes, cancellationToken).ConfigureAwait(false);
458459
}
459460
}
460-
catch (Exception exc)
461+
catch (Exception exc) when (!(exc is OperationCanceledException))
461462
{
462463
throw _state == WebSocketState.Aborted ?
463464
CreateOperationCanceledException(exc, cancellationToken) :
@@ -739,7 +740,7 @@ private async ValueTask<TWebSocketReceiveResult> ReceiveAsyncPrivate<TWebSocketR
739740
null, null);
740741
}
741742
}
742-
catch (Exception exc)
743+
catch (Exception exc) when (!(exc is OperationCanceledException))
743744
{
744745
if (_state == WebSocketState.Aborted)
745746
{

0 commit comments

Comments
 (0)