M5: outbound requests, previews, the media proxy and served traffic
- HttpScope (AsyncLocal) tags each outbound request with a purpose and a trigger:
- purpose is set by the caller: actor, key, object, webfinger, context, nodeinfo;
- trigger is set by the job kind, by "verify" during inbox verification, or defaults
to "request".
- FederationHttp records every JSON, media and stream fetch: status, time, bytes, hops,
and an outcome of ok, refused or failed, with a reason: disallowed, remembered,
bad-redirect, too-many-redirects, content-type, too-large, bad-json, private-address,
timeout, network, or the status. A fetch a reader caused (trigger "request") is only
counted per server per day.
- Link previews record a 'preview' event: card, no-card or failed.
- The media proxy counts cache hits.
- TrafficMeter counts the client API per endpoint group, method and status class. It
counts our served documents (actor, outbox, collection, object, activity, licence,
webfinger, nodeinfo) by kind, status and whether signed, per day and never per server,
and never names a circle's collections.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELjqpznMFMNrJoJUj6K5p2
This commit is contained in:
1 parent
e247001bdb
commit
d37999970c
14 files changed
+487
-19
No files matched your search
@@ -48,6 +48,7 @@ namespace PrivaPub.Federation.Actors
|
||||
|
||||
public async Task<FetchedJson> FetchObject(string uri, CancellationToken token)
|
||||
{
|
||||
using var scope = HttpScope.Default("object");
|
||||
var signer = await _localActors.GetInstanceActor(token);
|
||||
var fetched = await Get(uri, signer, token);
|
||||
if (fetched == default)
|
||||
@@ -81,6 +82,7 @@ namespace PrivaPub.Federation.Actors
|
||||
if (!MayFetch(actorUri))
|
||||
return cached;
|
||||
|
||||
using var scope = HttpScope.For("actor");
|
||||
using var fetched = await FetchObject(actorUri, token);
|
||||
var actor = fetched == default ? default : ActorDocument.Parse(fetched.Root);
|
||||
if (actor == default)
|
||||
@@ -101,6 +103,7 @@ namespace PrivaPub.Federation.Actors
|
||||
if (!MayFetch(keyId))
|
||||
return cached;
|
||||
|
||||
using var scope = HttpScope.For("key");
|
||||
using var fetched = await FetchObject(StripFragment(keyId), token);
|
||||
if (fetched == default)
|
||||
return cached;
|
||||
@@ -129,6 +132,7 @@ namespace PrivaPub.Federation.Actors
|
||||
if (parts is not { Length: 2 } || string.IsNullOrEmpty(parts[0]) || string.IsNullOrEmpty(parts[1]))
|
||||
return default;
|
||||
|
||||
using var scope = HttpScope.For("webfinger");
|
||||
var query = $"/.well-known/webfinger?resource={Uri.EscapeDataString($"acct:{parts[0]}@{parts[1]}")}";
|
||||
using var fetched = await _http.GetJson($"https://{parts[1]}{query}", "application/jrd+json, application/json", sign: default, token)
|
||||
?? (_options?.CurrentValue.AllowPlainHttp == true
|
||||
|
||||
@@ -2,6 +2,7 @@ using PrivaPub.Federation.Actors;
|
||||
using PrivaPub.Federation.Moderation;
|
||||
using PrivaPub.Federation.Objects;
|
||||
using PrivaPub.Federation.Signing;
|
||||
using PrivaPub.Infrastructure.Http;
|
||||
using PrivaPub.Infrastructure.Jobs;
|
||||
using PrivaPub.Infrastructure.Statistics;
|
||||
using PrivaPub.Models.Federation;
|
||||
@@ -72,6 +73,7 @@ namespace PrivaPub.Federation.Inbox
|
||||
|
||||
public async Task<InboxResult> Receive(HttpRequest request, LocalActor recipient, CancellationToken token)
|
||||
{
|
||||
using var scope = HttpScope.Triggered("verify");
|
||||
var started = System.Diagnostics.Stopwatch.GetTimestamp();
|
||||
var receipt = new Receipt { Inbox = recipient == default ? "shared" : "personal", Bytes = request.ContentLength };
|
||||
InboxResult result = default;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Entities;
|
||||
|
||||
using PrivaPub.Infrastructure.Http;
|
||||
using PrivaPub.Domain.Content;
|
||||
using PrivaPub.Federation.Actors;
|
||||
using PrivaPub.Federation.Moderation;
|
||||
@@ -123,6 +124,7 @@ namespace PrivaPub.Federation.Inbox
|
||||
if (existing != default || depth > MaxDepth || await DB.Default.Find<DeletedObject>().Match(d => d.ObjectURI == objectUri).ExecuteAnyAsync(token))
|
||||
return existing;
|
||||
|
||||
using var scope = HttpScope.For("context");
|
||||
using var fetched = await _remoteActors.FetchObject(objectUri, token);
|
||||
var note = fetched == default ? default : NoteParser.Parse(JsonNode.Parse(fetched.Root.GetRawText()));
|
||||
if (note == default || !Origin.Same(note.Id, note.AttributedTo))
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace PrivaPub.Federation.Objects
|
||||
public async Task<JobOutcome> Handle(Job job, CancellationToken token)
|
||||
{
|
||||
var host = job.Payload;
|
||||
using var scope = HttpScope.For("nodeinfo");
|
||||
using var links = await _http.GetJson($"https://{host}/.well-known/nodeinfo", "application/json", sign: default, token);
|
||||
var href = links == default ? default : Schemas.Select(schema => LinkFor(links.Root, schema)).FirstOrDefault(link => link != default);
|
||||
if (href == default)
|
||||
|
||||
Reference in new issue
Block a user