0

I have a Kestrel .NET server that serves a websocket connection:

var builder = WebApplication.CreateBuilder(args);
using (var db = new PTLCloudDatabase(builder.Configuration))
{
    db.Setup();
}
builder.Services.AddControllers();
builder.Services.AddDbContext<IPTLCloudDatabase, PTLCloudDatabase>();
builder.Services.AddSingleton<PTLControlWebSocketHandler>();
builder.Services.AddSingleton<PTLCloudWebSocketHandler>();
builder.Services.AddSingleton<ITokenService, JwtTokenService>();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.AddDebug();
});
var app = builder.Build();

app.UseDefaultFiles();
app.UseStaticFiles();
app.UseWebSockets();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapFallbackToFile("/index.html");

app.Run();

the WebSocketController is built as follows:

[ApiController]
[Route("api/[controller]")]
public class WsController : ControllerBase
    private async Task<bool> IsWebSocketRequest()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            return true;
        }
    
        HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        await HttpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("HTTP400: This endpoint only accepts WebSocket requests."));
        return false;
    }

    [HttpGet("ptlcontrol")]
    public async Task PtlControlSocket()
    {
        if (! await IsWebSocketRequest()) return;
    
        // Get the WebSocketHandler instance from the service provider
        var handler = HttpContext.RequestServices.GetRequiredService<PTLControlWebSocketHandler>();
        handler.MessageParser = new MessageParser();
        handler.MessageProcessor = new MessageProcessor(
            HttpContext.RequestServices.GetRequiredService<PTLCloudDatabase>(),
            HttpContext.RequestServices.GetRequiredService<PTLCloudWebSocketHandler>()
        );
    
        using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
        await handler.Handle(new WebSocketAdapter(webSocket));
    }
}

The problem I am having, is that I cannot connect to the Websocket from JavaScript. I run a simple let ws = new WebSocket("wss://localhost/api/ws/ptlcontrol"); and get:

wstester.html:19 WebSocket connection to 'wss://localhost/api/ws/ptlcontrol' failed:

When looking at my server logs, I see it returns a HTTP405 on a HTTP CONNECT call:

info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/2 CONNECT https://localhost/api/ws/ptlcontrol - 405 0 - 25.0023ms

Which is valid, because I never accept any CONNECT calls. However, a Websocket handshake apparently works differently, and the backend should handle this correctly.

Apparently, the browser wants to proxy something here. But what? And why? And how? I don't have a proxy setup.

I tried adding the developer certificates (no result), adding 127.0.0.1 localhost to hosts, but nothing seems to really work.

When I use 127.0.0.1 instead of localhost it does work. And sometimes it also just works (because why not?).

Also, I have a WebSocket tester extension in Chrome, which also has no problems connecting to the socket.

What am I missing here? Why does this straightforward Websocket call fail? Or how can I at least get a decent descriptive error message on this?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.