Repository navigation
Closed
Labels
area-System.NetenhancementProduct code improvement that does NOT require public API changes/additionsProduct code improvement that does NOT require public API changes/additionshelp wanted[up-for-grabs] Good issue for external contributors[up-for-grabs] Good issue for external contributors
Milestone
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
Reacted by iconics-milan
Metadata
Metadata
Assignees
Labels
area-System.NetenhancementProduct code improvement that does NOT require public API changes/additionsProduct code improvement that does NOT require public API changes/additionshelp wanted[up-for-grabs] Good issue for external contributors[up-for-grabs] Good issue for external contributors
Activity
karelz commentedon Jan 13, 2022
Can you give us simple self-contained repro we can run? (I assume the above code snippet is not sufficient)
Can you please test it also on .NET 6?
iconics-janb commentedon Jan 14, 2022
iconics-janb commentedon Jan 14, 2022
CarnaViire commentedon Jan 14, 2022
Indeed, as @iconics-janb pointed out, our code will throw PNSE if user is WindowsPrincipal:
runtime/src/libraries/System.Net.HttpListener/src/System/Net/WebSockets/HttpListenerWebSocketContext.cs
Line 104 in 57bfe47
but .NET Framework code contained an implementation for the same condition
https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/HttpListenerWebSocketContext.cs,150
@wfurt @stephentoub Do you know anything that would prevent us from reusing the original code with WindowsPrincipal existing in .NET Framework? I wonder why it was not used in the first place, and why PNSE was used (The PR that introduced it (dotnet/corefx#20398 (comment)) doesn't help). I would expect that user couldn't be WindowsPrincipal on non-Windows platform in the first place - am I missing something?
wfurt commentedon Jan 14, 2022
Yes, I think we can make it work for Windows (assuming all the dependencies are in place) and PNSE for other platforms.
The original exception was "not implemented" so int feels just something that got dropped from the initial implementation. (and perhaps lost)
karelz commentedon Jan 18, 2022
Triage: Needs to be ported from .NET Framework. Fairly straightforward work - see link to referencesource above.
Given that this is 1st report in .NET Core timeframe, moving it to Future. We would take a contribution though.