Files
Campus-25/static/Servers/Campus/api/server/CdnDiag.cs
T
2026-10-14 06:59:51 +02:00

61 lines
3.2 KiB
C#

using System.Collections.Concurrent;
using Campus_live_rewrite.api.server.Classes.db_class;
namespace Campus_live_rewrite.api.server
{
/// <summary>
/// Avatar-bundle download diagnostics ("[cdn-diag]" in the journal). Answers "does clothing
/// load on platform X?" without a device log: a client that loads a bundle caches it
/// (BestHTTP disk cache) and downloads it ONCE; a client whose load fails re-downloads the
/// same bundle over and over (133 times an hour before the CDN-host fix, see
/// ConfigCampus.CdnBaseUrl). Bundle requests carry no auth, so the player is recovered from
/// the client IP their heartbeat last came from.
///
/// Logged per (client IP, bundle): the first download, then repeats 2, 3, 5, 10, 25, 50...
/// A repeat line is the failure signal. 304s (revalidation of a cached copy) are success.
/// </summary>
public static class CdnDiag
{
private sealed record Client(ulong Id, string Name, string Platform, string Device, DateTime Seen);
private static readonly ConcurrentDictionary<string, Client> _byIp = new();
private static readonly ConcurrentDictionary<string, int> _counts = new();
public static void NoteClient(HttpContext ctx, player_info account, int platform, int deviceClass)
{
try
{
string ip = LoginGuard.ClientIp(ctx);
if (string.IsNullOrEmpty(ip)) return;
string name = account.Username ?? account.DisplayName ?? account.Id.ToString();
string plat = Enum.IsDefined(typeof(Platforms), platform) ? ((Platforms)platform).ToString() : platform.ToString();
string dev = Enum.IsDefined(typeof(DeviceClasses), deviceClass) ? ((DeviceClasses)deviceClass).ToString() : deviceClass.ToString();
_byIp[ip] = new Client(account.Id, name, plat, dev, DateTime.UtcNow);
}
catch { }
}
public static void OnBundle(HttpContext ctx, string bundle, bool notModified)
{
try
{
string ip = LoginGuard.ClientIp(ctx);
int n = _counts.AddOrUpdate(ip + "|" + bundle, 1, (_, c) => c + 1);
if (!(n <= 3 || n == 5 || n == 10 || n == 25 || n % 50 == 0)) return;
string who = _byIp.TryGetValue(ip, out var c) && DateTime.UtcNow - c.Seen < TimeSpan.FromMinutes(10)
? $"{c.Name}#{c.Id} platform={c.Platform} device={c.Device}"
: "unknown player (no recent heartbeat from this IP)";
string ua = ctx.Request.Headers.UserAgent.ToString();
string range = ctx.Request.Headers.Range.ToString();
string verdict = notModified ? "304 cached copy revalidated (OK)"
: n == 1 ? "first download"
: $"REPEAT #{n} - this client is not keeping the bundle (load likely failing)";
Console.WriteLine($"[cdn-diag] {who} host={ctx.Request.Host.Host} bundle={bundle} {verdict}" +
(range.Length > 0 ? $" range={range}" : "") + (ua.Length > 0 ? $" ua=\"{ua}\"" : ""));
}
catch { }
}
}
}