@fedify/fedify 2.4.5-dev.2462 → 2.4.5
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-BBUGd9wr.mjs → builder-CUAVOHg6.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-CariICma.mjs → compound-proof-BNjXclzO.mjs} +2 -2
- package/dist/{context-7NyiqIcg.d.ts → context-CVOOd9pD.d.ts} +28 -2
- package/dist/{context-tsYPJ2Ew.d.cts → context-D0gFIgev.d.cts} +28 -2
- package/dist/{deno-BO7-a3hz.mjs → deno-DcJISdwu.mjs} +1 -1
- package/dist/docloader-CP1UxMLL.mjs +136 -0
- 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 +4 -4
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +465 -32
- 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/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 +498 -88
- 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-Dd9IwOEi.cjs → federation-Bkfq2YpS.cjs} +240 -102
- package/dist/{http-DLf8QYD6.cjs → http-DHVH0DqC.cjs} +179 -110
- package/dist/{http-D7L2pqLT.mjs → http-DuGmr8J8.mjs} +204 -106
- package/dist/{http-BVrOR6MG.js → http-R0ty0Udi.js} +178 -109
- package/dist/{inbox-DtEKOr3W.mjs → inbox-Bl1Y0s_A.mjs} +2 -2
- package/dist/{key-DYxuyd-s.d.cts → key-CyCcut3h.d.ts} +1 -2
- package/dist/{key-C1nv7_jD.d.ts → key-VEL1QqHF.d.cts} +1 -2
- package/dist/{kv-cache-Dkzuqs8B.cjs → kv-cache-BtBSBZi9.cjs} +72 -27
- package/dist/{kv-cache-m3RIr811.mjs → kv-cache-DAV2_U44.mjs} +1 -1
- package/dist/{kv-cache-DoZoRS9p.js → kv-cache-FnLtmATH.js} +73 -28
- package/dist/{ld-PJLGA2MJ.mjs → ld-Cy5GqbvS.mjs} +2 -2
- package/dist/{metrics-CGR9cI-Y.mjs → metrics-CW8LoN7n.mjs} +1 -1
- package/dist/{middleware-DPI-Jp9w.mjs → middleware-CdP-lz8Q.mjs} +1 -1
- package/dist/{middleware-ByanhTvb.mjs → middleware-D2fk45uN.mjs} +82 -42
- package/dist/{middleware-BxhBd7pw.js → middleware-D4rQARvq.js} +241 -103
- package/dist/{mod-DEZXwV6J.d.ts → mod-B1Qt5cQQ.d.ts} +2 -9
- package/dist/{mod-Xj5P4s4d.d.ts → mod-B5COx5Wd.d.ts} +1 -1
- package/dist/{mod-CavhAgLw.d.cts → mod-BTxJG7ZH.d.cts} +1 -1
- package/dist/{mod-CtpF2BU5.d.ts → mod-CpyjFA9s.d.ts} +1 -1
- package/dist/{mod-BojP2Bw3.d.cts → mod-makPMVMg.d.cts} +1 -1
- package/dist/{mod-Dw-nPxQh.d.cts → mod-ss0wBUH1.d.cts} +2 -9
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +6 -6
- package/dist/mod.d.ts +6 -6
- 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 +292 -30
- 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-B5c276hH.mjs → owner-CbQZqFim.mjs} +3 -3
- package/dist/{portable-collection-BMZY9qT-.js → portable-collection-B5Aryi8I.js} +1 -1
- package/dist/{portable-collection-CR5uwjws.cjs → portable-collection-C_Kmvl3q.cjs} +1 -1
- package/dist/{portable-collection-C88tBiy0.mjs → portable-collection-D2JYDadi.mjs} +2 -2
- package/dist/{portable-key-DxTXUpCK.mjs → portable-key-Cc-ctNU2.mjs} +1 -1
- package/dist/{proof-DNu-qP0i.mjs → proof-UN4CW55z.mjs} +3 -3
- package/dist/{send-CdCifQs4.mjs → send-BzbX2e6v.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 +2 -2
- package/dist/sig/mod.d.ts +2 -2
- 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-BuMu2bgP.mjs → temporal-B7Wvxrjs.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 +7 -7
- package/dist/docloader-DQNRDYDb.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-t8xDeJZ5.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
7
|
+
import { o as createFederation } from "../../middleware-D2fk45uN.mjs";
|
|
8
8
|
import { n as ParallelMessageQueue } from "../../mq-Dm1Wmtm-.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-t8xDeJZ5.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
8
|
-
import { r as createFederationBuilder } from "../../builder-
|
|
7
|
+
import { o as createFederation } from "../../middleware-D2fk45uN.mjs";
|
|
8
|
+
import { r as createFederationBuilder } from "../../builder-CUAVOHg6.mjs";
|
|
9
9
|
import { t as TaskCodec } from "../../codec-C6rTrpIp.mjs";
|
|
10
10
|
import { t as InProcessMessageQueue } from "../../mq-Dm1Wmtm-.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-B7Wvxrjs.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-D2fk45uN.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-DHVH0DqC.cjs");
|
|
5
|
+
const require_types = require("./types-hKCCjii0.cjs");
|
|
6
|
+
const require_portable_collection = require("./portable-collection-C_Kmvl3q.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-BtBSBZi9.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");
|
|
@@ -16,6 +16,7 @@ let _fedify_vocab_runtime_jsonld = require("@fedify/vocab-runtime/jsonld");
|
|
|
16
16
|
_fedify_vocab_runtime_jsonld = require_rolldown_runtime.__toESM(_fedify_vocab_runtime_jsonld, 1);
|
|
17
17
|
let es_toolkit = require("es-toolkit");
|
|
18
18
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
19
|
+
let _fedify_vocab_runtime_internal = require("@fedify/vocab-runtime/internal");
|
|
19
20
|
let _fedify_webfinger = require("@fedify/webfinger");
|
|
20
21
|
let _opentelemetry_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
21
22
|
let devalue = require("devalue");
|
|
@@ -3620,9 +3621,10 @@ function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
|
|
|
3620
3621
|
*
|
|
3621
3622
|
* @param parameters The parameters for sending the activity.
|
|
3622
3623
|
* See also {@link SendActivityParameters}.
|
|
3624
|
+
* @param deps The dependencies, for tests.
|
|
3623
3625
|
* @throws {Error} If the activity fails to send.
|
|
3624
3626
|
*/
|
|
3625
|
-
function sendActivity(options) {
|
|
3627
|
+
function sendActivity(options, deps = {}) {
|
|
3626
3628
|
const tracerProvider = options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
|
|
3627
3629
|
return tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.send_activity", {
|
|
3628
3630
|
kind: _opentelemetry_api.SpanKind.CLIENT,
|
|
@@ -3634,7 +3636,7 @@ function sendActivity(options) {
|
|
|
3634
3636
|
await sendActivityInternal({
|
|
3635
3637
|
...options,
|
|
3636
3638
|
tracerProvider
|
|
3637
|
-
}, span);
|
|
3639
|
+
}, span, deps);
|
|
3638
3640
|
} catch (e) {
|
|
3639
3641
|
span.setStatus({
|
|
3640
3642
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -3719,13 +3721,14 @@ async function readLimitedResponseBody(response, maxBytes) {
|
|
|
3719
3721
|
totalBytes += value.length;
|
|
3720
3722
|
}
|
|
3721
3723
|
} finally {
|
|
3724
|
+
if (truncated) reader.cancel().catch(() => {});
|
|
3722
3725
|
reader.releaseLock();
|
|
3723
3726
|
}
|
|
3724
3727
|
let result = chunks.join("");
|
|
3725
3728
|
if (truncated) result += "… (truncated)";
|
|
3726
3729
|
return result;
|
|
3727
3730
|
}
|
|
3728
|
-
async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span) {
|
|
3731
|
+
async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span, deps) {
|
|
3729
3732
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
3730
3733
|
"fedify",
|
|
3731
3734
|
"federation",
|
|
@@ -3734,9 +3737,11 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
|
|
|
3734
3737
|
const federationMetrics = require_http.getFederationMetrics(meterProvider);
|
|
3735
3738
|
const started = performance.now();
|
|
3736
3739
|
let deliverySuccess = false;
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
+
const openUnvalidatedHop = deps.openHop ?? (allowPrivateAddress ? _fedify_vocab_runtime_internal.openDirectFetch : (url) => (0, _fedify_vocab_runtime_internal.openPublicFetch)(url));
|
|
3741
|
+
async function openHop(url) {
|
|
3742
|
+
let session;
|
|
3743
|
+
try {
|
|
3744
|
+
session = await openUnvalidatedHop(url);
|
|
3740
3745
|
} catch (error) {
|
|
3741
3746
|
if (error instanceof _fedify_vocab_runtime.UrlError && error.reason === "dns") {
|
|
3742
3747
|
logger.error("DNS resolution failed for URL: {url}", {
|
|
@@ -3753,82 +3758,90 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
|
|
|
3753
3758
|
});
|
|
3754
3759
|
throw error;
|
|
3755
3760
|
}
|
|
3761
|
+
return session;
|
|
3756
3762
|
}
|
|
3763
|
+
let initialSession;
|
|
3757
3764
|
try {
|
|
3758
|
-
await
|
|
3765
|
+
initialSession = await openHop(inbox.href);
|
|
3759
3766
|
} catch (error) {
|
|
3760
3767
|
if (error instanceof _fedify_vocab_runtime.FetchError) federationMetrics.recordDelivery(inbox, require_http.getDurationMs(started), false, activityType);
|
|
3761
3768
|
throw error;
|
|
3762
3769
|
}
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
}
|
|
3771
|
-
let rsaKey = null;
|
|
3772
|
-
for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
|
|
3773
|
-
rsaKey = key;
|
|
3774
|
-
break;
|
|
3775
|
-
}
|
|
3776
|
-
if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
|
|
3777
|
-
activityId,
|
|
3778
|
-
inbox: inbox.href
|
|
3779
|
-
});
|
|
3780
|
-
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.", {
|
|
3781
|
-
inbox: inbox.href,
|
|
3782
|
-
keys: keys.map((pair) => ({
|
|
3783
|
-
keyId: pair.keyId.href,
|
|
3784
|
-
privateKey: pair.privateKey
|
|
3785
|
-
}))
|
|
3786
|
-
});
|
|
3787
|
-
let response;
|
|
3770
|
+
let initialPending = true;
|
|
3771
|
+
const openDeliveryHop = (url) => {
|
|
3772
|
+
if (initialPending) {
|
|
3773
|
+
initialPending = false;
|
|
3774
|
+
return Promise.resolve(initialSession);
|
|
3775
|
+
}
|
|
3776
|
+
return openHop(url);
|
|
3777
|
+
};
|
|
3788
3778
|
try {
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3779
|
+
headers = new Headers(headers);
|
|
3780
|
+
headers.set("Content-Type", "application/activity+json");
|
|
3781
|
+
const request = new Request(inbox, {
|
|
3782
|
+
method: "POST",
|
|
3783
|
+
headers,
|
|
3784
|
+
body: JSON.stringify(activity),
|
|
3785
|
+
redirect: "manual"
|
|
3793
3786
|
});
|
|
3794
|
-
|
|
3795
|
-
const
|
|
3796
|
-
|
|
3787
|
+
let rsaKey = null;
|
|
3788
|
+
for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
|
|
3789
|
+
rsaKey = key;
|
|
3790
|
+
break;
|
|
3791
|
+
}
|
|
3792
|
+
if (rsaKey == null && keys.length < 1) logger.debug("Sending the activity {activityId} to {inbox} without a signature.", {
|
|
3797
3793
|
activityId,
|
|
3794
|
+
inbox: inbox.href
|
|
3795
|
+
});
|
|
3796
|
+
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.", {
|
|
3798
3797
|
inbox: inbox.href,
|
|
3799
|
-
|
|
3798
|
+
keys: keys.map((pair) => ({
|
|
3799
|
+
keyId: pair.keyId.href,
|
|
3800
|
+
privateKey: pair.privateKey
|
|
3801
|
+
}))
|
|
3800
3802
|
});
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
}
|
|
3810
|
-
error = "";
|
|
3811
|
-
}
|
|
3812
|
-
logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
3803
|
+
let outcome;
|
|
3804
|
+
try {
|
|
3805
|
+
outcome = rsaKey == null ? await fetchWithValidatedRedirects(request, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox)) : await require_http.doubleKnockScoped(request, rsaKey, {
|
|
3806
|
+
tracerProvider,
|
|
3807
|
+
specDeterminer
|
|
3808
|
+
}, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox));
|
|
3809
|
+
} catch (error) {
|
|
3810
|
+
const failure = error instanceof _fedify_vocab_runtime.FetchError || error instanceof _fedify_vocab_runtime.UrlError ? error : createFetchError(inbox.href, error);
|
|
3811
|
+
logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
|
|
3813
3812
|
activityId,
|
|
3814
3813
|
inbox: inbox.href,
|
|
3815
|
-
|
|
3816
|
-
statusText: response.statusText,
|
|
3817
|
-
error
|
|
3814
|
+
error: failure
|
|
3818
3815
|
});
|
|
3819
|
-
|
|
3816
|
+
federationMetrics.recordDelivery(inbox, require_http.getDurationMs(started), false, activityType);
|
|
3817
|
+
throw failure;
|
|
3818
|
+
}
|
|
3819
|
+
try {
|
|
3820
|
+
if (!outcome.ok) {
|
|
3821
|
+
const { status, statusText, error, responseHeaders } = outcome;
|
|
3822
|
+
logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
3823
|
+
activityId,
|
|
3824
|
+
inbox: inbox.href,
|
|
3825
|
+
status,
|
|
3826
|
+
statusText,
|
|
3827
|
+
error
|
|
3828
|
+
});
|
|
3829
|
+
throw new SendActivityError(inbox, status, `Failed to send activity ${activityId} to ${inbox.href} (${status} ${statusText}):\n${error}`, error, responseHeaders);
|
|
3830
|
+
}
|
|
3831
|
+
deliverySuccess = true;
|
|
3832
|
+
const eventAttributes = {
|
|
3833
|
+
"activitypub.inbox.url": inbox.href,
|
|
3834
|
+
"activitypub.activity.id": activityId ?? ""
|
|
3835
|
+
};
|
|
3836
|
+
if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
|
|
3837
|
+
const actorId = getActivityActorId(activity);
|
|
3838
|
+
if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
|
|
3839
|
+
span.addEvent("activitypub.activity.sent", eventAttributes);
|
|
3840
|
+
} finally {
|
|
3841
|
+
federationMetrics.recordDelivery(inbox, require_http.getDurationMs(started), deliverySuccess, activityType);
|
|
3820
3842
|
}
|
|
3821
|
-
deliverySuccess = true;
|
|
3822
|
-
const eventAttributes = {
|
|
3823
|
-
"activitypub.inbox.url": inbox.href,
|
|
3824
|
-
"activitypub.activity.id": activityId ?? ""
|
|
3825
|
-
};
|
|
3826
|
-
if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
|
|
3827
|
-
const actorId = getActivityActorId(activity);
|
|
3828
|
-
if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
|
|
3829
|
-
span.addEvent("activitypub.activity.sent", eventAttributes);
|
|
3830
3843
|
} finally {
|
|
3831
|
-
|
|
3844
|
+
if (initialPending) await initialSession.close();
|
|
3832
3845
|
}
|
|
3833
3846
|
}
|
|
3834
3847
|
function createFetchError(url, cause) {
|
|
@@ -3836,10 +3849,75 @@ function createFetchError(url, cause) {
|
|
|
3836
3849
|
error.cause = cause;
|
|
3837
3850
|
return error;
|
|
3838
3851
|
}
|
|
3839
|
-
|
|
3852
|
+
/**
|
|
3853
|
+
* Starts cancelling the body of a response without waiting for it to settle.
|
|
3854
|
+
* The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
|
|
3855
|
+
* settles only after the other branch is cancelled too, per the Streams
|
|
3856
|
+
* standard, so awaiting it could block forever. Failures are only logged.
|
|
3857
|
+
*/
|
|
3858
|
+
function cancelResponseBody(response, url) {
|
|
3859
|
+
response.body?.cancel().catch((error) => {
|
|
3860
|
+
(0, _logtape_logtape.getLogger)([
|
|
3861
|
+
"fedify",
|
|
3862
|
+
"federation",
|
|
3863
|
+
"outbox"
|
|
3864
|
+
]).debug("Failed to cancel the response body from {url}: {error}", {
|
|
3865
|
+
url,
|
|
3866
|
+
error
|
|
3867
|
+
});
|
|
3868
|
+
});
|
|
3869
|
+
}
|
|
3870
|
+
/**
|
|
3871
|
+
* Reads what is needed from the final response of a delivery while its
|
|
3872
|
+
* session is open: the body of a successful response is cancelled, and at
|
|
3873
|
+
* most 1 KiB of an error response body is read.
|
|
3874
|
+
*/
|
|
3875
|
+
async function consumeSendActivityResponse(response, inbox) {
|
|
3876
|
+
if (response.ok) {
|
|
3877
|
+
cancelResponseBody(response, response.url || inbox.href);
|
|
3878
|
+
return { ok: true };
|
|
3879
|
+
}
|
|
3880
|
+
let error;
|
|
3881
|
+
try {
|
|
3882
|
+
error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
|
|
3883
|
+
} catch (_) {
|
|
3884
|
+
error = "";
|
|
3885
|
+
}
|
|
3886
|
+
return {
|
|
3887
|
+
ok: false,
|
|
3888
|
+
status: response.status,
|
|
3889
|
+
statusText: response.statusText,
|
|
3890
|
+
error,
|
|
3891
|
+
responseHeaders: new Headers(response.headers)
|
|
3892
|
+
};
|
|
3893
|
+
}
|
|
3894
|
+
/**
|
|
3895
|
+
* Sends a request without signing it, following redirects manually like the
|
|
3896
|
+
* Fetch standard does. Every hop is opened with `openHop()`, which validates
|
|
3897
|
+
* the destination, after the previous hop is closed. The final response is
|
|
3898
|
+
* passed to `consume()`, and its session is closed after that.
|
|
3899
|
+
* This is exported only for tests; it is not exported from any entry point.
|
|
3900
|
+
* @param request The request to send.
|
|
3901
|
+
* @param openHop The function to open a session for each hop.
|
|
3902
|
+
* @param consume The function to consume the final response.
|
|
3903
|
+
* @returns The value returned by `consume`.
|
|
3904
|
+
*/
|
|
3905
|
+
async function fetchWithValidatedRedirects(request, openHop, consume) {
|
|
3906
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
3907
|
+
"fedify",
|
|
3908
|
+
"federation",
|
|
3909
|
+
"outbox"
|
|
3910
|
+
]);
|
|
3840
3911
|
let body = await request.clone().text();
|
|
3841
3912
|
for (let redirects = 0;; redirects++) {
|
|
3842
|
-
const
|
|
3913
|
+
const session = await openHop(request.url);
|
|
3914
|
+
let response;
|
|
3915
|
+
try {
|
|
3916
|
+
response = await session.fetch(request, { redirect: "manual" });
|
|
3917
|
+
} catch (error) {
|
|
3918
|
+
await closeAfterError(session, logger);
|
|
3919
|
+
throw error;
|
|
3920
|
+
}
|
|
3843
3921
|
const location = response.headers.get("Location");
|
|
3844
3922
|
if (![
|
|
3845
3923
|
301,
|
|
@@ -3847,11 +3925,21 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
|
|
|
3847
3925
|
303,
|
|
3848
3926
|
307,
|
|
3849
3927
|
308
|
|
3850
|
-
].includes(response.status) || location == null)
|
|
3851
|
-
|
|
3928
|
+
].includes(response.status) || location == null) try {
|
|
3929
|
+
return await consume(response);
|
|
3930
|
+
} catch (error) {
|
|
3931
|
+
logger.debug("Failed to consume the response from {url}; closing the session: {error}", {
|
|
3932
|
+
url: request.url,
|
|
3933
|
+
error
|
|
3934
|
+
});
|
|
3935
|
+
throw error;
|
|
3936
|
+
} finally {
|
|
3937
|
+
await session.close();
|
|
3938
|
+
}
|
|
3939
|
+
cancelResponseBody(response, request.url);
|
|
3940
|
+
await session.close();
|
|
3852
3941
|
if (redirects >= 20) throw new TypeError("Too many redirects");
|
|
3853
3942
|
const url = new URL(location, request.url);
|
|
3854
|
-
await validateUrl(url.href);
|
|
3855
3943
|
const headers = new Headers(request.headers);
|
|
3856
3944
|
if (url.origin !== new URL(request.url).origin) {
|
|
3857
3945
|
headers.delete("Authorization");
|
|
@@ -3879,6 +3967,16 @@ async function fetchWithValidatedRedirects(request, validateUrl) {
|
|
|
3879
3967
|
});
|
|
3880
3968
|
}
|
|
3881
3969
|
}
|
|
3970
|
+
async function closeAfterError(session, logger) {
|
|
3971
|
+
try {
|
|
3972
|
+
await session.close();
|
|
3973
|
+
} catch (error) {
|
|
3974
|
+
logger.debug("Failed to close the session for {url}: {error}", {
|
|
3975
|
+
url: session.url.href,
|
|
3976
|
+
error
|
|
3977
|
+
});
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3882
3980
|
/**
|
|
3883
3981
|
* An error that is thrown when an activity fails to send to a remote inbox.
|
|
3884
3982
|
* It contains structured information about the failure, including the HTTP
|
|
@@ -9920,7 +10018,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9920
10018
|
onNotAcceptable ??= notAcceptable;
|
|
9921
10019
|
onUnauthorized ??= unauthorized;
|
|
9922
10020
|
const url = new URL(request.url);
|
|
9923
|
-
const route = this.router.route(url.pathname);
|
|
10021
|
+
const route = this.router.route(url.pathname, { allowUnencodedColon: true });
|
|
9924
10022
|
if (route == null) {
|
|
9925
10023
|
const media = this.hashlinkMediaDispatcher == null ? null : parseHashlinkGatewayRequest(url);
|
|
9926
10024
|
if (media != null) return await this.#fetchHashlinkMedia(request, media, {
|
|
@@ -11653,45 +11751,85 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
11653
11751
|
if (object instanceof _fedify_vocab.Tombstone && !(object instanceof cls) && options?.tombstone !== "passthrough") return null;
|
|
11654
11752
|
return object;
|
|
11655
11753
|
}
|
|
11656
|
-
#signedKey =
|
|
11754
|
+
#signedKey = null;
|
|
11755
|
+
#signedKeyOwner = null;
|
|
11756
|
+
#signatureChecks = /* @__PURE__ */ new WeakMap();
|
|
11757
|
+
#getSignatureCheck(documentLoader, contextLoader) {
|
|
11758
|
+
let checks = this.#signatureChecks.get(documentLoader);
|
|
11759
|
+
if (checks == null) {
|
|
11760
|
+
checks = /* @__PURE__ */ new WeakMap();
|
|
11761
|
+
this.#signatureChecks.set(documentLoader, checks);
|
|
11762
|
+
}
|
|
11763
|
+
let check = checks.get(contextLoader);
|
|
11764
|
+
if (check == null) {
|
|
11765
|
+
check = {};
|
|
11766
|
+
checks.set(contextLoader, check);
|
|
11767
|
+
}
|
|
11768
|
+
return check;
|
|
11769
|
+
}
|
|
11657
11770
|
async getSignedKey(options = {}) {
|
|
11658
11771
|
if (this.#signedKey != null) return this.#signedKey;
|
|
11659
|
-
|
|
11772
|
+
const documentLoader = options.documentLoader ?? this.documentLoader;
|
|
11773
|
+
const contextLoader = options.contextLoader ?? this.contextLoader;
|
|
11774
|
+
const check = this.#getSignatureCheck(documentLoader, contextLoader);
|
|
11775
|
+
if (check.key != null) return check.key;
|
|
11776
|
+
const promise = require_http.verifyRequest(this.request, {
|
|
11660
11777
|
...this,
|
|
11661
|
-
contextLoader
|
|
11662
|
-
documentLoader
|
|
11778
|
+
contextLoader,
|
|
11779
|
+
documentLoader,
|
|
11663
11780
|
timeWindow: this.federation.signatureTimeWindow,
|
|
11664
11781
|
maxSignatures: this.federation.maxHttpSignatures,
|
|
11665
11782
|
meterProvider: this.meterProvider,
|
|
11666
11783
|
tracerProvider: options.tracerProvider ?? this.tracerProvider
|
|
11784
|
+
}).then((key) => {
|
|
11785
|
+
if (key != null) this.#signedKey ??= key;
|
|
11786
|
+
return key;
|
|
11787
|
+
}).catch((error) => {
|
|
11788
|
+
if (check.key === promise) check.key = void 0;
|
|
11789
|
+
throw error;
|
|
11667
11790
|
});
|
|
11791
|
+
check.key = promise;
|
|
11792
|
+
return await promise;
|
|
11668
11793
|
}
|
|
11669
|
-
#signedKeyOwner = void 0;
|
|
11670
11794
|
async getSignedKeyOwner(options = {}) {
|
|
11671
11795
|
if (this.#signedKeyOwner != null) return this.#signedKeyOwner;
|
|
11672
|
-
const
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11796
|
+
const normalizedOptions = {
|
|
11797
|
+
documentLoader: options.documentLoader ?? this.documentLoader,
|
|
11798
|
+
contextLoader: options.contextLoader ?? this.contextLoader,
|
|
11799
|
+
tracerProvider: options.tracerProvider ?? this.tracerProvider
|
|
11800
|
+
};
|
|
11801
|
+
const check = this.#getSignatureCheck(normalizedOptions.documentLoader, normalizedOptions.contextLoader);
|
|
11802
|
+
if (check.owner != null) return check.owner;
|
|
11803
|
+
const promise = this.getSignedKey(normalizedOptions).then(async (key) => {
|
|
11804
|
+
if (key == null) {
|
|
11805
|
+
if (check.owner === promise) check.owner = void 0;
|
|
11806
|
+
return null;
|
|
11807
|
+
}
|
|
11808
|
+
try {
|
|
11809
|
+
const owner = await require_portable_collection.getKeyOwner(key, normalizedOptions);
|
|
11810
|
+
if (owner != null) this.#signedKeyOwner ??= owner;
|
|
11811
|
+
return owner;
|
|
11812
|
+
} catch (error) {
|
|
11813
|
+
if (error instanceof _fedify_vocab_runtime.FetchError) {
|
|
11814
|
+
(0, _logtape_logtape.getLogger)([
|
|
11815
|
+
"fedify",
|
|
11816
|
+
"federation",
|
|
11817
|
+
"actor"
|
|
11818
|
+
]).warn("Failed to fetch the key owner {keyOwner} of {keyId} while verifying the request signature; treating the request as unauthenticated: {error}", {
|
|
11819
|
+
keyId: key.id?.href,
|
|
11820
|
+
keyOwner: error.url.href,
|
|
11821
|
+
error
|
|
11822
|
+
});
|
|
11823
|
+
return null;
|
|
11824
|
+
}
|
|
11825
|
+
throw error;
|
|
11692
11826
|
}
|
|
11827
|
+
}).catch((error) => {
|
|
11828
|
+
if (check.owner === promise) check.owner = void 0;
|
|
11693
11829
|
throw error;
|
|
11694
|
-
}
|
|
11830
|
+
});
|
|
11831
|
+
check.owner = promise;
|
|
11832
|
+
return await promise;
|
|
11695
11833
|
}
|
|
11696
11834
|
async isSignedByAudience(object, options = {}) {
|
|
11697
11835
|
return await isInAudience(object, null) || await isInAudience(object, await this.getSignedKeyOwner(options), options);
|