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

Add additional close statuses in ManagedWebSocket #83827

Merged
merged 9 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,9 @@ private static bool IsValidCloseStatus(WebSocketCloseStatus closeStatus)
case WebSocketCloseStatus.NormalClosure:
case WebSocketCloseStatus.PolicyViolation:
case WebSocketCloseStatus.ProtocolError:
case (WebSocketCloseStatus)1012: // ServiceRestart
case (WebSocketCloseStatus)1013: // TryAgainLater
case (WebSocketCloseStatus)1014: // BadGateway
return true;

default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public enum WebSocketCloseStatus
MessageTooBig = 1009,
MandatoryExtension = 1010,
InternalServerError = 1011
// non-RFC IANA registered status codes that we allow as valid closing status
// ServiceRestart = 1012, // indicates that the server / service is restarting.
// TryAgainLater = 1013, // indicates that a temporary server condition forced blocking the client's request.
// BadGateway = 1014 // indicates that the server acting as gateway received an invalid response
// TLSHandshakeFailed = 1015, // 1015 is reserved and should never be used by user

// 0 - 999 Status codes in the range 0-999 are not used.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<RdXmlFile Include="default.rd.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="WebSocketCloseTests.cs" />
<Compile Include="WebSocketTests.cs" />
<Compile Include="WebSocketExceptionTests.cs" />
<Compile Include="WebSocketReceiveResultTests.cs" />
Expand Down
89 changes: 89 additions & 0 deletions src/libraries/System.Net.WebSockets/tests/WebSocketCloseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace System.Net.WebSockets.Tests
{
public class WebSocketCloseTests
{
private readonly CancellationTokenSource? _cancellation;

public WebSocketCloseTests()
{
if (!Debugger.IsAttached)
{
_cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(5));
}
}

public CancellationToken CancellationToken => _cancellation?.Token ?? default;

public static object[][] CloseStatuses = {
new object[] { WebSocketCloseStatus.EndpointUnavailable },
new object[] { WebSocketCloseStatus.InternalServerError },
new object[] { WebSocketCloseStatus.InvalidMessageType},
new object[] { WebSocketCloseStatus.InvalidPayloadData },
new object[] { WebSocketCloseStatus.MandatoryExtension },
new object[] { WebSocketCloseStatus.MessageTooBig },
new object[] { WebSocketCloseStatus.NormalClosure },
new object[] { WebSocketCloseStatus.PolicyViolation },
new object[] { WebSocketCloseStatus.ProtocolError },
new object[] { (WebSocketCloseStatus)1012 }, // ServiceRestart indicates that the server / service is restarting.
new object[] { (WebSocketCloseStatus)1013 }, // TryAgainLater indicates that a temporary server condition forced blocking the client's request.
new object[] { (WebSocketCloseStatus)1014 }, // BadGateway indicates that the server acting as gateway received an invalid response
};

[Theory]
[MemberData(nameof(CloseStatuses))]
public void WebSocketReceiveResult_WebSocketCloseStatus_Roundtrip(WebSocketCloseStatus closeStatus)
{
string closeStatusDescription = "closeStatus " + closeStatus.ToString();
WebSocketReceiveResult wsrr = new WebSocketReceiveResult(42, WebSocketMessageType.Close, endOfMessage: true, closeStatus, closeStatusDescription);
Assert.Equal(42, wsrr.Count);
Assert.Equal(closeStatus, wsrr.CloseStatus);
Assert.Equal(closeStatusDescription, wsrr.CloseStatusDescription);
}

[Theory]
[MemberData(nameof(CloseStatuses))]
public async Task ReceiveAsync_ValidCloseStatus_Success(WebSocketCloseStatus closeStatus)
{
byte[] receiveBuffer = new byte[1024];
WebSocketTestStream stream = new();
Encoding encoding = Encoding.UTF8;

using (WebSocket server = WebSocket.CreateFromStream(stream, isServer: true, subProtocol: null, TimeSpan.FromSeconds(3)))
using (WebSocket client = WebSocket.CreateFromStream(stream.Remote, isServer: false, subProtocol: null, TimeSpan.FromSeconds(3)))
{
Assert.NotNull(server);
Assert.NotNull(client);

// send something
string hello = "Testing " + closeStatus.ToString();
byte[] sendBytes = encoding.GetBytes(hello);
await server.SendAsync(sendBytes.AsMemory(), WebSocketMessageType.Text, WebSocketMessageFlags.None, CancellationToken);

// and then server-side close with the test status
string closeStatusDescription = "CloseStatus " + closeStatus.ToString();
await server.CloseOutputAsync(closeStatus, closeStatusDescription, CancellationToken);

// get the hello from the client (after the close message was sent)
WebSocketReceiveResult result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken);
Assert.Equal(WebSocketMessageType.Text, result.MessageType);
string response = encoding.GetString(receiveBuffer.AsSpan(0, result.Count));
Assert.Equal(hello, response);

// now look for the expected close status
WebSocketReceiveResult closing = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken);
Assert.Equal(WebSocketMessageType.Close, closing.MessageType);
Assert.Equal(closeStatus, closing.CloseStatus);
Assert.Equal(closeStatusDescription, closing.CloseStatusDescription);
}
}
}
}