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?