@fedify/fedify 2.0.35-dev.2480 → 2.0.35-dev.2485
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/dist/{builder-cEjyBwpD.mjs → builder-CKbQrOH1.mjs} +3 -3
- package/dist/{client-CAM_bQXx.d.cts → client-BIBzqEIp.d.cts} +0 -1
- package/dist/{client-BUqbea1d.mjs → client-DGNacEx_.mjs} +113 -64
- package/dist/{client-CSddvgWN.d.ts → client-jE7oGvRh.d.ts} +0 -1
- 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/{context-r8sjhltN.d.cts → context-CnoWxuoB.d.cts} +2 -2
- package/dist/{context-B2iX3heK.d.ts → context-lxjaUTAI.d.ts} +2 -2
- package/dist/{deno-BcnGeGr2.mjs → deno-DL_ifPJk.mjs} +1 -1
- package/dist/docloader-B0_0Fjqo.mjs +124 -0
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +6 -6
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +42 -23
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +445 -70
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{http-Bs6thuCC.d.cts → http-Cd8UD1ym.d.ts} +1 -2
- package/dist/{http-BD9izRp9.cjs → http-CjkqnlR2.cjs} +114 -56
- package/dist/{http-CDupyfOc.d.ts → http-DFqAb11b.d.cts} +1 -2
- package/dist/{http-IYRDhhnT.js → http-DVEBwq6e.js} +113 -55
- package/dist/{http-D0cjeL8a.mjs → http-Dvsq4uCu.mjs} +136 -49
- package/dist/{inbox-D2fsxjqC.mjs → inbox-D6YbZFrZ.mjs} +1 -1
- package/dist/{key-BFzQ3Hef.mjs → key-CgcWGw6W.mjs} +1 -1
- package/dist/{kv-cache-C-RyNMt6.js → kv-cache-DC_F2WSH.js} +60 -18
- package/dist/{kv-cache-B4sDuLjb.cjs → kv-cache-nN9RaiYp.cjs} +59 -17
- package/dist/{ld-D2bkYBIz.mjs → ld-CfOIwHup.mjs} +2 -2
- package/dist/{middleware-DRZp7Odv.js → middleware-B0eqDUmD.js} +118 -33
- package/dist/{middleware-CXIBpN4y.mjs → middleware-CA7egMDR.mjs} +12 -12
- package/dist/{middleware-B2UC8mtN.cjs → middleware-Cu_yKroA.cjs} +1 -1
- package/dist/{middleware-3mc-kq3v.mjs → middleware-DEwj7jGj.mjs} +1 -1
- package/dist/{middleware-DxX8ZL-q.cjs → middleware-DZvWX5af.cjs} +118 -33
- package/dist/{mod-BkPB4hPe.d.cts → mod-BPDfu4Ey.d.cts} +2 -7
- package/dist/{mod-E7YM72JZ.d.ts → mod-BS-jg3Wx.d.ts} +1 -1
- package/dist/{mod-BKJyD7nk.d.ts → mod-DGP89q0u.d.ts} +2 -7
- package/dist/{mod-IOWUF5Yr.d.cts → mod-DVPS4yam.d.cts} +1 -1
- package/dist/{mod-CM_eR8hQ.d.ts → mod-I-34OMN3.d.ts} +1 -1
- package/dist/{mod-DDTLbfce.d.cts → mod-w1oyHseQ.d.cts} +1 -1
- 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/{owner-BuJXJDhl.mjs → owner-BvVwCoj2.mjs} +2 -2
- package/dist/{proof-DfmHAmtr.cjs → proof-BssSZpMi.cjs} +1 -1
- package/dist/{proof-JxVhLZA3.mjs → proof-C9TBSmUR.mjs} +2 -2
- package/dist/{proof-BJ_6nKhb.js → proof-Cp62GFJ3.js} +1 -1
- package/dist/{send-CDaWmeqV.mjs → send-ZWmjk2Zf.mjs} +117 -32
- package/dist/sig/http.test.mjs +411 -2
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +2 -2
- 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/proof.test.mjs +1 -1
- package/dist/{temporal-Bv6Zn1C5.mjs → temporal-BNtFi_ut.mjs} +1 -1
- package/dist/testing/mod.d.mts +0 -1
- package/dist/{types-miAYfiGO.js → types-B1_pRFE_.js} +112 -63
- package/dist/{types-DRPjmHVV.cjs → types-DOvyymM4.cjs} +111 -62
- package/dist/utils/docloader.test.mjs +316 -32
- 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 +5 -5
- package/dist/docloader-BqDq3fDF.mjs +0 -82
|
@@ -11,7 +11,7 @@ let byte_encodings_base64 = require("byte-encodings/base64");
|
|
|
11
11
|
let structured_field_values = require("structured-field-values");
|
|
12
12
|
//#region deno.json
|
|
13
13
|
var name = "@fedify/fedify";
|
|
14
|
-
var version = "2.0.35-dev.
|
|
14
|
+
var version = "2.0.35-dev.2485+75206550";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/sig/key.ts
|
|
17
17
|
/** Classifies decoding failures without hiding unexpected loader errors. */
|
|
@@ -1191,10 +1191,10 @@ async function createRedirectRequest(request, location, body, validateRedirect)
|
|
|
1191
1191
|
cache: request.cache
|
|
1192
1192
|
});
|
|
1193
1193
|
}
|
|
1194
|
-
async function fetchDoubleKnockRequest(request, signedRequest, signal) {
|
|
1194
|
+
async function fetchDoubleKnockRequest(session, request, signedRequest, signal) {
|
|
1195
1195
|
const maxAttempts = request.method === "GET" || request.method === "HEAD" ? 2 : 1;
|
|
1196
1196
|
for (let attempt = 1;; attempt++) try {
|
|
1197
|
-
return await fetch(signedRequest, {
|
|
1197
|
+
return await session.fetch(signedRequest, {
|
|
1198
1198
|
redirect: "manual",
|
|
1199
1199
|
signal
|
|
1200
1200
|
});
|
|
@@ -1245,69 +1245,127 @@ function getAbortReason(signal) {
|
|
|
1245
1245
|
return signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
1246
1246
|
}
|
|
1247
1247
|
/**
|
|
1248
|
-
* Performs a double-knock request
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1248
|
+
* Performs a double-knock request like {@link doubleKnock}, opening a session
|
|
1249
|
+
* for every hop with the given function, and passes the final response to
|
|
1250
|
+
* the given function. The final session is closed after `consume` settles,
|
|
1251
|
+
* so `consume` must read or cancel the response body before it returns.
|
|
1252
|
+
* This is not exported from any entry point.
|
|
1251
1253
|
* @param request The request to send.
|
|
1252
1254
|
* @param identity The identity to use for signing the request.
|
|
1253
1255
|
* @param options The options for double-knock requests.
|
|
1254
|
-
* @
|
|
1255
|
-
*
|
|
1256
|
+
* @param openHop The function to open a session for each hop. The session of
|
|
1257
|
+
* a hop is closed before the next hop is opened.
|
|
1258
|
+
* @param consume The function to consume the final response.
|
|
1259
|
+
* @returns The value returned by `consume`.
|
|
1256
1260
|
*/
|
|
1257
|
-
async function
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
visited.add(request.url);
|
|
1263
|
-
const origin = new URL(request.url).origin;
|
|
1264
|
-
const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
|
|
1265
|
-
const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
|
|
1266
|
-
let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1267
|
-
spec: firstTrySpec,
|
|
1268
|
-
tracerProvider,
|
|
1269
|
-
body
|
|
1270
|
-
});
|
|
1271
|
-
log?.(signedRequest);
|
|
1272
|
-
let response = await fetchDoubleKnockRequest(request, signedRequest, signal);
|
|
1273
|
-
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
1274
|
-
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new _fedify_vocab_runtime.FetchError(request.url, `Too many redirections (${redirected + 1})`);
|
|
1275
|
-
const redirectRequest = await createRedirectRequest(request, response.headers.get("Location"), body, options.validateRedirect);
|
|
1276
|
-
if (visited.has(redirectRequest.url)) throw new _fedify_vocab_runtime.FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
|
|
1277
|
-
return doubleKnockInternal(redirectRequest, identity, {
|
|
1278
|
-
...options,
|
|
1279
|
-
body
|
|
1280
|
-
}, redirected + 1, visited);
|
|
1281
|
-
} else if (response.status === 400 || response.status === 401 || response.status > 401) {
|
|
1282
|
-
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
1261
|
+
async function doubleKnockScoped(request, identity, options, openHop, consume) {
|
|
1262
|
+
const { response, session } = await doubleKnockCore(request, identity, options, openHop);
|
|
1263
|
+
try {
|
|
1264
|
+
return await consume(response);
|
|
1265
|
+
} catch (error) {
|
|
1283
1266
|
(0, _logtape_logtape.getLogger)([
|
|
1284
1267
|
"fedify",
|
|
1285
1268
|
"sig",
|
|
1286
1269
|
"http"
|
|
1287
|
-
]).debug("Failed to
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
status: response.status,
|
|
1291
|
-
statusText: response.statusText
|
|
1270
|
+
]).debug("Failed to consume the response from {url}; closing the session: {error}", {
|
|
1271
|
+
url: request.url,
|
|
1272
|
+
error
|
|
1292
1273
|
});
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1274
|
+
throw error;
|
|
1275
|
+
} finally {
|
|
1276
|
+
await session.close();
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
function isRedirectResponse(response) {
|
|
1280
|
+
return response.status >= 300 && response.status < 400 && response.headers.has("Location");
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Starts cancelling the body of a response that is not passed to the caller,
|
|
1284
|
+
* without waiting for it to settle. The cancellation of a branch of a tee
|
|
1285
|
+
* (e.g., a body cloned by a fetch mock) settles only after the other branch
|
|
1286
|
+
* is cancelled too, per the Streams standard, so awaiting it could block
|
|
1287
|
+
* forever. Failures are only logged.
|
|
1288
|
+
*/
|
|
1289
|
+
function cancelResponseBody(response, url) {
|
|
1290
|
+
response.body?.cancel().catch((error) => {
|
|
1291
|
+
(0, _logtape_logtape.getLogger)([
|
|
1292
|
+
"fedify",
|
|
1293
|
+
"sig",
|
|
1294
|
+
"http"
|
|
1295
|
+
]).debug("Failed to cancel the response body from {url}: {error}", {
|
|
1296
|
+
url,
|
|
1297
|
+
error
|
|
1297
1298
|
});
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
async function doubleKnockCore(request, identity, options, openHop) {
|
|
1302
|
+
const { specDeterminer, log, tracerProvider, signal } = options;
|
|
1303
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1304
|
+
const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
|
|
1305
|
+
let session = await openHop(request.url);
|
|
1306
|
+
try {
|
|
1307
|
+
for (let redirected = 0;; redirected++) {
|
|
1308
|
+
visited.add(request.url);
|
|
1309
|
+
const origin = new URL(request.url).origin;
|
|
1310
|
+
const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
|
|
1311
|
+
let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1312
|
+
spec: firstTrySpec,
|
|
1313
|
+
tracerProvider,
|
|
1314
|
+
body
|
|
1315
|
+
});
|
|
1316
|
+
log?.(signedRequest);
|
|
1317
|
+
let response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
1318
|
+
const firstRedirected = isRedirectResponse(response);
|
|
1319
|
+
if (!firstRedirected && (response.status === 400 || response.status === 401 || response.status > 401)) {
|
|
1320
|
+
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
1321
|
+
(0, _logtape_logtape.getLogger)([
|
|
1322
|
+
"fedify",
|
|
1323
|
+
"sig",
|
|
1324
|
+
"http"
|
|
1325
|
+
]).debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
|
|
1326
|
+
spec: firstTrySpec,
|
|
1327
|
+
secondSpec: spec,
|
|
1328
|
+
status: response.status,
|
|
1329
|
+
statusText: response.statusText
|
|
1330
|
+
});
|
|
1331
|
+
cancelResponseBody(response, request.url);
|
|
1332
|
+
signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1333
|
+
spec,
|
|
1334
|
+
tracerProvider,
|
|
1335
|
+
body
|
|
1336
|
+
});
|
|
1337
|
+
log?.(signedRequest);
|
|
1338
|
+
response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
1339
|
+
if (!isRedirectResponse(response) && response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
|
|
1340
|
+
} else if (!firstRedirected) await specDeterminer?.rememberSpec(origin, firstTrySpec);
|
|
1341
|
+
if (!isRedirectResponse(response)) return {
|
|
1342
|
+
response,
|
|
1343
|
+
session
|
|
1344
|
+
};
|
|
1345
|
+
cancelResponseBody(response, request.url);
|
|
1346
|
+
await session.close();
|
|
1301
1347
|
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new _fedify_vocab_runtime.FetchError(request.url, `Too many redirections (${redirected + 1})`);
|
|
1302
|
-
const
|
|
1348
|
+
const location = response.headers.get("Location");
|
|
1349
|
+
const redirectRequest = await createRedirectRequest(request, location, body, options.validateRedirect);
|
|
1303
1350
|
if (visited.has(redirectRequest.url)) throw new _fedify_vocab_runtime.FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1351
|
+
request = redirectRequest;
|
|
1352
|
+
session = await openHop(request.url);
|
|
1353
|
+
}
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
try {
|
|
1356
|
+
await session.close();
|
|
1357
|
+
} catch (closeError) {
|
|
1358
|
+
(0, _logtape_logtape.getLogger)([
|
|
1359
|
+
"fedify",
|
|
1360
|
+
"sig",
|
|
1361
|
+
"http"
|
|
1362
|
+
]).debug("Failed to close the session for {url}: {error}", {
|
|
1363
|
+
url: request.url,
|
|
1364
|
+
error: closeError
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
throw error;
|
|
1368
|
+
}
|
|
1311
1369
|
}
|
|
1312
1370
|
/**
|
|
1313
1371
|
* Performs a timing-safe equality comparison between two `Uint8Array` values.
|
|
@@ -1337,10 +1395,10 @@ function timingSafeEqual(a, b) {
|
|
|
1337
1395
|
return result === 0;
|
|
1338
1396
|
}
|
|
1339
1397
|
//#endregion
|
|
1340
|
-
Object.defineProperty(exports, "
|
|
1398
|
+
Object.defineProperty(exports, "doubleKnockScoped", {
|
|
1341
1399
|
enumerable: true,
|
|
1342
1400
|
get: function() {
|
|
1343
|
-
return
|
|
1401
|
+
return doubleKnockScoped;
|
|
1344
1402
|
}
|
|
1345
1403
|
});
|
|
1346
1404
|
Object.defineProperty(exports, "exportJwk", {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
3
|
-
import { TracerProvider } from "@opentelemetry/api";
|
|
4
3
|
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
5
|
-
|
|
4
|
+
import { TracerProvider } from "@opentelemetry/api";
|
|
6
5
|
//#region src/sig/key.d.ts
|
|
7
6
|
/**
|
|
8
7
|
* Generates a key pair which is appropriate for Fedify.
|
|
@@ -10,7 +10,7 @@ import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
|
10
10
|
import { Item, decodeDict, encodeItem } from "structured-field-values";
|
|
11
11
|
//#region deno.json
|
|
12
12
|
var name = "@fedify/fedify";
|
|
13
|
-
var version = "2.0.35-dev.
|
|
13
|
+
var version = "2.0.35-dev.2485+75206550";
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/sig/key.ts
|
|
16
16
|
/** Classifies decoding failures without hiding unexpected loader errors. */
|
|
@@ -1190,10 +1190,10 @@ async function createRedirectRequest(request, location, body, validateRedirect)
|
|
|
1190
1190
|
cache: request.cache
|
|
1191
1191
|
});
|
|
1192
1192
|
}
|
|
1193
|
-
async function fetchDoubleKnockRequest(request, signedRequest, signal) {
|
|
1193
|
+
async function fetchDoubleKnockRequest(session, request, signedRequest, signal) {
|
|
1194
1194
|
const maxAttempts = request.method === "GET" || request.method === "HEAD" ? 2 : 1;
|
|
1195
1195
|
for (let attempt = 1;; attempt++) try {
|
|
1196
|
-
return await fetch(signedRequest, {
|
|
1196
|
+
return await session.fetch(signedRequest, {
|
|
1197
1197
|
redirect: "manual",
|
|
1198
1198
|
signal
|
|
1199
1199
|
});
|
|
@@ -1244,69 +1244,127 @@ function getAbortReason(signal) {
|
|
|
1244
1244
|
return signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
1245
1245
|
}
|
|
1246
1246
|
/**
|
|
1247
|
-
* Performs a double-knock request
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1247
|
+
* Performs a double-knock request like {@link doubleKnock}, opening a session
|
|
1248
|
+
* for every hop with the given function, and passes the final response to
|
|
1249
|
+
* the given function. The final session is closed after `consume` settles,
|
|
1250
|
+
* so `consume` must read or cancel the response body before it returns.
|
|
1251
|
+
* This is not exported from any entry point.
|
|
1250
1252
|
* @param request The request to send.
|
|
1251
1253
|
* @param identity The identity to use for signing the request.
|
|
1252
1254
|
* @param options The options for double-knock requests.
|
|
1253
|
-
* @
|
|
1254
|
-
*
|
|
1255
|
+
* @param openHop The function to open a session for each hop. The session of
|
|
1256
|
+
* a hop is closed before the next hop is opened.
|
|
1257
|
+
* @param consume The function to consume the final response.
|
|
1258
|
+
* @returns The value returned by `consume`.
|
|
1255
1259
|
*/
|
|
1256
|
-
async function
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
visited.add(request.url);
|
|
1262
|
-
const origin = new URL(request.url).origin;
|
|
1263
|
-
const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
|
|
1264
|
-
const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
|
|
1265
|
-
let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1266
|
-
spec: firstTrySpec,
|
|
1267
|
-
tracerProvider,
|
|
1268
|
-
body
|
|
1269
|
-
});
|
|
1270
|
-
log?.(signedRequest);
|
|
1271
|
-
let response = await fetchDoubleKnockRequest(request, signedRequest, signal);
|
|
1272
|
-
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
1273
|
-
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(request.url, `Too many redirections (${redirected + 1})`);
|
|
1274
|
-
const redirectRequest = await createRedirectRequest(request, response.headers.get("Location"), body, options.validateRedirect);
|
|
1275
|
-
if (visited.has(redirectRequest.url)) throw new FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
|
|
1276
|
-
return doubleKnockInternal(redirectRequest, identity, {
|
|
1277
|
-
...options,
|
|
1278
|
-
body
|
|
1279
|
-
}, redirected + 1, visited);
|
|
1280
|
-
} else if (response.status === 400 || response.status === 401 || response.status > 401) {
|
|
1281
|
-
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
1260
|
+
async function doubleKnockScoped(request, identity, options, openHop, consume) {
|
|
1261
|
+
const { response, session } = await doubleKnockCore(request, identity, options, openHop);
|
|
1262
|
+
try {
|
|
1263
|
+
return await consume(response);
|
|
1264
|
+
} catch (error) {
|
|
1282
1265
|
getLogger([
|
|
1283
1266
|
"fedify",
|
|
1284
1267
|
"sig",
|
|
1285
1268
|
"http"
|
|
1286
|
-
]).debug("Failed to
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
status: response.status,
|
|
1290
|
-
statusText: response.statusText
|
|
1269
|
+
]).debug("Failed to consume the response from {url}; closing the session: {error}", {
|
|
1270
|
+
url: request.url,
|
|
1271
|
+
error
|
|
1291
1272
|
});
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1273
|
+
throw error;
|
|
1274
|
+
} finally {
|
|
1275
|
+
await session.close();
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
function isRedirectResponse(response) {
|
|
1279
|
+
return response.status >= 300 && response.status < 400 && response.headers.has("Location");
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Starts cancelling the body of a response that is not passed to the caller,
|
|
1283
|
+
* without waiting for it to settle. The cancellation of a branch of a tee
|
|
1284
|
+
* (e.g., a body cloned by a fetch mock) settles only after the other branch
|
|
1285
|
+
* is cancelled too, per the Streams standard, so awaiting it could block
|
|
1286
|
+
* forever. Failures are only logged.
|
|
1287
|
+
*/
|
|
1288
|
+
function cancelResponseBody(response, url) {
|
|
1289
|
+
response.body?.cancel().catch((error) => {
|
|
1290
|
+
getLogger([
|
|
1291
|
+
"fedify",
|
|
1292
|
+
"sig",
|
|
1293
|
+
"http"
|
|
1294
|
+
]).debug("Failed to cancel the response body from {url}: {error}", {
|
|
1295
|
+
url,
|
|
1296
|
+
error
|
|
1296
1297
|
});
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
async function doubleKnockCore(request, identity, options, openHop) {
|
|
1301
|
+
const { specDeterminer, log, tracerProvider, signal } = options;
|
|
1302
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1303
|
+
const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
|
|
1304
|
+
let session = await openHop(request.url);
|
|
1305
|
+
try {
|
|
1306
|
+
for (let redirected = 0;; redirected++) {
|
|
1307
|
+
visited.add(request.url);
|
|
1308
|
+
const origin = new URL(request.url).origin;
|
|
1309
|
+
const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
|
|
1310
|
+
let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1311
|
+
spec: firstTrySpec,
|
|
1312
|
+
tracerProvider,
|
|
1313
|
+
body
|
|
1314
|
+
});
|
|
1315
|
+
log?.(signedRequest);
|
|
1316
|
+
let response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
1317
|
+
const firstRedirected = isRedirectResponse(response);
|
|
1318
|
+
if (!firstRedirected && (response.status === 400 || response.status === 401 || response.status > 401)) {
|
|
1319
|
+
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
1320
|
+
getLogger([
|
|
1321
|
+
"fedify",
|
|
1322
|
+
"sig",
|
|
1323
|
+
"http"
|
|
1324
|
+
]).debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
|
|
1325
|
+
spec: firstTrySpec,
|
|
1326
|
+
secondSpec: spec,
|
|
1327
|
+
status: response.status,
|
|
1328
|
+
statusText: response.statusText
|
|
1329
|
+
});
|
|
1330
|
+
cancelResponseBody(response, request.url);
|
|
1331
|
+
signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
1332
|
+
spec,
|
|
1333
|
+
tracerProvider,
|
|
1334
|
+
body
|
|
1335
|
+
});
|
|
1336
|
+
log?.(signedRequest);
|
|
1337
|
+
response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
1338
|
+
if (!isRedirectResponse(response) && response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
|
|
1339
|
+
} else if (!firstRedirected) await specDeterminer?.rememberSpec(origin, firstTrySpec);
|
|
1340
|
+
if (!isRedirectResponse(response)) return {
|
|
1341
|
+
response,
|
|
1342
|
+
session
|
|
1343
|
+
};
|
|
1344
|
+
cancelResponseBody(response, request.url);
|
|
1345
|
+
await session.close();
|
|
1300
1346
|
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(request.url, `Too many redirections (${redirected + 1})`);
|
|
1301
|
-
const
|
|
1347
|
+
const location = response.headers.get("Location");
|
|
1348
|
+
const redirectRequest = await createRedirectRequest(request, location, body, options.validateRedirect);
|
|
1302
1349
|
if (visited.has(redirectRequest.url)) throw new FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1350
|
+
request = redirectRequest;
|
|
1351
|
+
session = await openHop(request.url);
|
|
1352
|
+
}
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
try {
|
|
1355
|
+
await session.close();
|
|
1356
|
+
} catch (closeError) {
|
|
1357
|
+
getLogger([
|
|
1358
|
+
"fedify",
|
|
1359
|
+
"sig",
|
|
1360
|
+
"http"
|
|
1361
|
+
]).debug("Failed to close the session for {url}: {error}", {
|
|
1362
|
+
url: request.url,
|
|
1363
|
+
error: closeError
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
throw error;
|
|
1367
|
+
}
|
|
1310
1368
|
}
|
|
1311
1369
|
/**
|
|
1312
1370
|
* Performs a timing-safe equality comparison between two `Uint8Array` values.
|
|
@@ -1336,4 +1394,4 @@ function timingSafeEqual(a, b) {
|
|
|
1336
1394
|
return result === 0;
|
|
1337
1395
|
}
|
|
1338
1396
|
//#endregion
|
|
1339
|
-
export { fetchActorDocument as a, importJwk as c, name as d, version as f, exportJwk as i, validateCryptoKey as l, signRequest as n, fetchKey as o, verifyRequest as r, generateCryptoKeyPair as s,
|
|
1397
|
+
export { fetchActorDocument as a, importJwk as c, name as d, version as f, exportJwk as i, validateCryptoKey as l, signRequest as n, fetchKey as o, verifyRequest as r, generateCryptoKeyPair as s, doubleKnockScoped as t, verifyKeyOwnership as u };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { o as validateCryptoKey, r as fetchKey } from "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-DL_ifPJk.mjs";
|
|
5
|
+
import { o as validateCryptoKey, r as fetchKey } from "./key-CgcWGw6W.mjs";
|
|
6
6
|
import { CryptographicKey } from "@fedify/vocab";
|
|
7
7
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
8
8
|
import { FetchError } from "@fedify/vocab-runtime";
|
|
@@ -710,10 +710,10 @@ async function createRedirectRequest(request, location, body, validateRedirect)
|
|
|
710
710
|
cache: request.cache
|
|
711
711
|
});
|
|
712
712
|
}
|
|
713
|
-
async function fetchDoubleKnockRequest(request, signedRequest, signal) {
|
|
713
|
+
async function fetchDoubleKnockRequest(session, request, signedRequest, signal) {
|
|
714
714
|
const maxAttempts = request.method === "GET" || request.method === "HEAD" ? 2 : 1;
|
|
715
715
|
for (let attempt = 1;; attempt++) try {
|
|
716
|
-
return await fetch(signedRequest, {
|
|
716
|
+
return await session.fetch(signedRequest, {
|
|
717
717
|
redirect: "manual",
|
|
718
718
|
signal
|
|
719
719
|
});
|
|
@@ -774,59 +774,146 @@ function getAbortReason(signal) {
|
|
|
774
774
|
* @since 1.6.0
|
|
775
775
|
*/
|
|
776
776
|
async function doubleKnock(request, identity, options = {}) {
|
|
777
|
-
|
|
777
|
+
const { response } = await doubleKnockCore(request, identity, options, openGlobalFetchHop);
|
|
778
|
+
return response;
|
|
778
779
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
return
|
|
797
|
-
|
|
798
|
-
body
|
|
799
|
-
}, redirected + 1, visited);
|
|
800
|
-
} else if (response.status === 400 || response.status === 401 || response.status > 401) {
|
|
801
|
-
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
780
|
+
/**
|
|
781
|
+
* Performs a double-knock request like {@link doubleKnock}, opening a session
|
|
782
|
+
* for every hop with the given function, and passes the final response to
|
|
783
|
+
* the given function. The final session is closed after `consume` settles,
|
|
784
|
+
* so `consume` must read or cancel the response body before it returns.
|
|
785
|
+
* This is not exported from any entry point.
|
|
786
|
+
* @param request The request to send.
|
|
787
|
+
* @param identity The identity to use for signing the request.
|
|
788
|
+
* @param options The options for double-knock requests.
|
|
789
|
+
* @param openHop The function to open a session for each hop. The session of
|
|
790
|
+
* a hop is closed before the next hop is opened.
|
|
791
|
+
* @param consume The function to consume the final response.
|
|
792
|
+
* @returns The value returned by `consume`.
|
|
793
|
+
*/
|
|
794
|
+
async function doubleKnockScoped(request, identity, options, openHop, consume) {
|
|
795
|
+
const { response, session } = await doubleKnockCore(request, identity, options, openHop);
|
|
796
|
+
try {
|
|
797
|
+
return await consume(response);
|
|
798
|
+
} catch (error) {
|
|
802
799
|
getLogger([
|
|
803
800
|
"fedify",
|
|
804
801
|
"sig",
|
|
805
802
|
"http"
|
|
806
|
-
]).debug("Failed to
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
status: response.status,
|
|
810
|
-
statusText: response.statusText
|
|
803
|
+
]).debug("Failed to consume the response from {url}; closing the session: {error}", {
|
|
804
|
+
url: request.url,
|
|
805
|
+
error
|
|
811
806
|
});
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
807
|
+
throw error;
|
|
808
|
+
} finally {
|
|
809
|
+
await session.close();
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* A hop session for {@link doubleKnock}: it uses the global `fetch()` without
|
|
814
|
+
* validating or pinning addresses, and its `close()` does nothing, so that
|
|
815
|
+
* the final response can be returned to the caller.
|
|
816
|
+
*/
|
|
817
|
+
function openGlobalFetchHop(url) {
|
|
818
|
+
const sessionUrl = new URL(url);
|
|
819
|
+
return Promise.resolve({
|
|
820
|
+
get url() {
|
|
821
|
+
return new URL(sessionUrl);
|
|
822
|
+
},
|
|
823
|
+
fetch: (input, init) => fetch(input, init),
|
|
824
|
+
close: () => Promise.resolve()
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
function isRedirectResponse(response) {
|
|
828
|
+
return response.status >= 300 && response.status < 400 && response.headers.has("Location");
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Starts cancelling the body of a response that is not passed to the caller,
|
|
832
|
+
* without waiting for it to settle. The cancellation of a branch of a tee
|
|
833
|
+
* (e.g., a body cloned by a fetch mock) settles only after the other branch
|
|
834
|
+
* is cancelled too, per the Streams standard, so awaiting it could block
|
|
835
|
+
* forever. Failures are only logged.
|
|
836
|
+
*/
|
|
837
|
+
function cancelResponseBody(response, url) {
|
|
838
|
+
response.body?.cancel().catch((error) => {
|
|
839
|
+
getLogger([
|
|
840
|
+
"fedify",
|
|
841
|
+
"sig",
|
|
842
|
+
"http"
|
|
843
|
+
]).debug("Failed to cancel the response body from {url}: {error}", {
|
|
844
|
+
url,
|
|
845
|
+
error
|
|
816
846
|
});
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
async function doubleKnockCore(request, identity, options, openHop) {
|
|
850
|
+
const { specDeterminer, log, tracerProvider, signal } = options;
|
|
851
|
+
const visited = /* @__PURE__ */ new Set();
|
|
852
|
+
const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
|
|
853
|
+
let session = await openHop(request.url);
|
|
854
|
+
try {
|
|
855
|
+
for (let redirected = 0;; redirected++) {
|
|
856
|
+
visited.add(request.url);
|
|
857
|
+
const origin = new URL(request.url).origin;
|
|
858
|
+
const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
|
|
859
|
+
let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
860
|
+
spec: firstTrySpec,
|
|
861
|
+
tracerProvider,
|
|
862
|
+
body
|
|
863
|
+
});
|
|
864
|
+
log?.(signedRequest);
|
|
865
|
+
let response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
866
|
+
const firstRedirected = isRedirectResponse(response);
|
|
867
|
+
if (!firstRedirected && (response.status === 400 || response.status === 401 || response.status > 401)) {
|
|
868
|
+
const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
869
|
+
getLogger([
|
|
870
|
+
"fedify",
|
|
871
|
+
"sig",
|
|
872
|
+
"http"
|
|
873
|
+
]).debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
|
|
874
|
+
spec: firstTrySpec,
|
|
875
|
+
secondSpec: spec,
|
|
876
|
+
status: response.status,
|
|
877
|
+
statusText: response.statusText
|
|
878
|
+
});
|
|
879
|
+
cancelResponseBody(response, request.url);
|
|
880
|
+
signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
881
|
+
spec,
|
|
882
|
+
tracerProvider,
|
|
883
|
+
body
|
|
884
|
+
});
|
|
885
|
+
log?.(signedRequest);
|
|
886
|
+
response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
|
|
887
|
+
if (!isRedirectResponse(response) && response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
|
|
888
|
+
} else if (!firstRedirected) await specDeterminer?.rememberSpec(origin, firstTrySpec);
|
|
889
|
+
if (!isRedirectResponse(response)) return {
|
|
890
|
+
response,
|
|
891
|
+
session
|
|
892
|
+
};
|
|
893
|
+
cancelResponseBody(response, request.url);
|
|
894
|
+
await session.close();
|
|
820
895
|
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(request.url, `Too many redirections (${redirected + 1})`);
|
|
821
|
-
const
|
|
896
|
+
const location = response.headers.get("Location");
|
|
897
|
+
const redirectRequest = await createRedirectRequest(request, location, body, options.validateRedirect);
|
|
822
898
|
if (visited.has(redirectRequest.url)) throw new FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
899
|
+
request = redirectRequest;
|
|
900
|
+
session = await openHop(request.url);
|
|
901
|
+
}
|
|
902
|
+
} catch (error) {
|
|
903
|
+
try {
|
|
904
|
+
await session.close();
|
|
905
|
+
} catch (closeError) {
|
|
906
|
+
getLogger([
|
|
907
|
+
"fedify",
|
|
908
|
+
"sig",
|
|
909
|
+
"http"
|
|
910
|
+
]).debug("Failed to close the session for {url}: {error}", {
|
|
911
|
+
url: request.url,
|
|
912
|
+
error: closeError
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
throw error;
|
|
916
|
+
}
|
|
830
917
|
}
|
|
831
918
|
/**
|
|
832
919
|
* Performs a timing-safe equality comparison between two `Uint8Array` values.
|
|
@@ -856,4 +943,4 @@ function timingSafeEqual(a, b) {
|
|
|
856
943
|
return result === 0;
|
|
857
944
|
}
|
|
858
945
|
//#endregion
|
|
859
|
-
export {
|
|
946
|
+
export { formatRfc9421SignatureParameters as a, signRequest as c, formatRfc9421Signature as i, timingSafeEqual as l, doubleKnock as n, parseRfc9421Signature as o, doubleKnockScoped as r, parseRfc9421SignatureInput as s, createRfc9421SignatureBase as t, verifyRequest as u };
|