Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClientWebSocket aborts on closes when going thru a proxy #25440

Open
davidsh opened this issue Mar 13, 2018 · 4 comments · Fixed by dotnet/corefx#36928
Open

ClientWebSocket aborts on closes when going thru a proxy #25440

davidsh opened this issue Mar 13, 2018 · 4 comments · Fixed by dotnet/corefx#36928
Labels
area-System.Net bug disabled-test The test is disabled in source code against the issue
Milestone

Comments

@davidsh
Copy link
Contributor

davidsh commented Mar 13, 2018

While working on #24965, I noticed that the following test will fail. There seems to be a problem with the close frame exchange and final TCP socket close when going thru a proxy.

Repro test (not yet in repo):

[OuterLoop] // TODO: Issue dotnet/runtime#18406
[ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))]
public async Task Proxy_ConnectThruProxy_Success(Uri server)
{
    IWebProxy proxy = new WebProxy(new Uri("http://localhost:8080/"));
    using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(
        server,
        TimeOutMilliseconds,
        _output,
        default(TimeSpan),
        proxy))
    {
        var cts = new CancellationTokenSource(TimeOutMilliseconds);
        Assert.Equal(WebSocketState.Open, cws.State);

        var closeStatus = WebSocketCloseStatus.NormalClosure;
        string closeDescription = "Normal Closure";

        await cws.CloseAsync(closeStatus, closeDescription, cts.Token);

        // Verify a clean close frame handshake.
        Assert.Equal(WebSocketState.Closed, cws.State); // *** FAILS HERE ***
        Assert.Equal(closeStatus, cws.CloseStatus);
        Assert.Equal(closeDescription, cws.CloseStatusDescription);
    }
}

Fails with error:

    System.Net.WebSockets.Client.Tests.ClientWebSocketOptionsTests.Proxy_SecureConnectThruProxy_Success(server: ws://corefx-net.cloudapp.net/WebSocket/EchoWeb
  Socket.ashx) [FAIL]
        Assert.Equal() Failure
        Expected: Closed
        Actual:   Aborted
        Stack Trace:
           s:\GitHub\corefx\src\System.Net.WebSockets.Client\tests\ClientWebSocketOptionsTests.cs(90,0): at System.Net.WebSockets.Client.Tests.ClientWebSocketO
  ptionsTests.Proxy_SecureConnectThruProxy_Success(Uri server)
           --- End of stack trace from previous location where exception was thrown ---
     System.Net.WebSockets.Client.Tests.ClientWebSocketOptionsTests.Proxy_SecureConnectThruProxy_Success(server: wss://corefx-net.cloudapp.net/WebSocket/EchoWe
  bSocket.ashx) [FAIL]
        Assert.Equal() Failure
        Expected: Closed
        Actual:   Aborted
        Stack Trace:
           s:\GitHub\corefx\src\System.Net.WebSockets.Client\tests\ClientWebSocketOptionsTests.cs(90,0): at System.Net.WebSockets.Client.Tests.ClientWebSocketO
  ptionsTests.Proxy_SecureConnectThruProxy_Success(Uri server)
           --- End of stack trace from previous location where exception was thrown ---
@davidsh davidsh changed the title ClientWebSocket doesn't get clean closes when going thru a proxy ClientWebSocket aborts on closes when going thru a proxy Mar 13, 2018
@davidsh
Copy link
Contributor Author

davidsh commented Mar 13, 2018

cc: @dotnet/ncl

davidsh referenced this issue in dotnet/corefx Mar 14, 2018
Created new tests for verifying ClientWebSocket connections
end-to-end thru a proxy.

Opened new issues #28024 and #28027 which cause the new tests to fail.

Fixes #26957
@davidsh davidsh self-assigned this Mar 16, 2018
@davidsh
Copy link
Contributor Author

davidsh commented Mar 21, 2018

It seems that the behavior differs depending on which proxy is used. Using the test 'HappySockets' proxy server, we get a RST back after the close frame (which causes this test failure). Using Fiddler, we get a FIN.

However, the test passes using .NET Framework and 'HappySockets' proxy server.

Need to investigate further to determine whether or not this is a test bug (proxy server shouldn't be sending back RST) or a difference between .NET Core and .NET Framework.

ericstj referenced this issue in ericstj/corefx Mar 28, 2018
Created new tests for verifying ClientWebSocket connections
end-to-end thru a proxy.

Opened new issues #28024 and #28027 which cause the new tests to fail.

Fixes #26957
davidsh referenced this issue in davidsh/corefx Apr 16, 2019
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
davidsh referenced this issue in dotnet/corefx Apr 17, 2019
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
@stephentoub
Copy link
Member

@davidsh, there's still a test disabled against this issue:

[ActiveIssue("https://github.com/dotnet/corefx/issues/28027")]
[OuterLoop]
[ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))]
public async Task Proxy_ConnectThruProxy_Success(Uri server)

@stephentoub stephentoub reopened this Jan 23, 2020
@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 5.0 milestone Jan 31, 2020
@karelz karelz modified the milestones: 5.0, Future May 7, 2020
@davidsh davidsh removed their assignment May 20, 2020
Copy link
Contributor

Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.

This process is part of our issue cleanup automation.

@dotnet-policy-service dotnet-policy-service bot added backlog-cleanup-candidate An inactive issue that has been marked for automated closure. no-recent-activity labels Nov 19, 2024
@ManickaP ManickaP removed no-recent-activity backlog-cleanup-candidate An inactive issue that has been marked for automated closure. labels Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Net bug disabled-test The test is disabled in source code against the issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants