diff --git a/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs b/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs index 69d9783..93086e0 100644 --- a/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs +++ b/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs @@ -51,11 +51,19 @@ public static class BattleNodeExtensions public static IApplicationBuilder UseBattleNode(this IApplicationBuilder app) { app.UseWebSockets(); - app.Map("/socket.io", branch => branch.Run(async ctx => - { - var handler = ctx.RequestServices.GetRequiredService(); - await handler.HandleAsync(ctx); - })); + app.Map("/socket.io", branch => branch.Run(HandleSocketIoAsync)); return app; } + + /// + /// Terminal handler for /socket.io/* — resolves the singleton + /// from DI and hands the request over. + /// Extracted from the inline lambda in so stack traces + /// show a real method name during WS connect failures. + /// + private static async Task HandleSocketIoAsync(Microsoft.AspNetCore.Http.HttpContext ctx) + { + var handler = ctx.RequestServices.GetRequiredService(); + await handler.HandleAsync(ctx); + } } diff --git a/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs b/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs index 25c381e..dc3a1fa 100644 --- a/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs +++ b/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs @@ -47,6 +47,10 @@ public sealed class BattleNodeWebSocketHandler /// public async Task HandleAsync(HttpContext ctx) { + // Status code mapping: 400 protocol violations (not WS, missing creds); + // 401 credential validation failures (decrypt, viewer mismatch); 404 unknown + // BattleId. Log messages carry the diagnostic detail; the wire code gives the + // client class of failure. if (!ctx.WebSockets.IsWebSocketRequest) { ctx.Response.StatusCode = StatusCodes.Status400BadRequest; @@ -75,7 +79,7 @@ public sealed class BattleNodeWebSocketHandler catch (Exception ex) { _log.LogWarning(ex, "viewerId failed to decrypt (encryptedLen={Len})", encryptedViewerId.Length); - ctx.Response.StatusCode = StatusCodes.Status400BadRequest; + ctx.Response.StatusCode = StatusCodes.Status401Unauthorized; return; } @@ -86,7 +90,7 @@ public sealed class BattleNodeWebSocketHandler "WS upgrade for unknown BattleId={Bid} (decrypted viewerId={Vid}). " + "Bridge may not have minted this battle, or it was already consumed/expired.", battleId, viewerId); - ctx.Response.StatusCode = StatusCodes.Status400BadRequest; + ctx.Response.StatusCode = StatusCodes.Status404NotFound; return; } if (pending.ViewerId != viewerId) @@ -94,7 +98,7 @@ public sealed class BattleNodeWebSocketHandler _log.LogWarning( "WS upgrade viewer-id mismatch on BattleId={Bid}: bridge expected={Expected}, decrypted={Got}.", battleId, pending.ViewerId, viewerId); - ctx.Response.StatusCode = StatusCodes.Status400BadRequest; + ctx.Response.StatusCode = StatusCodes.Status401Unauthorized; return; }