@fedify/fedify 2.5.0-dev.2448 → 2.5.0-dev.2492
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/README.md +3 -0
- package/dist/{builder-DbC8bn6k.mjs → builder-DphK9v7f.mjs} +2 -2
- package/dist/{client-D-XP_Ccb.d.cts → client-3InDZbba.d.cts} +0 -1
- package/dist/{client-D-XP_Ccb.d.ts → client-3InDZbba.d.ts} +0 -1
- package/dist/{client-CEnNz8O7.mjs → client-5SIFzW3q.mjs} +113 -64
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-heeXW2_A.mjs → compound-proof-cYtvlTgF.mjs} +2 -2
- package/dist/{context-GlHrzdSP.d.ts → context-DQJE9YfH.d.ts} +29 -3
- package/dist/{context-BazwQrHn.d.cts → context-DSuL3YWx.d.cts} +29 -3
- package/dist/{deno-Dpewt_jJ.mjs → deno-iOAA7iRN.mjs} +1 -1
- package/dist/docloader-DCZkiTeF.mjs +136 -0
- package/dist/{exporter-DThFfe7P.mjs → exporter-DVG68ozH.mjs} +36 -30
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +6 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-actorless.test.mjs +5 -5
- package/dist/federation/inbox-report.test.mjs +5 -5
- package/dist/federation/inbox-view.test.mjs +1 -1
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +468 -33
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +1 -1
- package/dist/federation/outgoing-proof.test.mjs +9 -6
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +4 -4
- package/dist/federation/portable-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +11 -5
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +502 -93
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-D8YexkP1.cjs → federation-D0AZw_aw.cjs} +246 -108
- package/dist/{http-BZkyQIkb.mjs → http-BoJCMnjY.mjs} +204 -106
- package/dist/{http-BBr-VLil.js → http-ChXUU_3c.js} +178 -109
- package/dist/{http-CAl7Num5.cjs → http-kv1LZw-r.cjs} +179 -110
- package/dist/{inbox-wDuMKaFW.mjs → inbox-BjihTKLo.mjs} +2 -2
- package/dist/{inbox-view-NE9wXdLo.mjs → inbox-view-D-skypg-.mjs} +1 -1
- package/dist/{key-nYGY4QB9.d.cts → key-DbLNT-vA.d.ts} +1 -2
- package/dist/{key-DhsBxxZN.d.ts → key-DlHT4R6k.d.cts} +1 -2
- package/dist/{kv-cache-CTQSfkmp.mjs → kv-cache-Cmsyke4d.mjs} +1 -1
- package/dist/{kv-cache-U7uz5nx4.js → kv-cache-DQcte7ES.js} +73 -28
- package/dist/{kv-cache-CjgCOz3i.cjs → kv-cache-Ddh5sMxH.cjs} +72 -27
- package/dist/{ld-Cel1RRZ5.d.cts → ld-CWVpo_NM.d.cts} +1 -1
- package/dist/{ld-CQFuiah9.mjs → ld-CdvcOEXQ.mjs} +2 -2
- package/dist/{ld-CcuZ4NZa.d.ts → ld-Inagww4R.d.ts} +1 -1
- package/dist/{metrics-B-QxRSSR.mjs → metrics-CpxvUrBz.mjs} +1 -1
- package/dist/{middleware-P-AZEQMn.js → middleware-BHb5toqA.js} +247 -109
- package/dist/{middleware-BdlNa8BD.mjs → middleware-BKs2DLUF.mjs} +1 -1
- package/dist/{middleware-CuVa0UuS.mjs → middleware-CoBLrTKX.mjs} +89 -49
- package/dist/{mod-DvWOv2Ho.d.cts → mod-6TQ4UQZb.d.cts} +1 -1
- package/dist/{mod-BHQE-7WQ.d.ts → mod-B4kBw1-8.d.ts} +1 -1
- package/dist/{mod-DkhfZC1u.d.ts → mod-B7j30IQf.d.ts} +2 -9
- package/dist/{mod-B2783_3v.d.cts → mod-C1nT74IG.d.cts} +1 -1
- package/dist/{mod-DjZfJj9R.d.ts → mod-CIeBgsOC.d.ts} +1 -1
- package/dist/{mod-B5-wdzmJ.d.cts → mod-YvA4Rk8J.d.cts} +2 -9
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +7 -7
- package/dist/mod.d.ts +7 -7
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +337 -19
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/otel/exporter.test.mjs +257 -1
- package/dist/otel/mod.cjs +36 -30
- package/dist/otel/mod.d.cts +13 -0
- package/dist/otel/mod.d.ts +13 -0
- package/dist/otel/mod.js +36 -30
- package/dist/{owner-ccKkQOAf.mjs → owner-DTnVpUKA.mjs} +3 -3
- package/dist/{portable-collection-CndnpXfo.mjs → portable-collection-CJeYTL61.mjs} +2 -2
- package/dist/{portable-collection-TC-kvkVI.cjs → portable-collection-DciwBIia.cjs} +1 -1
- package/dist/{portable-collection-DUlQ3s2M.js → portable-collection-TmtpzD4E.js} +1 -1
- package/dist/{portable-key-D5tFOoV-.mjs → portable-key-DcYok4ki.mjs} +1 -1
- package/dist/{proof-DPoF06nz.mjs → proof-GH7Poos_.mjs} +3 -3
- package/dist/{send-CF44O1Df.mjs → send-BgGJh6pv.mjs} +174 -76
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +553 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +1 -1
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +4 -4
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.mjs +3 -3
- package/dist/{temporal-XlZ4Wb94.mjs → temporal-gJINzcL4.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +26 -0
- package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
- package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
- package/dist/utils/docloader.test.mjs +344 -36
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +8 -8
- package/dist/docloader-CpsDyqVN.mjs +0 -91
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
|
|
5
5
|
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-IG3pHfvZ.mjs";
|
|
6
6
|
import "../../testing-Dbr21vZk.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
7
|
+
import { o as createFederation } from "../../middleware-CoBLrTKX.mjs";
|
|
8
8
|
import { n as ParallelMessageQueue } from "../../mq-eF96QxUT.mjs";
|
|
9
9
|
import { configure, reset } from "@logtape/logtape";
|
|
10
10
|
import { delay } from "es-toolkit";
|
|
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
|
|
5
5
|
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-IG3pHfvZ.mjs";
|
|
6
6
|
import "../../testing-Dbr21vZk.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
8
|
-
import { r as createFederationBuilder } from "../../builder-
|
|
7
|
+
import { o as createFederation } from "../../middleware-CoBLrTKX.mjs";
|
|
8
|
+
import { r as createFederationBuilder } from "../../builder-DphK9v7f.mjs";
|
|
9
9
|
import { t as TaskCodec } from "../../codec-C6rTrpIp.mjs";
|
|
10
10
|
import { t as InProcessMessageQueue } from "../../mq-eF96QxUT.mjs";
|
|
11
11
|
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
5
5
|
import "../std__assert-DvMDRPes.mjs";
|
|
6
6
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
7
|
-
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-
|
|
7
|
+
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-gJINzcL4.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/federation/temporal.test.ts
|
|
10
10
|
test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
|
|
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
|
6
6
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
7
7
|
import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
|
-
import { o as createFederation, s as handleWebFinger } from "../middleware-
|
|
9
|
+
import { o as createFederation, s as handleWebFinger } from "../middleware-CoBLrTKX.mjs";
|
|
10
10
|
import { Image, Link, Person, Tombstone } from "@fedify/vocab";
|
|
11
11
|
import { parseIri } from "@fedify/vocab-runtime";
|
|
12
12
|
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
3
|
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
5
|
-
const require_types = require("./types-
|
|
6
|
-
const require_portable_collection = require("./portable-collection-
|
|
7
|
-
const require_kv_cache = require("./kv-cache-
|
|
4
|
+
const require_http = require("./http-kv1LZw-r.cjs");
|
|
5
|
+
const require_types = require("./types-hKCCjii0.cjs");
|
|
6
|
+
const require_portable_collection = require("./portable-collection-DciwBIia.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-Ddh5sMxH.cjs");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
9
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
10
10
|
let _fedify_vocab = require("@fedify/vocab");
|
|
@@ -18,6 +18,7 @@ let es_toolkit = require("es-toolkit");
|
|
|
18
18
|
let json_canon = require("json-canon");
|
|
19
19
|
json_canon = require_rolldown_runtime.__toESM(json_canon, 1);
|
|
20
20
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
21
|
+
let _fedify_vocab_runtime_internal = require("@fedify/vocab-runtime/internal");
|
|
21
22
|
let _fedify_webfinger = require("@fedify/webfinger");
|
|
22
23
|
let _opentelemetry_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
23
24
|
let devalue = require("devalue");
|
|
@@ -4319,9 +4320,10 @@ function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
|
|
|
4319
4320
|
*
|
|
4320
4321
|
* @param parameters The parameters for sending the activity.
|
|
4321
4322
|
* See also {@link SendActivityParameters}.
|
|
4323
|
+
* @param deps The dependencies, for tests.
|
|
4322
4324
|
* @throws {Error} If the activity fails to send.
|
|
4323
4325
|
*/
|
|
4324
|
-
function sendActivity(options) {
|
|
4326
|
+
function sendActivity(options, deps = {}) {
|
|
4325
4327
|
const tracerProvider = options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
|
|
4326
4328
|
return tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.send_activity", {
|
|
4327
4329
|
kind: _opentelemetry_api.SpanKind.CLIENT,
|
|
@@ -4337,7 +4339,7 @@ function sendActivity(options) {
|
|
|
4337
4339
|
}, () => sendActivityInternal({
|
|
4338
4340
|
...options,
|
|
4339
4341
|
tracerProvider
|
|
4340
|
-
}, span));
|
|
4342
|
+
}, span, deps));
|
|
4341
4343
|
} catch (e) {
|
|
4342
4344
|
span.setStatus({
|
|
4343
4345
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -4422,13 +4424,14 @@ async function readLimitedResponseBody(response, maxBytes) {
|
|
|
4422
4424
|
totalBytes += value.length;
|
|
4423
4425
|
}
|
|
4424
4426
|
} finally {
|
|
4427
|
+
if (truncated) reader.cancel().catch(() => {});
|
|
4425
4428
|
reader.releaseLock();
|
|
4426
4429
|
}
|
|
4427
4430
|
let result = chunks.join("");
|
|
4428
4431
|
if (truncated) result += "… (truncated)";
|
|
4429
4432
|
return result;
|
|
4430
4433
|
}
|
|
4431
|
-
async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span) {
|
|
4434
|
+
async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span, deps) {
|
|
4432
4435
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
4433
4436
|
"fedify",
|
|
4434
4437
|
"federation",
|
|
@@ -4437,9 +4440,11 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
|
|
|
4437
4440
|
const federationMetrics = require_http.getFederationMetrics(meterProvider);
|
|
4438
4441
|
const started = performance.now();
|
|
4439
4442
|
let deliverySuccess = false;
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
+
const openUnvalidatedHop = deps.openHop ?? (allowPrivateAddress ? _fedify_vocab_runtime_internal.openDirectFetch : (url) => (0, _fedify_vocab_runtime_internal.openPublicFetch)(url));
|
|
4444
|
+
async function openHop(url) {
|
|
4445
|
+
let session;
|
|
4446
|
+
try {
|
|
4447
|
+
session = await openUnvalidatedHop(url);
|
|
4443
4448
|
} catch (error) {
|
|
4444
4449
|
if (error instanceof _fedify_vocab_runtime.UrlError && error.reason === "dns") {
|
|
4445
4450
|
logger.error("DNS resolution failed for URL: {url}", {
|
|
@@ -4456,82 +4461,90 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
|
|
|
4456
4461
|
});
|
|
4457
4462
|
throw error;
|
|
4458
4463
|
}
|
|
4464
|
+
return session;
|
|
4459
4465
|
}
|
|
4466
|
+
let initialSession;
|
|
4460
4467
|
try {
|
|
4461
|
-
await
|
|
4468
|
+
initialSession = await openHop(inbox.href);
|
|
4462
4469
|
} catch (error) {
|
|
4463
4470
|
if (error instanceof _fedify_vocab_runtime.FetchError) federationMetrics.recordDelivery(inbox, require_http.getDurationMs(started), false, activityType);
|
|
4464
4471
|
throw error;
|
|
4465
4472
|
}
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
}
|
|
4474
|
-
let rsaKey = null;
|
|
4475
|
-
for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
|
|
4476
|
-
rsaKey = key;
|
|
4477
|
-
break;
|
|
4478
|
-
}
|
|
4479
|
-
if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
|
|
4480
|
-
activityId,
|
|
4481
|
-
inbox: inbox.href
|
|
4482
|
-
});
|
|
4483
|
-
else if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
|
|
4484
|
-
inbox: inbox.href,
|
|
4485
|
-
keys: keys.map((pair) => ({
|
|
4486
|
-
keyId: pair.keyId.href,
|
|
4487
|
-
privateKey: pair.privateKey
|
|
4488
|
-
}))
|
|
4489
|
-
});
|
|
4490
|
-
let response;
|
|
4473
|
+
let initialPending = true;
|
|
4474
|
+
const openDeliveryHop = (url) => {
|
|
4475
|
+
if (initialPending) {
|
|
4476
|
+
initialPending = false;
|
|
4477
|
+
return Promise.resolve(initialSession);
|
|
4478
|
+
}
|
|
4479
|
+
return openHop(url);
|
|
4480
|
+
};
|
|
4491
4481
|
try {
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4482
|
+
headers = new Headers(headers);
|
|
4483
|
+
headers.set("Content-Type", "application/activity+json");
|
|
4484
|
+
const request = new Request(inbox, {
|
|
4485
|
+
method: "POST",
|
|
4486
|
+
headers,
|
|
4487
|
+
body: JSON.stringify(activity),
|
|
4488
|
+
redirect: "manual"
|
|
4496
4489
|
});
|
|
4497
|
-
|
|
4498
|
-
const
|
|
4499
|
-
|
|
4490
|
+
let rsaKey = null;
|
|
4491
|
+
for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
|
|
4492
|
+
rsaKey = key;
|
|
4493
|
+
break;
|
|
4494
|
+
}
|
|
4495
|
+
if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
|
|
4500
4496
|
activityId,
|
|
4497
|
+
inbox: inbox.href
|
|
4498
|
+
});
|
|
4499
|
+
else if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
|
|
4501
4500
|
inbox: inbox.href,
|
|
4502
|
-
|
|
4501
|
+
keys: keys.map((pair) => ({
|
|
4502
|
+
keyId: pair.keyId.href,
|
|
4503
|
+
privateKey: pair.privateKey
|
|
4504
|
+
}))
|
|
4503
4505
|
});
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
}
|
|
4513
|
-
error = "";
|
|
4514
|
-
}
|
|
4515
|
-
logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
4506
|
+
let outcome;
|
|
4507
|
+
try {
|
|
4508
|
+
outcome = rsaKey == null ? await fetchWithValidatedRedirects(request, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox)) : await require_http.doubleKnockScoped(request, rsaKey, {
|
|
4509
|
+
tracerProvider,
|
|
4510
|
+
specDeterminer
|
|
4511
|
+
}, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox));
|
|
4512
|
+
} catch (error) {
|
|
4513
|
+
const failure = error instanceof _fedify_vocab_runtime.FetchError || error instanceof _fedify_vocab_runtime.UrlError ? error : createFetchError(inbox.href, error);
|
|
4514
|
+
logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
|
|
4516
4515
|
activityId,
|
|
4517
4516
|
inbox: inbox.href,
|
|
4518
|
-
|
|
4519
|
-
statusText: response.statusText,
|
|
4520
|
-
error
|
|
4517
|
+
error: failure
|
|
4521
4518
|
});
|
|
4522
|
-
|
|
4519
|
+
federationMetrics.recordDelivery(inbox, require_http.getDurationMs(started), false, activityType);
|
|
4520
|
+
throw failure;
|
|
4521
|
+
}
|
|
4522
|
+
try {
|
|
4523
|
+
if (!outcome.ok) {
|
|
4524
|
+
const { status, statusText, error, responseHeaders } = outcome;
|
|
4525
|
+
logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
4526
|
+
activityId,
|
|
4527
|
+
inbox: inbox.href,
|
|
4528
|
+
status,
|
|
4529
|
+
statusText,
|
|
4530
|
+
error
|
|
4531
|
+
});
|
|
4532
|
+
throw new SendActivityError(inbox, status, `Failed to send activity ${activityId} to ${inbox.href} (${status} ${statusText}):\n${error}`, error, responseHeaders);
|
|
4533
|
+
}
|
|
4534
|
+
deliverySuccess = true;
|
|
4535
|
+
const eventAttributes = {
|
|
4536
|
+
"activitypub.inbox.url": inbox.href,
|
|
4537
|
+
"activitypub.activity.id": activityId ?? ""
|
|
4538
|
+
};
|
|
4539
|
+
if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
|
|
4540
|
+
const actorId = getActivityActorId(activity);
|
|
4541
|
+
if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
|
|
4542
|
+
span.addEvent("activitypub.activity.sent", eventAttributes);
|
|
4543
|
+
} finally {
|
|
4544
|
+
federationMetrics.recordDelivery(inbox, require_http.getDurationMs(started), deliverySuccess, activityType);
|
|
4523
4545
|
}
|
|
4524
|
-
deliverySuccess = true;
|
|
4525
|
-
const eventAttributes = {
|
|
4526
|
-
"activitypub.inbox.url": inbox.href,
|
|
4527
|
-
"activitypub.activity.id": activityId ?? ""
|
|
4528
|
-
};
|
|
4529
|
-
if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
|
|
4530
|
-
const actorId = getActivityActorId(activity);
|
|
4531
|
-
if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
|
|
4532
|
-
span.addEvent("activitypub.activity.sent", eventAttributes);
|
|
4533
4546
|
} finally {
|
|
4534
|
-
|
|
4547
|
+
if (initialPending) await initialSession.close();
|
|
4535
4548
|
}
|
|
4536
4549
|
}
|
|
4537
4550
|
function createFetchError(url, cause) {
|
|
@@ -4539,10 +4552,75 @@ function createFetchError(url, cause) {
|
|
|
4539
4552
|
error.cause = cause;
|
|
4540
4553
|
return error;
|
|
4541
4554
|
}
|
|
4542
|
-
|
|
4555
|
+
/**
|
|
4556
|
+
* Starts cancelling the body of a response without waiting for it to settle.
|
|
4557
|
+
* The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
|
|
4558
|
+
* settles only after the other branch is cancelled too, per the Streams
|
|
4559
|
+
* standard, so awaiting it could block forever. Failures are only logged.
|
|
4560
|
+
*/
|
|
4561
|
+
function cancelResponseBody(response, url) {
|
|
4562
|
+
response.body?.cancel().catch((error) => {
|
|
4563
|
+
(0, _logtape_logtape.getLogger)([
|
|
4564
|
+
"fedify",
|
|
4565
|
+
"federation",
|
|
4566
|
+
"outbox"
|
|
4567
|
+
]).debug("Failed to cancel the response body from {url}: {error}", {
|
|
4568
|
+
url,
|
|
4569
|
+
error
|
|
4570
|
+
});
|
|
4571
|
+
});
|
|
4572
|
+
}
|
|
4573
|
+
/**
|
|
4574
|
+
* Reads what is needed from the final response of a delivery while its
|
|
4575
|
+
* session is open: the body of a successful response is cancelled, and at
|
|
4576
|
+
* most 1 KiB of an error response body is read.
|
|
4577
|
+
*/
|
|
4578
|
+
async function consumeSendActivityResponse(response, inbox) {
|
|
4579
|
+
if (response.ok) {
|
|
4580
|
+
cancelResponseBody(response, response.url || inbox.href);
|
|
4581
|
+
return { ok: true };
|
|
4582
|
+
}
|
|
4583
|
+
let error;
|
|
4584
|
+
try {
|
|
4585
|
+
error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
|
|
4586
|
+
} catch (_) {
|
|
4587
|
+
error = "";
|
|
4588
|
+
}
|
|
4589
|
+
return {
|
|
4590
|
+
ok: false,
|
|
4591
|
+
status: response.status,
|
|
4592
|
+
statusText: response.statusText,
|
|
4593
|
+
error,
|
|
4594
|
+
responseHeaders: new Headers(response.headers)
|
|
4595
|
+
};
|
|
4596
|
+
}
|
|
4597
|
+
/**
|
|
4598
|
+
* Sends a request without signing it, following redirects manually like the
|
|
4599
|
+
* Fetch standard does. Every hop is opened with `openHop()`, which validates
|
|
4600
|
+
* the destination, after the previous hop is closed. The final response is
|
|
4601
|
+
* passed to `consume()`, and its session is closed after that.
|
|
4602
|
+
* This is exported only for tests; it is not exported from any entry point.
|
|
4603
|
+
* @param request The request to send.
|
|
4604
|
+
* @param openHop The function to open a session for each hop.
|
|
4605
|
+
* @param consume The function to consume the final response.
|
|
4606
|
+
* @returns The value returned by `consume`.
|
|
4607
|
+
*/
|
|
4608
|
+
async function fetchWithValidatedRedirects(request, openHop, consume) {
|
|
4609
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
4610
|
+
"fedify",
|
|
4611
|
+
"federation",
|
|
4612
|
+
"outbox"
|
|
4613
|
+
]);
|
|
4543
4614
|
let body = await request.clone().text();
|
|
4544
4615
|
for (let redirects = 0;; redirects++) {
|
|
4545
|
-
const
|
|
4616
|
+
const session = await openHop(request.url);
|
|
4617
|
+
let response;
|
|
4618
|
+
try {
|
|
4619
|
+
response = await session.fetch(request, { redirect: "manual" });
|
|
4620
|
+
} catch (error) {
|
|
4621
|
+
await closeAfterError(session, logger);
|
|
4622
|
+
throw error;
|
|
4623
|
+
}
|
|
4546
4624
|
const location = response.headers.get("Location");
|
|
4547
4625
|
if (![
|
|
4548
4626
|
301,
|
|
@@ -4550,11 +4628,21 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
|
|
|
4550
4628
|
303,
|
|
4551
4629
|
307,
|
|
4552
4630
|
308
|
|
4553
|
-
].includes(response.status) || location == null)
|
|
4554
|
-
|
|
4631
|
+
].includes(response.status) || location == null) try {
|
|
4632
|
+
return await consume(response);
|
|
4633
|
+
} catch (error) {
|
|
4634
|
+
logger.debug("Failed to consume the response from {url}; closing the session: {error}", {
|
|
4635
|
+
url: request.url,
|
|
4636
|
+
error
|
|
4637
|
+
});
|
|
4638
|
+
throw error;
|
|
4639
|
+
} finally {
|
|
4640
|
+
await session.close();
|
|
4641
|
+
}
|
|
4642
|
+
cancelResponseBody(response, request.url);
|
|
4643
|
+
await session.close();
|
|
4555
4644
|
if (redirects >= 20) throw new TypeError("Too many redirects");
|
|
4556
4645
|
const url = new URL(location, request.url);
|
|
4557
|
-
await validateUrl(url.href);
|
|
4558
4646
|
const headers = new Headers(request.headers);
|
|
4559
4647
|
if (url.origin !== new URL(request.url).origin) {
|
|
4560
4648
|
headers.delete("Authorization");
|
|
@@ -4582,6 +4670,16 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
|
|
|
4582
4670
|
});
|
|
4583
4671
|
}
|
|
4584
4672
|
}
|
|
4673
|
+
async function closeAfterError(session, logger) {
|
|
4674
|
+
try {
|
|
4675
|
+
await session.close();
|
|
4676
|
+
} catch (error) {
|
|
4677
|
+
logger.debug("Failed to close the session for {url}: {error}", {
|
|
4678
|
+
url: session.url.href,
|
|
4679
|
+
error
|
|
4680
|
+
});
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4585
4683
|
/**
|
|
4586
4684
|
* An error that is thrown when an activity fails to send to a remote inbox.
|
|
4587
4685
|
* It contains structured information about the failure, including the HTTP
|
|
@@ -10902,7 +11000,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
10902
11000
|
onNotAcceptable ??= notAcceptable;
|
|
10903
11001
|
onUnauthorized ??= unauthorized;
|
|
10904
11002
|
const url = new URL(request.url);
|
|
10905
|
-
const route = this.router.route(url.pathname);
|
|
11003
|
+
const route = this.router.route(url.pathname, { allowUnencodedColon: true });
|
|
10906
11004
|
if (route == null) {
|
|
10907
11005
|
const media = this.hashlinkMediaDispatcher == null ? null : parseHashlinkGatewayRequest(url);
|
|
10908
11006
|
if (media != null) return await this.#fetchHashlinkMedia(request, media, {
|
|
@@ -12721,52 +12819,92 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
12721
12819
|
if (object instanceof _fedify_vocab.Tombstone && !(object instanceof cls) && options?.tombstone !== "passthrough") return null;
|
|
12722
12820
|
return object;
|
|
12723
12821
|
}
|
|
12724
|
-
#signedKey =
|
|
12822
|
+
#signedKey = null;
|
|
12823
|
+
#signedKeyOwner = null;
|
|
12824
|
+
#signatureChecks = /* @__PURE__ */ new WeakMap();
|
|
12825
|
+
#getSignatureCheck(documentLoader, contextLoader) {
|
|
12826
|
+
let checks = this.#signatureChecks.get(documentLoader);
|
|
12827
|
+
if (checks == null) {
|
|
12828
|
+
checks = /* @__PURE__ */ new WeakMap();
|
|
12829
|
+
this.#signatureChecks.set(documentLoader, checks);
|
|
12830
|
+
}
|
|
12831
|
+
let check = checks.get(contextLoader);
|
|
12832
|
+
if (check == null) {
|
|
12833
|
+
check = {};
|
|
12834
|
+
checks.set(contextLoader, check);
|
|
12835
|
+
}
|
|
12836
|
+
return check;
|
|
12837
|
+
}
|
|
12725
12838
|
async getSignedKey(options = {}) {
|
|
12726
12839
|
if (this.#signedKey != null) return this.#signedKey;
|
|
12727
|
-
const
|
|
12728
|
-
|
|
12729
|
-
|
|
12840
|
+
const documentLoader = options.documentLoader ?? this.documentLoader;
|
|
12841
|
+
const contextLoader = options.contextLoader ?? this.contextLoader;
|
|
12842
|
+
const check = this.#getSignatureCheck(documentLoader, contextLoader);
|
|
12843
|
+
if (check.key != null) return check.key;
|
|
12844
|
+
const keyCache = documentLoader === this.documentLoader && contextLoader === this.contextLoader ? new KvKeyCache(this.federation.kv, this.federation.kvPrefixes.publicKey, {
|
|
12845
|
+
documentLoader,
|
|
12846
|
+
contextLoader,
|
|
12730
12847
|
tracerProvider: options.tracerProvider ?? this.tracerProvider,
|
|
12731
12848
|
keyTtl: this.federation.publicKeyTtl
|
|
12732
|
-
});
|
|
12733
|
-
|
|
12849
|
+
}) : void 0;
|
|
12850
|
+
const promise = require_http.verifyRequest(this.request, {
|
|
12734
12851
|
...this,
|
|
12735
|
-
contextLoader
|
|
12736
|
-
documentLoader
|
|
12852
|
+
contextLoader,
|
|
12853
|
+
documentLoader,
|
|
12737
12854
|
timeWindow: this.federation.signatureTimeWindow,
|
|
12738
12855
|
maxSignatures: this.federation.maxHttpSignatures,
|
|
12856
|
+
keyCache,
|
|
12739
12857
|
meterProvider: this.meterProvider,
|
|
12740
|
-
tracerProvider: options.tracerProvider ?? this.tracerProvider
|
|
12741
|
-
|
|
12858
|
+
tracerProvider: options.tracerProvider ?? this.tracerProvider
|
|
12859
|
+
}).then((key) => {
|
|
12860
|
+
if (key != null) this.#signedKey ??= key;
|
|
12861
|
+
return key;
|
|
12862
|
+
}).catch((error) => {
|
|
12863
|
+
if (check.key === promise) check.key = void 0;
|
|
12864
|
+
throw error;
|
|
12742
12865
|
});
|
|
12866
|
+
check.key = promise;
|
|
12867
|
+
return await promise;
|
|
12743
12868
|
}
|
|
12744
|
-
#signedKeyOwner = void 0;
|
|
12745
12869
|
async getSignedKeyOwner(options = {}) {
|
|
12746
12870
|
if (this.#signedKeyOwner != null) return this.#signedKeyOwner;
|
|
12747
|
-
const
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
"fedify",
|
|
12759
|
-
"federation",
|
|
12760
|
-
"actor"
|
|
12761
|
-
]).warn("Failed to fetch the key owner {keyOwner} of {keyId} while verifying the request signature; treating the request as unauthenticated: {error}", {
|
|
12762
|
-
keyId: key.id?.href,
|
|
12763
|
-
keyOwner: error.url.href,
|
|
12764
|
-
error
|
|
12765
|
-
});
|
|
12766
|
-
return this.#signedKeyOwner = null;
|
|
12871
|
+
const normalizedOptions = {
|
|
12872
|
+
documentLoader: options.documentLoader ?? this.documentLoader,
|
|
12873
|
+
contextLoader: options.contextLoader ?? this.contextLoader,
|
|
12874
|
+
tracerProvider: options.tracerProvider ?? this.tracerProvider
|
|
12875
|
+
};
|
|
12876
|
+
const check = this.#getSignatureCheck(normalizedOptions.documentLoader, normalizedOptions.contextLoader);
|
|
12877
|
+
if (check.owner != null) return check.owner;
|
|
12878
|
+
const promise = this.getSignedKey(normalizedOptions).then(async (key) => {
|
|
12879
|
+
if (key == null) {
|
|
12880
|
+
if (check.owner === promise) check.owner = void 0;
|
|
12881
|
+
return null;
|
|
12767
12882
|
}
|
|
12883
|
+
try {
|
|
12884
|
+
const owner = await require_portable_collection.getKeyOwner(key, normalizedOptions);
|
|
12885
|
+
if (owner != null) this.#signedKeyOwner ??= owner;
|
|
12886
|
+
return owner;
|
|
12887
|
+
} catch (error) {
|
|
12888
|
+
if (error instanceof _fedify_vocab_runtime.FetchError) {
|
|
12889
|
+
(0, _logtape_logtape.getLogger)([
|
|
12890
|
+
"fedify",
|
|
12891
|
+
"federation",
|
|
12892
|
+
"actor"
|
|
12893
|
+
]).warn("Failed to fetch the key owner {keyOwner} of {keyId} while verifying the request signature; treating the request as unauthenticated: {error}", {
|
|
12894
|
+
keyId: key.id?.href,
|
|
12895
|
+
keyOwner: error.url.href,
|
|
12896
|
+
error
|
|
12897
|
+
});
|
|
12898
|
+
return null;
|
|
12899
|
+
}
|
|
12900
|
+
throw error;
|
|
12901
|
+
}
|
|
12902
|
+
}).catch((error) => {
|
|
12903
|
+
if (check.owner === promise) check.owner = void 0;
|
|
12768
12904
|
throw error;
|
|
12769
|
-
}
|
|
12905
|
+
});
|
|
12906
|
+
check.owner = promise;
|
|
12907
|
+
return await promise;
|
|
12770
12908
|
}
|
|
12771
12909
|
async isSignedByAudience(object, options = {}) {
|
|
12772
12910
|
return await isInAudience(object, null) || await isInAudience(object, await this.getSignedKeyOwner(options), options);
|