Files
SocialPub/PrivaPub/Infrastructure/Statistics/TrafficMeter.cs
T

78 lines
2.5 KiB
C#
Raw Normal View History

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"
}
};
}
}
}