@fedify/fedify 1.3.0-dev.575 → 1.3.0-dev.577
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/deno.js +1 -1
- package/esm/federation/handler.js +154 -59
- package/esm/federation/inbox.js +5 -2
- package/esm/federation/middleware.js +105 -63
- package/esm/sig/http.js +1 -1
- package/esm/sig/key.js +35 -1
- package/esm/vocab/vocab.js +173 -173
- package/package.json +1 -1
- package/types/federation/handler.d.ts +1 -1
- package/types/federation/handler.d.ts.map +1 -1
- package/types/federation/inbox.d.ts +4 -0
- package/types/federation/inbox.d.ts.map +1 -1
- package/types/federation/middleware.d.ts.map +1 -1
- package/types/federation/queue.d.ts +1 -0
- package/types/federation/queue.d.ts.map +1 -1
- package/types/sig/key.d.ts +2 -2
- package/types/sig/key.d.ts.map +1 -1
package/esm/deno.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import * as dntShim from "../_dnt.shims.js";
|
2
2
|
import { getLogger } from "@logtape/logtape";
|
3
|
-
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
3
|
+
import { context, propagation, SpanKind, SpanStatusCode, trace, } from "@opentelemetry/api";
|
4
4
|
import { accepts } from "../deps/jsr.io/@std/http/1.0.11/negotiation.js";
|
5
5
|
import metadata from "../deno.js";
|
6
6
|
import { verifyRequest } from "../sig/http.js";
|
@@ -231,21 +231,55 @@ function filterCollectionItems(items, collectionName, filterPredicate) {
|
|
231
231
|
}
|
232
232
|
return result;
|
233
233
|
}
|
234
|
-
export async function handleInbox(request,
|
234
|
+
export async function handleInbox(request, options) {
|
235
|
+
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
236
|
+
const tracer = tracerProvider.getTracer(metadata.name, metadata.version);
|
237
|
+
return await tracer.startActiveSpan("activitypub.inbox", {
|
238
|
+
kind: options.queue == null ? SpanKind.SERVER : SpanKind.PRODUCER,
|
239
|
+
attributes: { "activitypub.shared_inbox": options.recipient == null },
|
240
|
+
}, async (span) => {
|
241
|
+
if (options.recipient != null) {
|
242
|
+
span.setAttribute("fedify.inbox.recipient", options.recipient);
|
243
|
+
}
|
244
|
+
try {
|
245
|
+
return await handleInboxInternal(request, options, span);
|
246
|
+
}
|
247
|
+
catch (e) {
|
248
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: String(e) });
|
249
|
+
throw e;
|
250
|
+
}
|
251
|
+
finally {
|
252
|
+
span.end();
|
253
|
+
}
|
254
|
+
});
|
255
|
+
}
|
256
|
+
async function handleInboxInternal(request, { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider, }, span) {
|
235
257
|
const logger = getLogger(["fedify", "federation", "inbox"]);
|
236
258
|
if (actorDispatcher == null) {
|
237
259
|
logger.error("Actor dispatcher is not set.", { recipient });
|
260
|
+
span.setStatus({
|
261
|
+
code: SpanStatusCode.ERROR,
|
262
|
+
message: "Actor dispatcher is not set.",
|
263
|
+
});
|
238
264
|
return await onNotFound(request);
|
239
265
|
}
|
240
266
|
else if (recipient != null) {
|
241
|
-
const actor = await actorDispatcher(
|
267
|
+
const actor = await actorDispatcher(ctx, recipient);
|
242
268
|
if (actor == null) {
|
243
269
|
logger.error("Actor {recipient} not found.", { recipient });
|
270
|
+
span.setStatus({
|
271
|
+
code: SpanStatusCode.ERROR,
|
272
|
+
message: `Actor ${recipient} not found.`,
|
273
|
+
});
|
244
274
|
return await onNotFound(request);
|
245
275
|
}
|
246
276
|
}
|
247
277
|
if (request.bodyUsed) {
|
248
278
|
logger.error("Request body has already been read.", { recipient });
|
279
|
+
span.setStatus({
|
280
|
+
code: SpanStatusCode.ERROR,
|
281
|
+
message: "Request body has already been read.",
|
282
|
+
});
|
249
283
|
return new Response("Internal server error.", {
|
250
284
|
status: 500,
|
251
285
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
@@ -253,6 +287,10 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
253
287
|
}
|
254
288
|
else if (request.body?.locked) {
|
255
289
|
logger.error("Request body is locked.", { recipient });
|
290
|
+
span.setStatus({
|
291
|
+
code: SpanStatusCode.ERROR,
|
292
|
+
message: "Request body is locked.",
|
293
|
+
});
|
256
294
|
return new Response("Internal server error.", {
|
257
295
|
status: 500,
|
258
296
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
@@ -265,11 +303,15 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
265
303
|
catch (error) {
|
266
304
|
logger.error("Failed to parse JSON:\n{error}", { recipient, error });
|
267
305
|
try {
|
268
|
-
await inboxErrorHandler?.(
|
306
|
+
await inboxErrorHandler?.(ctx, error);
|
269
307
|
}
|
270
308
|
catch (error) {
|
271
309
|
logger.error("An unexpected error occurred in inbox error handler:\n{error}", { error, activity: json, recipient });
|
272
310
|
}
|
311
|
+
span.setStatus({
|
312
|
+
code: SpanStatusCode.ERROR,
|
313
|
+
message: `Failed to parse JSON:\n${error}`,
|
314
|
+
});
|
273
315
|
return new Response("Invalid JSON.", {
|
274
316
|
status: 400,
|
275
317
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
@@ -288,7 +330,7 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
288
330
|
return undefined;
|
289
331
|
let object;
|
290
332
|
try {
|
291
|
-
object = await Object.fromJsonLd(serialized,
|
333
|
+
object = await Object.fromJsonLd(serialized, ctx);
|
292
334
|
}
|
293
335
|
catch {
|
294
336
|
await kv.delete([...kvPrefixes.publicKey, keyId.href]);
|
@@ -307,13 +349,13 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
307
349
|
return;
|
308
350
|
}
|
309
351
|
this.nullKeys.delete(keyId.href);
|
310
|
-
const serialized = await key.toJsonLd(
|
352
|
+
const serialized = await key.toJsonLd(ctx);
|
311
353
|
await kv.set([...kvPrefixes.publicKey, keyId.href], serialized);
|
312
354
|
},
|
313
355
|
};
|
314
356
|
const ldSigVerified = await verifyJsonLd(json, {
|
315
|
-
contextLoader:
|
316
|
-
documentLoader:
|
357
|
+
contextLoader: ctx.contextLoader,
|
358
|
+
documentLoader: ctx.documentLoader,
|
317
359
|
keyCache,
|
318
360
|
tracerProvider,
|
319
361
|
});
|
@@ -321,14 +363,14 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
321
363
|
let activity = null;
|
322
364
|
if (ldSigVerified) {
|
323
365
|
logger.debug("Linked Data Signatures are verified.", { recipient, json });
|
324
|
-
activity = await Activity.fromJsonLd(jsonWithoutSig,
|
366
|
+
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
325
367
|
}
|
326
368
|
else {
|
327
369
|
logger.debug("Linked Data Signatures are not verified.", { recipient, json });
|
328
370
|
try {
|
329
371
|
activity = await verifyObject(Activity, jsonWithoutSig, {
|
330
|
-
contextLoader:
|
331
|
-
documentLoader:
|
372
|
+
contextLoader: ctx.contextLoader,
|
373
|
+
documentLoader: ctx.documentLoader,
|
332
374
|
keyCache,
|
333
375
|
tracerProvider,
|
334
376
|
});
|
@@ -340,11 +382,15 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
340
382
|
error,
|
341
383
|
});
|
342
384
|
try {
|
343
|
-
await inboxErrorHandler?.(
|
385
|
+
await inboxErrorHandler?.(ctx, error);
|
344
386
|
}
|
345
387
|
catch (error) {
|
346
388
|
logger.error("An unexpected error occurred in inbox error handler:\n{error}", { error, activity: json, recipient });
|
347
389
|
}
|
390
|
+
span.setStatus({
|
391
|
+
code: SpanStatusCode.ERROR,
|
392
|
+
message: `Failed to parse activity:\n${error}`,
|
393
|
+
});
|
348
394
|
return new Response("Invalid activity.", {
|
349
395
|
status: 400,
|
350
396
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
@@ -361,14 +407,18 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
361
407
|
if (activity == null) {
|
362
408
|
if (!skipSignatureVerification) {
|
363
409
|
const key = await verifyRequest(request, {
|
364
|
-
contextLoader:
|
365
|
-
documentLoader:
|
410
|
+
contextLoader: ctx.contextLoader,
|
411
|
+
documentLoader: ctx.documentLoader,
|
366
412
|
timeWindow: signatureTimeWindow,
|
367
413
|
keyCache,
|
368
414
|
tracerProvider,
|
369
415
|
});
|
370
416
|
if (key == null) {
|
371
417
|
logger.error("Failed to verify the request's HTTP Signatures.", { recipient });
|
418
|
+
span.setStatus({
|
419
|
+
code: SpanStatusCode.ERROR,
|
420
|
+
message: `Failed to verify the request's HTTP Signatures.`,
|
421
|
+
});
|
372
422
|
const response = new Response("Failed to verify the request signature.", {
|
373
423
|
status: 401,
|
374
424
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
@@ -380,8 +430,12 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
380
430
|
}
|
381
431
|
httpSigKey = key;
|
382
432
|
}
|
383
|
-
activity = await Activity.fromJsonLd(jsonWithoutSig,
|
433
|
+
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
434
|
+
}
|
435
|
+
if (activity.id != null) {
|
436
|
+
span.setAttribute("activitypub.activity.id", activity.id.href);
|
384
437
|
}
|
438
|
+
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
|
385
439
|
const cacheKey = activity.id == null
|
386
440
|
? null
|
387
441
|
: [...kvPrefixes.activityIdempotence, activity.id.href];
|
@@ -393,6 +447,10 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
393
447
|
activity: json,
|
394
448
|
recipient,
|
395
449
|
});
|
450
|
+
span.setStatus({
|
451
|
+
code: SpanStatusCode.UNSET,
|
452
|
+
message: `Activity ${activity.id?.href} has already been processed.`,
|
453
|
+
});
|
396
454
|
return new Response(`Activity <${activity.id}> has already been processed.`, {
|
397
455
|
status: 202,
|
398
456
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
@@ -401,83 +459,120 @@ export async function handleInbox(request, { recipient, context, inboxContextFac
|
|
401
459
|
}
|
402
460
|
if (activity.actorId == null) {
|
403
461
|
logger.error("Missing actor.", { activity: json });
|
404
|
-
|
462
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: "Missing actor." });
|
463
|
+
return new Response("Missing actor.", {
|
405
464
|
status: 400,
|
406
465
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
407
466
|
});
|
408
|
-
return response;
|
409
467
|
}
|
410
|
-
|
468
|
+
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
469
|
+
if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
|
411
470
|
logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
|
412
471
|
activity: json,
|
413
472
|
recipient,
|
414
473
|
keyId: httpSigKey.id?.href,
|
415
474
|
actorId: activity.actorId.href,
|
416
475
|
});
|
417
|
-
|
476
|
+
span.setStatus({
|
477
|
+
code: SpanStatusCode.ERROR,
|
478
|
+
message: `The signer (${httpSigKey.id?.href}) and ` +
|
479
|
+
`the actor (${activity.actorId.href}) do not match.`,
|
480
|
+
});
|
481
|
+
return new Response("The signer and the actor do not match.", {
|
418
482
|
status: 401,
|
419
483
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
420
484
|
});
|
421
|
-
return response;
|
422
485
|
}
|
423
486
|
if (queue != null) {
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
487
|
+
const carrier = {};
|
488
|
+
propagation.inject(context.active(), carrier);
|
489
|
+
try {
|
490
|
+
await queue.enqueue({
|
491
|
+
type: "inbox",
|
492
|
+
id: dntShim.crypto.randomUUID(),
|
493
|
+
baseUrl: request.url,
|
494
|
+
activity: json,
|
495
|
+
identifier: recipient,
|
496
|
+
attempt: 0,
|
497
|
+
started: new Date().toISOString(),
|
498
|
+
traceContext: carrier,
|
499
|
+
});
|
500
|
+
}
|
501
|
+
catch (error) {
|
502
|
+
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", { error, activityId: activity.id?.href, activity: json, recipient });
|
503
|
+
span.setStatus({
|
504
|
+
code: SpanStatusCode.ERROR,
|
505
|
+
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`,
|
506
|
+
});
|
507
|
+
throw error;
|
508
|
+
}
|
433
509
|
logger.info("Activity {activityId} is enqueued.", { activityId: activity.id?.href, activity: json, recipient });
|
434
510
|
return new Response("Activity is enqueued.", {
|
435
511
|
status: 202,
|
436
512
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
437
513
|
});
|
438
514
|
}
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
515
|
+
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
516
|
+
const tracer = tracerProvider.getTracer(metadata.name, metadata.version);
|
517
|
+
const response = await tracer.startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
518
|
+
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
519
|
+
if (dispatched == null) {
|
520
|
+
logger.error("Unsupported activity type:\n{activity}", { activity: json, recipient });
|
521
|
+
span.setStatus({
|
522
|
+
code: SpanStatusCode.UNSET,
|
523
|
+
message: `Unsupported activity type: ${getTypeId(activity).href}`,
|
524
|
+
});
|
525
|
+
span.end();
|
526
|
+
return new Response("", {
|
527
|
+
status: 202,
|
528
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
529
|
+
});
|
530
|
+
}
|
531
|
+
const { class: cls, listener } = dispatched;
|
532
|
+
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
451
533
|
try {
|
452
|
-
await
|
534
|
+
await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
|
453
535
|
}
|
454
536
|
catch (error) {
|
455
|
-
|
537
|
+
try {
|
538
|
+
await inboxErrorHandler?.(ctx, error);
|
539
|
+
}
|
540
|
+
catch (error) {
|
541
|
+
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
542
|
+
error,
|
543
|
+
activityId: activity.id?.href,
|
544
|
+
activity: json,
|
545
|
+
recipient,
|
546
|
+
});
|
547
|
+
}
|
548
|
+
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
456
549
|
error,
|
457
550
|
activityId: activity.id?.href,
|
458
551
|
activity: json,
|
459
552
|
recipient,
|
460
553
|
});
|
554
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: String(error) });
|
555
|
+
span.end();
|
556
|
+
return new Response("Internal server error.", {
|
557
|
+
status: 500,
|
558
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
559
|
+
});
|
461
560
|
}
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
});
|
468
|
-
|
469
|
-
|
561
|
+
if (cacheKey != null) {
|
562
|
+
await kv.set(cacheKey, true, {
|
563
|
+
ttl: dntShim.Temporal.Duration.from({ days: 1 }),
|
564
|
+
});
|
565
|
+
}
|
566
|
+
logger.info("Activity {activityId} has been processed.", { activityId: activity.id?.href, activity: json, recipient });
|
567
|
+
span.end();
|
568
|
+
return new Response("", {
|
569
|
+
status: 202,
|
470
570
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
471
571
|
});
|
472
|
-
}
|
473
|
-
if (cacheKey != null) {
|
474
|
-
await kv.set(cacheKey, true, { ttl: dntShim.Temporal.Duration.from({ days: 1 }) });
|
475
|
-
}
|
476
|
-
logger.info("Activity {activityId} has been processed.", { activityId: activity.id?.href, activity: json, recipient });
|
477
|
-
return new Response("", {
|
478
|
-
status: 202,
|
479
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
480
572
|
});
|
573
|
+
if (response.status >= 500)
|
574
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
575
|
+
return response;
|
481
576
|
}
|
482
577
|
/**
|
483
578
|
* Responds with the given object in JSON-LD format.
|
package/esm/federation/inbox.js
CHANGED
@@ -12,7 +12,7 @@ export class InboxListenerSet {
|
|
12
12
|
}
|
13
13
|
this.#listeners.set(type, listener);
|
14
14
|
}
|
15
|
-
|
15
|
+
dispatchWithClass(activity) {
|
16
16
|
// deno-lint-ignore no-explicit-any
|
17
17
|
let cls = activity
|
18
18
|
// deno-lint-ignore no-explicit-any
|
@@ -29,6 +29,9 @@ export class InboxListenerSet {
|
|
29
29
|
cls = globalThis.Object.getPrototypeOf(cls);
|
30
30
|
}
|
31
31
|
const listener = inboxListeners.get(cls);
|
32
|
-
return listener;
|
32
|
+
return { class: cls, listener };
|
33
|
+
}
|
34
|
+
dispatch(activity) {
|
35
|
+
return this.dispatchWithClass(activity)?.listener ?? null;
|
33
36
|
}
|
34
37
|
}
|
@@ -158,18 +158,21 @@ export class FederationImpl {
|
|
158
158
|
}
|
159
159
|
#listenQueue(ctxData, message) {
|
160
160
|
const tracer = this.#getTracer();
|
161
|
+
const extractedContext = propagation.extract(context.active(), message.traceContext);
|
161
162
|
return withContext({ messageId: message.id }, async () => {
|
162
163
|
if (message.type === "outbox") {
|
163
|
-
const extractedContext = propagation.extract(context.active(), message.traceContext);
|
164
164
|
await tracer.startActiveSpan("activitypub.outbox", {
|
165
165
|
kind: SpanKind.CONSUMER,
|
166
|
-
attributes: {
|
166
|
+
attributes: {
|
167
|
+
"activitypub.activity.type": message.activityType,
|
168
|
+
"activitypub.activity.retries": message.attempt,
|
169
|
+
},
|
167
170
|
}, extractedContext, async (span) => {
|
168
171
|
if (message.activityId != null) {
|
169
172
|
span.setAttribute("activitypub.activity.id", message.activityId);
|
170
173
|
}
|
171
174
|
try {
|
172
|
-
await this.#listenOutboxMessage(ctxData, message);
|
175
|
+
await this.#listenOutboxMessage(ctxData, message, span);
|
173
176
|
}
|
174
177
|
catch (e) {
|
175
178
|
span.setStatus({
|
@@ -184,11 +187,30 @@ export class FederationImpl {
|
|
184
187
|
});
|
185
188
|
}
|
186
189
|
else if (message.type === "inbox") {
|
187
|
-
await
|
190
|
+
await tracer.startActiveSpan("activitypub.inbox", {
|
191
|
+
kind: SpanKind.CONSUMER,
|
192
|
+
attributes: {
|
193
|
+
"activitypub.shared_inbox": message.identifier == null,
|
194
|
+
},
|
195
|
+
}, extractedContext, async (span) => {
|
196
|
+
try {
|
197
|
+
await this.#listenInboxMessage(ctxData, message, span);
|
198
|
+
}
|
199
|
+
catch (e) {
|
200
|
+
span.setStatus({
|
201
|
+
code: SpanStatusCode.ERROR,
|
202
|
+
message: String(e),
|
203
|
+
});
|
204
|
+
throw e;
|
205
|
+
}
|
206
|
+
finally {
|
207
|
+
span.end();
|
208
|
+
}
|
209
|
+
});
|
188
210
|
}
|
189
211
|
});
|
190
212
|
}
|
191
|
-
async #listenOutboxMessage(_, message) {
|
213
|
+
async #listenOutboxMessage(_, message, span) {
|
192
214
|
const logger = getLogger(["fedify", "federation", "outbox"]);
|
193
215
|
const logData = {
|
194
216
|
keyIds: message.keys.map((pair) => pair.keyId),
|
@@ -224,6 +246,7 @@ export class FederationImpl {
|
|
224
246
|
});
|
225
247
|
}
|
226
248
|
catch (error) {
|
249
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: String(error) });
|
227
250
|
const activity = await Activity.fromJsonLd(message.activity, {
|
228
251
|
contextLoader: this.contextLoader,
|
229
252
|
documentLoader: rsaKeyPair == null
|
@@ -261,7 +284,7 @@ export class FederationImpl {
|
|
261
284
|
}
|
262
285
|
logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
|
263
286
|
}
|
264
|
-
async #listenInboxMessage(ctxData, message) {
|
287
|
+
async #listenInboxMessage(ctxData, message, span) {
|
265
288
|
const logger = getLogger(["fedify", "federation", "inbox"]);
|
266
289
|
const baseUrl = new URL(message.baseUrl);
|
267
290
|
let context = this.#createContext(baseUrl, ctxData);
|
@@ -284,6 +307,10 @@ export class FederationImpl {
|
|
284
307
|
}
|
285
308
|
}
|
286
309
|
const activity = await Activity.fromJsonLd(message.activity, context);
|
310
|
+
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
|
311
|
+
if (activity.id != null) {
|
312
|
+
span.setAttribute("activitypub.activity.id", activity.id.href);
|
313
|
+
}
|
287
314
|
const cacheKey = activity.id == null ? null : [
|
288
315
|
...this.kvPrefixes.activityIdempotence,
|
289
316
|
activity.id.href,
|
@@ -299,74 +326,89 @@ export class FederationImpl {
|
|
299
326
|
return;
|
300
327
|
}
|
301
328
|
}
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
activity: message.activity,
|
307
|
-
recipient: message.identifier,
|
308
|
-
trial: message.attempt,
|
309
|
-
});
|
310
|
-
return;
|
311
|
-
}
|
312
|
-
try {
|
313
|
-
await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, getTypeId(activity).href), activity);
|
314
|
-
}
|
315
|
-
catch (error) {
|
316
|
-
try {
|
317
|
-
await this.inboxErrorHandler?.(context, error);
|
318
|
-
}
|
319
|
-
catch (error) {
|
320
|
-
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
321
|
-
error,
|
322
|
-
trial: message.attempt,
|
329
|
+
await this.#getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
330
|
+
const dispatched = this.inboxListeners?.dispatchWithClass(activity);
|
331
|
+
if (dispatched == null) {
|
332
|
+
logger.error("Unsupported activity type:\n{activity}", {
|
323
333
|
activityId: activity.id?.href,
|
324
334
|
activity: message.activity,
|
325
335
|
recipient: message.identifier,
|
336
|
+
trial: message.attempt,
|
337
|
+
});
|
338
|
+
span.setStatus({
|
339
|
+
code: SpanStatusCode.ERROR,
|
340
|
+
message: `Unsupported activity type: ${getTypeId(activity).href}`,
|
326
341
|
});
|
342
|
+
span.end();
|
343
|
+
return;
|
327
344
|
}
|
328
|
-
const
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
error
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
345
|
+
const { class: cls, listener } = dispatched;
|
346
|
+
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
347
|
+
try {
|
348
|
+
await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, getTypeId(activity).href), activity);
|
349
|
+
}
|
350
|
+
catch (error) {
|
351
|
+
try {
|
352
|
+
await this.inboxErrorHandler?.(context, error);
|
353
|
+
}
|
354
|
+
catch (error) {
|
355
|
+
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
356
|
+
error,
|
357
|
+
trial: message.attempt,
|
358
|
+
activityId: activity.id?.href,
|
359
|
+
activity: message.activity,
|
360
|
+
recipient: message.identifier,
|
361
|
+
});
|
362
|
+
}
|
363
|
+
const delay = this.inboxRetryPolicy({
|
364
|
+
elapsedTime: dntShim.Temporal.Instant.from(message.started).until(dntShim.Temporal.Now.instant()),
|
365
|
+
attempts: message.attempt,
|
340
366
|
});
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
:
|
367
|
+
if (delay != null) {
|
368
|
+
logger.error("Failed to process the incoming activity {activityId} (attempt " +
|
369
|
+
"#{attempt}); retry...:\n{error}", {
|
370
|
+
error,
|
371
|
+
attempt: message.attempt,
|
372
|
+
activityId: activity.id?.href,
|
373
|
+
activity: message.activity,
|
374
|
+
recipient: message.identifier,
|
375
|
+
});
|
376
|
+
await this.inboxQueue?.enqueue({
|
377
|
+
...message,
|
378
|
+
attempt: message.attempt + 1,
|
379
|
+
}, {
|
380
|
+
delay: dntShim.Temporal.Duration.compare(delay, { seconds: 0 }) < 0
|
381
|
+
? dntShim.Temporal.Duration.from({ seconds: 0 })
|
382
|
+
: delay,
|
383
|
+
});
|
384
|
+
}
|
385
|
+
else {
|
386
|
+
logger.error("Failed to process the incoming activity {activityId} after " +
|
387
|
+
"{trial} attempts; giving up:\n{error}", {
|
388
|
+
error,
|
389
|
+
activityId: activity.id?.href,
|
390
|
+
activity: message.activity,
|
391
|
+
recipient: message.identifier,
|
392
|
+
});
|
393
|
+
}
|
394
|
+
span.setStatus({
|
395
|
+
code: SpanStatusCode.ERROR,
|
396
|
+
message: String(error),
|
348
397
|
});
|
398
|
+
span.end();
|
399
|
+
return;
|
349
400
|
}
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
error,
|
354
|
-
activityId: activity.id?.href,
|
355
|
-
activity: message.activity,
|
356
|
-
recipient: message.identifier,
|
401
|
+
if (cacheKey != null) {
|
402
|
+
await this.kv.set(cacheKey, true, {
|
403
|
+
ttl: dntShim.Temporal.Duration.from({ days: 1 }),
|
357
404
|
});
|
358
405
|
}
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
ttl: dntShim.Temporal.Duration.from({ days: 1 }),
|
406
|
+
logger.info("Activity {activityId} has been processed.", {
|
407
|
+
activityId: activity.id?.href,
|
408
|
+
activity: message.activity,
|
409
|
+
recipient: message.identifier,
|
364
410
|
});
|
365
|
-
|
366
|
-
logger.info("Activity {activityId} has been processed.", {
|
367
|
-
activityId: activity.id?.href,
|
368
|
-
activity: message.activity,
|
369
|
-
recipient: message.identifier,
|
411
|
+
span.end();
|
370
412
|
});
|
371
413
|
}
|
372
414
|
startQueue(contextData, options = {}) {
|
package/esm/sig/http.js
CHANGED
@@ -236,7 +236,6 @@ async function verifyRequestInternal(request, span, { documentLoader, contextLoa
|
|
236
236
|
}
|
237
237
|
const { keyId, headers, signature } = sigValues;
|
238
238
|
span?.setAttribute("http_signatures.key_id", keyId);
|
239
|
-
span?.setAttribute("http_signatures.signature", signature);
|
240
239
|
if ("algorithm" in sigValues) {
|
241
240
|
span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
|
242
241
|
}
|
@@ -266,6 +265,7 @@ async function verifyRequestInternal(request, span, { documentLoader, contextLoa
|
|
266
265
|
? request.headers.get("host") ?? new URL(request.url).host
|
267
266
|
: request.headers.get(name))).join("\n");
|
268
267
|
const sig = decodeBase64(signature);
|
268
|
+
span?.setAttribute("http_signatures.signature", encodeHex(sig));
|
269
269
|
// TODO: support other than RSASSA-PKCS1-v1_5:
|
270
270
|
const verified = await dntShim.crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message));
|
271
271
|
if (!verified) {
|