@@ -1063,50 +1063,56 @@ private async Task CloseAsyncPrivate(WebSocketCloseStatus closeStatus, string st
10631063 await SendCloseFrameAsync ( closeStatus , statusDescription , cancellationToken ) . ConfigureAwait ( false ) ;
10641064 }
10651065
1066- // We should now either be in a CloseSent case (because we just sent one), or in a CloseReceived state, in case
1066+ // We should now either be in a CloseSent case (because we just sent one), or in a Closed state, in case
10671067 // there was a concurrent receive that ended up handling an immediate close frame response from the server.
10681068 // Of course it could also be Aborted if something happened concurrently to cause things to blow up.
10691069 Debug . Assert (
10701070 State == WebSocketState . CloseSent ||
1071- State == WebSocketState . CloseReceived ||
1071+ State == WebSocketState . Closed ||
10721072 State == WebSocketState . Aborted ,
10731073 $ "Unexpected state { State } .") ;
10741074
1075- // Wait until we've received a close response
1076- byte [ ] closeBuffer = ArrayPool < byte > . Shared . Rent ( MaxMessageHeaderLength + MaxControlPayloadLength ) ;
1077- try
1075+ // We only need to wait for a received close frame if we are in the CloseSent State. If we are in the Closed
1076+ // State then it means we already received a close frame. If we are in the Aborted State, then we should not
1077+ // wait for a close frame as per RFC 6455 Section 7.1.7 "Fail the WebSocket Connection".
1078+ if ( State == WebSocketState . CloseSent )
10781079 {
1079- while ( ! _receivedCloseFrame )
1080+ // Wait until we've received a close response
1081+ byte [ ] closeBuffer = ArrayPool < byte > . Shared . Rent ( MaxMessageHeaderLength + MaxControlPayloadLength ) ;
1082+ try
10801083 {
1081- Debug . Assert ( ! Monitor . IsEntered ( StateUpdateLock ) , $ "{ nameof ( StateUpdateLock ) } must never be held when acquiring { nameof ( ReceiveAsyncLock ) } ") ;
1082- Task receiveTask ;
1083- lock ( ReceiveAsyncLock )
1084+ while ( ! _receivedCloseFrame )
10841085 {
1085- // Now that we're holding the ReceiveAsyncLock, double-check that we've not yet received the close frame.
1086- // It could have been received between our check above and now due to a concurrent receive completing.
1087- if ( _receivedCloseFrame )
1086+ Debug . Assert ( ! Monitor . IsEntered ( StateUpdateLock ) , $ " { nameof ( StateUpdateLock ) } must never be held when acquiring { nameof ( ReceiveAsyncLock ) } " ) ;
1087+ Task receiveTask ;
1088+ lock ( ReceiveAsyncLock )
10881089 {
1089- break ;
1090+ // Now that we're holding the ReceiveAsyncLock, double-check that we've not yet received the close frame.
1091+ // It could have been received between our check above and now due to a concurrent receive completing.
1092+ if ( _receivedCloseFrame )
1093+ {
1094+ break ;
1095+ }
1096+
1097+ // We've not yet processed a received close frame, which means we need to wait for a received close to complete.
1098+ // There may already be one in flight, in which case we want to just wait for that one rather than kicking off
1099+ // another (we don't support concurrent receive operations). We need to kick off a new receive if either we've
1100+ // never issued a receive or if the last issued receive completed for reasons other than a close frame. There is
1101+ // a race condition here, e.g. if there's a in-flight receive that completes after we check, but that's fine: worst
1102+ // case is we then await it, find that it's not what we need, and try again.
1103+ receiveTask = _lastReceiveAsync ;
1104+ _lastReceiveAsync = receiveTask = ValidateAndReceiveAsync ( receiveTask , closeBuffer , cancellationToken ) ;
10901105 }
10911106
1092- // We've not yet processed a received close frame, which means we need to wait for a received close to complete.
1093- // There may already be one in flight, in which case we want to just wait for that one rather than kicking off
1094- // another (we don't support concurrent receive operations). We need to kick off a new receive if either we've
1095- // never issued a receive or if the last issued receive completed for reasons other than a close frame. There is
1096- // a race condition here, e.g. if there's a in-flight receive that completes after we check, but that's fine: worst
1097- // case is we then await it, find that it's not what we need, and try again.
1098- receiveTask = _lastReceiveAsync ;
1099- _lastReceiveAsync = receiveTask = ValidateAndReceiveAsync ( receiveTask , closeBuffer , cancellationToken ) ;
1107+ // Wait for whatever receive task we have. We'll then loop around again to re-check our state.
1108+ Debug . Assert ( receiveTask != null ) ;
1109+ await receiveTask . ConfigureAwait ( false ) ;
11001110 }
1101-
1102- // Wait for whatever receive task we have. We'll then loop around again to re-check our state.
1103- Debug . Assert ( receiveTask != null ) ;
1104- await receiveTask . ConfigureAwait ( false ) ;
11051111 }
1106- }
1107- finally
1108- {
1109- ArrayPool < byte > . Shared . Return ( closeBuffer ) ;
1112+ finally
1113+ {
1114+ ArrayPool < byte > . Shared . Return ( closeBuffer ) ;
1115+ }
11101116 }
11111117
11121118 // We're closed. Close the connection and update the status.
0 commit comments