Closed
Description
Description
HttpListenerContext.AcceptWebSocketAsync throws PlatformNotSupportedException when called with User set to WindowsPrincipal. This happens in .NET Core 3.1 when running on Windows. Works fine when running in .NET Framework.
Reproduction Steps
using System;
using System.Diagnostics;
using System.Net;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketCloseTest
{
public class Program
{
public static async Task Main(string[] args)
{
var serverTask = Server(http://localhost:45000/test/ws/);
await Client("ws://localhost:45000/test/ws/");
await serverTask;
}
private static async Task Server(String serverAddress)
{
var listener = new HttpListener();
listener.Prefixes.Add(serverAddress);
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
listener.Start();
HttpListenerContext ctx = await listener.GetContextAsync();
try
{
Debug.Assert(ctx.User != null); // this is supposed to be a WindowsPrincipal
HttpListenerWebSocketContext wsCtx = await ctx.AcceptWebSocketAsync("subProtocol"); // this throws on .NET Core
await wsCtx.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
}
finally
{
ctx.Response.Close();
}
}
private static async Task Client(String serverAddress)
{
try
{
using (ClientWebSocket webSocket = new ClientWebSocket())
{
webSocket.Options.AddSubProtocol("subProtocol");
webSocket.Options.Credentials = CredentialCache.DefaultCredentials;
await webSocket.ConnectAsync(new Uri(serverAddress), CancellationToken.None);
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
}
}
catch (Exception e)
{
Console.WriteLine($"Client WS exception: {e.Message}");
}
}
}
}
Expected behavior
It should not throw the exception and work the same way as it does in .NET Framework
Actual behavior
Throws PlatformNotSupportedException.
Regression?
Yes, works in .NET Framework
Known Workarounds
Use reflection to set the _user field to null, make the call and set it back to the original value, like this:
HttpListenerContext ctx = await listener.GetContextAsync();
IPrincipal originalPrincipal = ctx.User;
FieldInfo field = ctx.GetType().GetField("_user", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(_ctx, null);
// make the call with User = null to prevent the exception
wsCtx = await ctx.AcceptWebSocketAsync(WsConstants.SubProtocol, TimeSpan.FromSeconds(WsConstants.KeepAliveIntervalSeconds));
// revert to the original value
field.SetValue(_ctx, originalPrincipal);
Configuration
.NET Core 3.1, Windows 10 x64
Other information
No response