diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs
index 48a55b462c1c0..114cbbe92d5bf 100644
--- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs
+++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs
@@ -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:
diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketCloseStatus.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketCloseStatus.cs
index 235dc86bbbeca..0608a831f85f9 100644
--- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketCloseStatus.cs
+++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketCloseStatus.cs
@@ -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.
diff --git a/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj b/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj
index 8af7c5bed3507..f49bdcbbac0de 100644
--- a/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj
+++ b/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj
@@ -6,6 +6,7 @@
+
diff --git a/src/libraries/System.Net.WebSockets/tests/WebSocketCloseTests.cs b/src/libraries/System.Net.WebSockets/tests/WebSocketCloseTests.cs
new file mode 100644
index 0000000000000..86d1dfb2cd530
--- /dev/null
+++ b/src/libraries/System.Net.WebSockets/tests/WebSocketCloseTests.cs
@@ -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(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(receiveBuffer), CancellationToken);
+ Assert.Equal(WebSocketMessageType.Close, closing.MessageType);
+ Assert.Equal(closeStatus, closing.CloseStatus);
+ Assert.Equal(closeStatusDescription, closing.CloseStatusDescription);
+ }
+ }
+ }
+}