From da7f98016620718775875f8c1bd9bfc24533e6a6 Mon Sep 17 00:00:00 2001 From: Alexa <53471801+Alexa-RR@users.noreply.github.com> Date: Tue, 28 Jul 2026 10:48:13 +0300 Subject: [PATCH] diag: log success response bodies for the routes that fail silently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the client rejects a response it still answers HTTP 200 and throws while reading it, so the server log shows a clean 200, the client shows a bare "Failed to copy room" / an unopened chat with no URL, and neither side records the payload. Every diagnosis then comes down to guessing which key the strict reader tripped on. Success bodies are now logged for /player, /thread and /clone, and the response cap is raised to 8000 chars for those paths — a room-details payload is a few KB and a 300-char truncation answers nothing. Everything else is unchanged: failures are still always read, successes still discarded, bodies still pass through RedactSecrets. Override with the Trace__SuccessBodyPaths env var (comma-separated substrings, empty to disable). --- .../Startup/RequestTracingExtensions.cs | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/DorkNet.Server/Startup/RequestTracingExtensions.cs b/DorkNet.Server/Startup/RequestTracingExtensions.cs index a7cd320..8a308e9 100644 --- a/DorkNet.Server/Startup/RequestTracingExtensions.cs +++ b/DorkNet.Server/Startup/RequestTracingExtensions.cs @@ -127,7 +127,13 @@ public static class RequestTracingExtensions var safeResponse = RedactSecrets(responseBody) ?? ""; var trimmedRequest = safeRequest is null ? null : (safeRequest.Length > 200 ? safeRequest[..200] + "..." : safeRequest); - var trimmedResponse = safeResponse.Length > 300 ? safeResponse[..300] + "..." : safeResponse; + // 300 chars is plenty to identify an error, but a room-details + // payload is a few KB and the whole point of capturing it is to + // see which key is missing — a truncated one answers nothing. + var responseCap = IsSuccessBodyPath(path) ? 8000 : 300; + var trimmedResponse = safeResponse.Length > responseCap + ? safeResponse[..responseCap] + "..." + : safeResponse; var authPresence = DescribeAuthPresence(ctx.Request); var level = thrown is not null || status >= 500 ? LogLevel.Error : status >= 400 ? LogLevel.Warning @@ -241,12 +247,38 @@ public static class RequestTracingExtensions || ext.Equals(".log", StringComparison.OrdinalIgnoreCase); } + /// Paths whose SUCCESS bodies are logged as well as their failures. + /// + /// Worth stating why this list has to exist: when the client rejects a + /// response it answers 200 and the client throws while reading it, so the + /// server log shows a clean 200 and the client shows a bare "Failed to …" + /// with no URL. Neither side records the payload, and without it every + /// diagnosis is guesswork about which key the strict reader tripped on. + /// + /// Override with Trace__SuccessBodyPaths (comma-separated substrings, + /// empty to disable). Bodies are still redacted and capped at 300 chars by + /// the caller. + private static readonly string[] SuccessBodyPaths = ResolveSuccessBodyPaths(); + + private static string[] ResolveSuccessBodyPaths() + { + var configured = Environment.GetEnvironmentVariable("Trace__SuccessBodyPaths"); + if (configured is null) + return ["/player", "/thread", "/clone"]; + + return configured + .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .ToArray(); + } + + private static bool IsSuccessBodyPath(string path) => + SuccessBodyPaths.Any(p => path.Contains(p, StringComparison.OrdinalIgnoreCase)); + private static bool ShouldReadResponseBody(string? contentType, int status, string path) { - // TEMP diagnostic: always read the /player roster body (even 200s) so - // [presence-debug] can dump the exact online payload the watch renders. - var isPresence = path.Equals("/player", StringComparison.OrdinalIgnoreCase); - if (status < 400 && !isPresence) return false; + // Failures are always read; successes only for the paths above, because + // a rejected-but-200 response is invisible otherwise. + if (status < 400 && !IsSuccessBodyPath(path)) return false; if (string.IsNullOrWhiteSpace(contentType)) return true; return contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("text/", StringComparison.OrdinalIgnoreCase);