using System; using System.Collections.Generic; using BestHTTP; using HarmonyLib; internal static class RequestRouteLog { private static readonly object Gate = new(); private static readonly HashSet Seen = new(); private const int Limit = 300; internal static void Apply(Harmony harmony) { try { var response = AccessTools.TypeByName("BestHTTP.HTTPResponse"); if (response == null) { RadicadeEacBypassPlugin.LogSource.LogWarning("[HttpLog] BestHTTP.HTTPResponse not found - REST routes stay unlogged."); return; } System.Reflection.ConstructorInfo constructor = null; foreach (var candidate in AccessTools.GetDeclaredConstructors(response)) { var parameters = candidate.GetParameters(); if (parameters.Length == 4 && parameters[0].ParameterType.Name.Contains("HTTPRequest") && parameters[2].ParameterType.Name == "Boolean" && parameters[3].ParameterType.Name == "Boolean") { constructor = candidate; break; } } if (constructor == null) { RadicadeEacBypassPlugin.LogSource.LogWarning("[HttpLog] HTTPResponse ctor not found - REST routes stay unlogged."); return; } harmony.Patch(constructor, postfix: new HarmonyMethod(typeof(RequestRouteLog), nameof(ResponsePostfix))); RadicadeEacBypassPlugin.LogSource.LogInfo("[HttpLog] hooked HTTPResponse(HTTPRequest, Stream) - every REST route and status is logged once"); } catch (Exception ex) { RadicadeEacBypassPlugin.LogSource.LogWarning($"[HttpLog] could not hook HTTPResponse: {ex.GetType().Name}: {ex.Message}"); } } private static void ResponsePostfix(HTTPResponse __instance, HTTPRequest __0) { try { var uri = __0?.Uri?.ToString(); if (string.IsNullOrEmpty(uri)) return; if (!HubSocketAuth.IsServer(uri)) return; var path = new Uri(uri).AbsolutePath; if (NameserverRedirect.IsHubUrl(uri)) return; if (path.Contains("/images/", StringComparison.OrdinalIgnoreCase)) return; if (path.Contains("/cdn/", StringComparison.OrdinalIgnoreCase)) return; int status; try { status = __instance.StatusCode; } catch { return; } var method = "REQ"; try { method = __0.MethodType.ToString().ToUpperInvariant(); } catch { } var line = $"{status} {method} {path}"; lock (Gate) { if (Seen.Count >= Limit || !Seen.Add(line)) return; } RadicadeEacBypassPlugin.LogSource.LogInfo($"[HttpLog] {line}"); } catch { } } }