78 lines
2.5 KiB
C#
78 lines
2.5 KiB
C#
using System.Diagnostics;
|
|||
|
|
|
||
|
|
namespace PrivaPub.Infrastructure.Statistics
|
||
|
|
{
|
||
|
|
public static class TrafficMeter
|
||
|
|
{
|
||
|
|
public static IApplicationBuilder UseTrafficMeter(this IApplicationBuilder app) =>
|
||
|
|
app.Use(async (context, next) =>
|
||
|
|
{
|
||
|
|
var started = Stopwatch.GetTimestamp();
|
||
|
|
try
|
||
|
|
{
|
||
|
|
await next(context);
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
Count(context, (int)Stopwatch.GetElapsedTime(started).TotalMilliseconds);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
static void Count(HttpContext context, int latencyMs)
|
||
|
|
{
|
||
|
|
var ledger = context.RequestServices?.GetService<IInteractionLedger>();
|
||
|
|
if (ledger == default)
|
||
|
|
return;
|
||
|
|
var path = context.Request.Path;
|
||
|
|
var template = (context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText;
|
||
|
|
var method = context.Request.Method;
|
||
|
|
var status = context.Response.StatusCode;
|
||
|
|
if (path.StartsWithSegments("/api") || path.StartsWithSegments("/oauth") || path.StartsWithSegments("/clientapi"))
|
||
|
|
{
|
||
|
|
ledger.CountServer(ServerSections.Client, $"{Group(template)}:{method}:{status / 100}xx", latencyMs);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (method != HttpMethods.Get && method != HttpMethods.Head)
|
||
|
|
return;
|
||
|
|
var served = Served(template, path);
|
||
|
|
if (served != default)
|
||
|
|
ledger.CountServer(ServerSections.Served,
|
||
|
|
$"{served}:{status}:{(context.Request.Headers.ContainsKey("Signature") || context.Request.Headers.ContainsKey("Signature-Input") ? "signed" : "unsigned")}");
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string Group(string template)
|
||
|
|
{
|
||
|
|
if (template == default)
|
||
|
|
return "unmatched";
|
||
|
|
var literals = template.Trim('/').Split('/', StringSplitOptions.RemoveEmptyEntries)
|
||
|
|
.TakeWhile(segment => !segment.Contains('{'))
|
||
|
|
.Take(3);
|
||
|
|
var group = string.Join('/', literals);
|
||
|
|
return group.Length == 0 ? "unmatched" : group;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string Served(string template, PathString path)
|
||
|
|
{
|
||
|
|
if (path.StartsWithSegments("/.well-known/webfinger"))
|
||
|
|
return "webfinger";
|
||
|
|
if (path.StartsWithSegments("/.well-known/nodeinfo") || path.StartsWithSegments("/nodeinfo"))
|
||
|
|
return "nodeinfo";
|
||
|
|
if (!path.StartsWithSegments("/peasants") && !path.StartsWithSegments("/users"))
|
||
|
|
return default;
|
||
|
|
var segments = (template ?? path.Value ?? string.Empty).Trim('/').Split('/');
|
||
|
|
return segments.Length switch
|
||
|
|
{
|
||
|
|
<= 2 => "actor",
|
||
|
|
_ => segments[2] switch
|
||
|
|
{
|
||
|
|
"anus" => "outbox",
|
||
|
|
"scribbles" or "whispers" => "object",
|
||
|
|
"grunts" => "activity",
|
||
|
|
"parrot-licences" => "licence",
|
||
|
|
"mouth" or "human-centipede" => default,
|
||
|
|
_ => "collection"
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|