-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Fix ClientWebSocket close handshake when using proxy #36928
Conversation
When the client websocket was going through a proxy, it would sometimes transition to the 'Aborted' state and not the 'Closed' state after a successful closing handshake. I first opened this bug a year ago but finally was able to get back to it and root cause the problem. The problem only occured with the managed websocket. The .NET Framework did not have this problem. And some proxies didn't cause a problem with managed websocket (such as Fiddler). The root cause is a misinterpretation of RFC 6455, Section 7.1.1. This describes the behavior of how the websocket's TCP connection should be closed. The most graceful way is to wait for the server to initiate the close of the socket. In cases where it is taking too long to wait for the server to start the TCP close, then the client can start the TCP close of the socket. But no matter how the socket is finally closed, the websocket state should transition to 'Closed' as soon as a valid closing handshake was performed (i.e. close frames both sent and received). And this occurs before any logic for closing the TCP connection. The code in managed websocket has a timer to wait 1 second for the server to start a close. If the timer finishes, then the managed websocket calls an Abort() method to close the socket. This ends up transitioning the websocket to an 'Aborted' state which is incorrect. The state should only be moved to the 'Aborted' state if a closing handshake was not completed. I added a new test to support this change and moved the LoopbackProxyServer code to Common. Fixes #28027
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
/azp run |
Azure Pipelines successfully started running 4 pipeline(s). |
Outerloop failures not related to PR: |
Innerloop failures not related to PR: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
@davidsh, they are related. Those HttpListener failures are asserts due to this change. All PRs are failing as a result.
|
Sigh. Ok. I'll get that fix/disabled asap. |
An HttpListener websocket test was failing after the change from PR dotnet#36928. That PR made changes to tighten up the transition to the Closed state after the closing handshake had completed. That PR missed some additional changes needed especially in cases where a server (such as loopback) sends the close frame almost concurrently with the client sending the close frame. This PR fixes the close frame handshake logic and also makes some optimizations in cases where the websocket has already transitioned to the Aborted state during the closing handshake. In that case, the websocket should not wait for a close frame to be received but should simply proceed to closing the connection. Fixes #36963
An HttpListener websocket test was failing after the change from PR #36928. That PR made changes to tighten up the transition to the Closed state after the closing handshake had completed. That PR missed some additional changes needed especially in cases where a server (such as loopback) sends the close frame almost concurrently with the client sending the close frame. This PR fixes the close frame handshake logic and also makes some optimizations in cases where the websocket has already transitioned to the Aborted state during the closing handshake. In that case, the websocket should not wait for a close frame to be received but should simply proceed to closing the connection. Fixes #36963
…6928) When the client websocket was going through a proxy, it would sometimes transition to the 'Aborted' state and not the 'Closed' state after a successful closing handshake. I first opened this bug a year ago but finally was able to get back to it and root cause the problem. The problem only occured with the managed websocket. The .NET Framework did not have this problem. And some proxies didn't cause a problem with managed websocket (such as Fiddler). The root cause is a misinterpretation of RFC 6455, Section 7.1.1. This describes the behavior of how the websocket's TCP connection should be closed. The most graceful way is to wait for the server to initiate the close of the socket. In cases where it is taking too long to wait for the server to start the TCP close, then the client can start the TCP close of the socket. But no matter how the socket is finally closed, the websocket state should transition to 'Closed' as soon as a valid closing handshake was performed (i.e. close frames both sent and received). And this occurs before any logic for closing the TCP connection. The code in managed websocket has a timer to wait 1 second for the server to start a close. If the timer finishes, then the managed websocket calls an Abort() method to close the socket. This ends up transitioning the websocket to an 'Aborted' state which is incorrect. The state should only be moved to the 'Aborted' state if a closing handshake was not completed. I added a new test to support this change and moved the LoopbackProxyServer code to Common. Fixes dotnet/corefx#28027 Commit migrated from dotnet/corefx@0c9684c
An HttpListener websocket test was failing after the change from PR dotnet/corefx#36928. That PR made changes to tighten up the transition to the Closed state after the closing handshake had completed. That PR missed some additional changes needed especially in cases where a server (such as loopback) sends the close frame almost concurrently with the client sending the close frame. This PR fixes the close frame handshake logic and also makes some optimizations in cases where the websocket has already transitioned to the Aborted state during the closing handshake. In that case, the websocket should not wait for a close frame to be received but should simply proceed to closing the connection. Fixes dotnet/corefx#36963 Commit migrated from dotnet/corefx@1824535
When the client websocket was going through a proxy, it would sometimes transition to the
'Aborted' state and not the 'Closed' state after a successful closing handshake.
I first opened this bug a year ago but finally was able to get back to it and root cause the
problem. The problem only occured with the managed websocket. The .NET Framework did not have
this problem. And some proxies didn't cause a problem with managed websocket (such as Fiddler).
The root cause is a misinterpretation of RFC 6455, Section 7.1.1. This describes the behavior
of how the websocket's TCP connection should be closed. The most graceful way is to wait for
the server to initiate the close of the socket. In cases where it is taking too long to wait
for the server to start the TCP close, then the client can start the TCP close of the socket.
But no matter how the socket is finally closed, the websocket state should transition to 'Closed'
as soon as a valid closing handshake was performed (i.e. close frames both sent and received).
And this occurs before any logic for closing the TCP connection.
The code in managed websocket has a timer to wait 1 second for the server to start a close. If
the timer finishes, then the managed websocket calls an Abort() method to close the socket. This
ends up transitioning the websocket to an 'Aborted' state which is incorrect. The state should
only be moved to the 'Aborted' state if a closing handshake was not completed.
I added a new test to support this change and moved the LoopbackProxyServer code to Common.
Fixes #28027