Skip to content

HttpListenerContext.AcceptWebSocketAsync unsupported for IWindowsPrincipal in .NET Core 3.1 #63738

Closed

Description

@iconics-janb

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

Activity

  1. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Jan 13, 2022
  2. karelz commented on Jan 13, 2022

    @karelz
    Member

    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?

  3. iconics-janb commented on Jan 14, 2022

    @iconics-janb
    Author
  4. iconics-janb commented on Jan 14, 2022

    @iconics-janb
    Author
  5. CarnaViire commented on Jan 14, 2022

    @CarnaViire
    Member

    Indeed, as @iconics-janb pointed out, our code will throw PNSE if user is WindowsPrincipal:


    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?

  6. wfurt commented on Jan 14, 2022

    @wfurt
    Member

    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)

  7. karelz commented on Jan 18, 2022

    @karelz
    Member

    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.

  8. added this to the Future milestone on Jan 18, 2022
  9. added
    enhancementProduct code improvement that does NOT require public API changes/additions
    help wanted[up-for-grabs] Good issue for external contributors
    and removed
    untriagedNew issue has not been triaged by the area owner
    on Jan 18, 2022
  10. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Feb 22, 2022
  11. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Mar 15, 2022
  12. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Mar 15, 2022
  13. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Mar 15, 2022
  14. modified the milestones: Future, 7.0.0 on Apr 8, 2022
  15. ghost locked as resolved and limited conversation to collaborators on May 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.NetenhancementProduct code improvement that does NOT require public API changes/additionshelp wanted[up-for-grabs] Good issue for external contributors

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions