@fedify/fedify 2.4.5-dev.2462 → 2.4.6-dev.2628

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.
Files changed (119) hide show
  1. package/README.md +3 -0
  2. package/dist/{builder-BBUGd9wr.mjs → builder-kj5boG2l.mjs} +2 -2
  3. package/dist/{client-D-XP_Ccb.d.cts → client-3InDZbba.d.cts} +0 -1
  4. package/dist/{client-D-XP_Ccb.d.ts → client-3InDZbba.d.ts} +0 -1
  5. package/dist/{client-CEnNz8O7.mjs → client-5SIFzW3q.mjs} +113 -64
  6. package/dist/compat/mod.d.cts +1 -1
  7. package/dist/compat/mod.d.ts +1 -1
  8. package/dist/compat/transformers.test.mjs +1 -1
  9. package/dist/{compound-proof-CariICma.mjs → compound-proof-BoNAYSHt.mjs} +2 -2
  10. package/dist/{context-7NyiqIcg.d.ts → context-CVOOd9pD.d.ts} +28 -2
  11. package/dist/{context-tsYPJ2Ew.d.cts → context-D0gFIgev.d.cts} +28 -2
  12. package/dist/{deno-BO7-a3hz.mjs → deno-ksnVeCA7.mjs} +1 -1
  13. package/dist/docloader-BhVNi06k.mjs +136 -0
  14. package/dist/federation/builder.test.mjs +1 -1
  15. package/dist/federation/compound-fanout.test.mjs +6 -3
  16. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  17. package/dist/federation/handler.test.mjs +4 -4
  18. package/dist/federation/hashlink-media.test.mjs +2 -2
  19. package/dist/federation/idempotency.test.mjs +2 -2
  20. package/dist/federation/inbox-report.test.mjs +5 -5
  21. package/dist/federation/inbox.test.mjs +1 -1
  22. package/dist/federation/metrics.test.mjs +1 -1
  23. package/dist/federation/middleware.test.mjs +465 -32
  24. package/dist/federation/mod.cjs +1 -1
  25. package/dist/federation/mod.d.cts +3 -3
  26. package/dist/federation/mod.d.ts +3 -3
  27. package/dist/federation/mod.js +1 -1
  28. package/dist/federation/outgoing-proof.test.mjs +9 -6
  29. package/dist/federation/portable-actor.test.mjs +3 -3
  30. package/dist/federation/portable-chain.test.mjs +3 -3
  31. package/dist/federation/portable-collection.test.mjs +4 -4
  32. package/dist/federation/portable-delivery.test.mjs +2 -2
  33. package/dist/federation/portable-gateway-keys.test.mjs +11 -5
  34. package/dist/federation/portable-inbox.test.mjs +4 -4
  35. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  36. package/dist/federation/portable-route-activity.test.mjs +2 -2
  37. package/dist/federation/portable.test.mjs +3 -3
  38. package/dist/federation/send.test.mjs +498 -88
  39. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  40. package/dist/federation/tasks/tasks.test.mjs +2 -2
  41. package/dist/federation/temporal.test.mjs +1 -1
  42. package/dist/federation/webfinger.test.mjs +1 -1
  43. package/dist/{federation-Dd9IwOEi.cjs → federation-BtPz9sr3.cjs} +240 -102
  44. package/dist/{http-D7L2pqLT.mjs → http-BSARoDPh.mjs} +204 -106
  45. package/dist/{http-DLf8QYD6.cjs → http-CeVcKQMZ.cjs} +179 -110
  46. package/dist/{http-BVrOR6MG.js → http-rH230xCD.js} +178 -109
  47. package/dist/{inbox-DtEKOr3W.mjs → inbox-BdZenpZQ.mjs} +2 -2
  48. package/dist/{key-DYxuyd-s.d.cts → key-CyCcut3h.d.ts} +1 -2
  49. package/dist/{key-C1nv7_jD.d.ts → key-VEL1QqHF.d.cts} +1 -2
  50. package/dist/{kv-cache-Dkzuqs8B.cjs → kv-cache-BpGIONmd.cjs} +72 -27
  51. package/dist/{kv-cache-DoZoRS9p.js → kv-cache-CNfNI_95.js} +73 -28
  52. package/dist/{kv-cache-m3RIr811.mjs → kv-cache-Cxn7pNMx.mjs} +1 -1
  53. package/dist/{ld-PJLGA2MJ.mjs → ld-DJZrjNuN.mjs} +2 -2
  54. package/dist/{metrics-CGR9cI-Y.mjs → metrics-DtxsYIkN.mjs} +1 -1
  55. package/dist/{middleware-BxhBd7pw.js → middleware-DDvck2q9.js} +241 -103
  56. package/dist/{middleware-ByanhTvb.mjs → middleware-DblGj642.mjs} +82 -42
  57. package/dist/{middleware-DPI-Jp9w.mjs → middleware-DyEjt33n.mjs} +1 -1
  58. package/dist/{mod-DEZXwV6J.d.ts → mod-B1Qt5cQQ.d.ts} +2 -9
  59. package/dist/{mod-Xj5P4s4d.d.ts → mod-B5COx5Wd.d.ts} +1 -1
  60. package/dist/{mod-CavhAgLw.d.cts → mod-BTxJG7ZH.d.cts} +1 -1
  61. package/dist/{mod-CtpF2BU5.d.ts → mod-CpyjFA9s.d.ts} +1 -1
  62. package/dist/{mod-BojP2Bw3.d.cts → mod-makPMVMg.d.cts} +1 -1
  63. package/dist/{mod-Dw-nPxQh.d.cts → mod-ss0wBUH1.d.cts} +2 -9
  64. package/dist/mod.cjs +5 -5
  65. package/dist/mod.d.cts +6 -6
  66. package/dist/mod.d.ts +6 -6
  67. package/dist/mod.js +5 -5
  68. package/dist/nodeinfo/client.test.mjs +337 -19
  69. package/dist/nodeinfo/handler.test.mjs +1 -1
  70. package/dist/nodeinfo/mod.cjs +1 -1
  71. package/dist/nodeinfo/mod.d.cts +1 -1
  72. package/dist/nodeinfo/mod.d.ts +1 -1
  73. package/dist/nodeinfo/mod.js +1 -1
  74. package/dist/otel/exporter.test.mjs +292 -30
  75. package/dist/otel/mod.cjs +36 -30
  76. package/dist/otel/mod.d.cts +13 -0
  77. package/dist/otel/mod.d.ts +13 -0
  78. package/dist/otel/mod.js +36 -30
  79. package/dist/{owner-B5c276hH.mjs → owner-2vfqhUJw.mjs} +3 -3
  80. package/dist/{portable-collection-BMZY9qT-.js → portable-collection-B7AQ_dW7.js} +1 -1
  81. package/dist/{portable-collection-CR5uwjws.cjs → portable-collection-Cs--KD5A.cjs} +1 -1
  82. package/dist/{portable-collection-C88tBiy0.mjs → portable-collection-DKJwGO8S.mjs} +2 -2
  83. package/dist/{portable-key-DxTXUpCK.mjs → portable-key-DXGepvtD.mjs} +1 -1
  84. package/dist/{proof-DNu-qP0i.mjs → proof-DW0FK6Cp.mjs} +3 -3
  85. package/dist/{send-CdCifQs4.mjs → send-CqsocpvF.mjs} +174 -76
  86. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  87. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  88. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  89. package/dist/sig/compound-proof.test.mjs +2 -2
  90. package/dist/sig/http.test.mjs +553 -2
  91. package/dist/sig/key.test.mjs +1 -1
  92. package/dist/sig/ld.test.mjs +1 -1
  93. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  94. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  95. package/dist/sig/mod.cjs +2 -2
  96. package/dist/sig/mod.d.cts +2 -2
  97. package/dist/sig/mod.d.ts +2 -2
  98. package/dist/sig/mod.js +2 -2
  99. package/dist/sig/owner.test.mjs +1 -1
  100. package/dist/sig/portable-collection.test.mjs +1 -1
  101. package/dist/sig/portable-interop.test.mjs +3 -3
  102. package/dist/sig/portable-key.test.mjs +5 -5
  103. package/dist/sig/proof.test.mjs +4 -4
  104. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  105. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  106. package/dist/sig/verification.test.mjs +3 -3
  107. package/dist/{temporal-BuMu2bgP.mjs → temporal-Dm2E1-vs.mjs} +1 -1
  108. package/dist/testing/kv.test.mjs +1 -1
  109. package/dist/testing/mod.d.mts +26 -0
  110. package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
  111. package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
  112. package/dist/utils/docloader.test.mjs +344 -36
  113. package/dist/utils/kv-cache.test.mjs +1 -1
  114. package/dist/utils/mod.cjs +1 -1
  115. package/dist/utils/mod.d.cts +1 -1
  116. package/dist/utils/mod.d.ts +1 -1
  117. package/dist/utils/mod.js +1 -1
  118. package/package.json +7 -7
  119. package/dist/docloader-DQNRDYDb.mjs +0 -91
@@ -3,12 +3,12 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { m as parseKeyIdString } from "./portable-key-id-Ca30-hOa.mjs";
5
5
  import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
6
- import { n as version, t as name } from "./deno-BO7-a3hz.mjs";
7
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-CGR9cI-Y.mjs";
6
+ import { n as version, t as name } from "./deno-ksnVeCA7.mjs";
7
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-DtxsYIkN.mjs";
8
8
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
9
9
  import { a as promoteKeyOwnerEvidence } from "./key-owner-evidence-APB6NpSk.mjs";
10
- import { S as fetchKeyDetailed, T as validateCryptoKey, v as bypassKeyCacheReads } from "./ld-PJLGA2MJ.mjs";
11
- import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-DxTXUpCK.mjs";
10
+ import { S as fetchKeyDetailed, T as validateCryptoKey, v as bypassKeyCacheReads } from "./ld-DJZrjNuN.mjs";
11
+ import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-DXGepvtD.mjs";
12
12
  import { getLogger } from "@logtape/logtape";
13
13
  import { CryptographicKey } from "@fedify/vocab";
14
14
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1116,10 +1116,10 @@ async function createRedirectRequest(request, location, body, validateRedirect)
1116
1116
  cache: request.cache
1117
1117
  });
1118
1118
  }
1119
- async function fetchDoubleKnockRequest(request, signedRequest, signal) {
1119
+ async function fetchDoubleKnockRequest(session, request, signedRequest, signal) {
1120
1120
  const maxAttempts = request.method === "GET" || request.method === "HEAD" ? 2 : 1;
1121
1121
  for (let attempt = 1;; attempt++) try {
1122
- return await fetch(signedRequest, {
1122
+ return await session.fetch(signedRequest, {
1123
1123
  redirect: "manual",
1124
1124
  signal
1125
1125
  });
@@ -1180,115 +1180,213 @@ function getAbortReason(signal) {
1180
1180
  * @since 1.6.0
1181
1181
  */
1182
1182
  async function doubleKnock(request, identity, options = {}) {
1183
- return await doubleKnockInternal(request, identity, options);
1183
+ const { response } = await doubleKnockCore(request, identity, options, openGlobalFetchHop);
1184
+ return response;
1184
1185
  }
1185
- async function doubleKnockInternal(request, identity, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
1186
- const { specDeterminer, log, tracerProvider, signal } = options;
1187
- signal?.throwIfAborted();
1188
- const maximumRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
1189
- visited.add(request.url);
1190
- const origin = new URL(request.url).origin;
1191
- const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
1192
- signal?.throwIfAborted();
1193
- const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
1194
- let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
1195
- spec: firstTrySpec,
1196
- tracerProvider,
1197
- body
1186
+ /**
1187
+ * Performs a double-knock request like {@link doubleKnock}, opening a session
1188
+ * for every hop with the given function, and passes the final response to
1189
+ * the given function. The final session is closed after `consume` settles,
1190
+ * so `consume` must read or cancel the response body before it returns.
1191
+ * This is not exported from any entry point.
1192
+ * @param request The request to send.
1193
+ * @param identity The identity to use for signing the request.
1194
+ * @param options The options for double-knock requests.
1195
+ * @param openHop The function to open a session for each hop. The session of
1196
+ * a hop is closed before the next hop is opened.
1197
+ * @param consume The function to consume the final response.
1198
+ * @returns The value returned by `consume`.
1199
+ */
1200
+ async function doubleKnockScoped(request, identity, options, openHop, consume) {
1201
+ const { response, session } = await doubleKnockCore(request, identity, options, openHop);
1202
+ try {
1203
+ return await consume(response);
1204
+ } catch (error) {
1205
+ getLogger([
1206
+ "fedify",
1207
+ "sig",
1208
+ "http"
1209
+ ]).debug("Failed to consume the response from {url}; closing the session: {error}", {
1210
+ url: request.url,
1211
+ error
1212
+ });
1213
+ throw error;
1214
+ } finally {
1215
+ await session.close();
1216
+ }
1217
+ }
1218
+ /**
1219
+ * A hop session for {@link doubleKnock}: it uses the global `fetch()` without
1220
+ * validating or pinning addresses, and its `close()` does nothing, so that
1221
+ * the final response can be returned to the caller.
1222
+ */
1223
+ function openGlobalFetchHop(url) {
1224
+ const sessionUrl = new URL(url);
1225
+ return Promise.resolve({
1226
+ get url() {
1227
+ return new URL(sessionUrl);
1228
+ },
1229
+ fetch: (input, init) => fetch(input, init),
1230
+ close: () => Promise.resolve()
1198
1231
  });
1199
- signal?.throwIfAborted();
1200
- log?.(signedRequest);
1201
- let response = await fetchDoubleKnockRequest(request, signedRequest, signal);
1202
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
1203
- if (redirected >= maximumRedirection) throw new FetchError(request.url, `Too many redirections (${redirected + 1})`);
1204
- const redirectRequest = await createRedirectRequest(request, response.headers.get("Location"), body, options.validateRedirect);
1205
- if (visited.has(redirectRequest.url)) throw new FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
1206
- return doubleKnockInternal(redirectRequest, identity, {
1207
- ...options,
1208
- body
1209
- }, redirected + 1, visited);
1210
- } else if (response.status === 400 || response.status === 401 || response.status > 401) {
1211
- const logger = getLogger([
1232
+ }
1233
+ function isRedirectResponse(response) {
1234
+ return response.status >= 300 && response.status < 400 && response.headers.has("Location");
1235
+ }
1236
+ /**
1237
+ * Starts cancelling the body of a response that is not passed to the caller,
1238
+ * without waiting for it to settle. The cancellation of a branch of a tee
1239
+ * (e.g., a body cloned by a fetch mock) settles only after the other branch
1240
+ * is cancelled too, per the Streams standard, so awaiting it could block
1241
+ * forever. Failures are only logged.
1242
+ */
1243
+ function cancelResponseBody(response, url) {
1244
+ response.body?.cancel().catch((error) => {
1245
+ getLogger([
1212
1246
  "fedify",
1213
1247
  "sig",
1214
1248
  "http"
1215
- ]);
1216
- const acceptSigHeader = response.headers.get("Accept-Signature");
1217
- if (acceptSigHeader != null) {
1218
- const entries = validateAcceptSignature(parseAcceptSignature(acceptSigHeader));
1219
- const localKeyId = identity.keyId.href;
1220
- const localAlg = "rsa-v1_5-sha256";
1221
- let fulfilled = false;
1222
- let challengeRequest;
1223
- for (const entry of entries) {
1224
- signal?.throwIfAborted();
1225
- const rfc9421 = fulfillAcceptSignature(entry, localKeyId, localAlg);
1226
- if (rfc9421 == null) continue;
1227
- logger.debug("Received Accept-Signature challenge; accumulating label {label} and components {components}.", {
1228
- label: rfc9421.label,
1229
- components: rfc9421.components
1230
- });
1231
- try {
1232
- challengeRequest = await signRequest(challengeRequest ?? request, identity.privateKey, identity.keyId, {
1233
- spec: "rfc9421",
1234
- tracerProvider,
1235
- body,
1236
- rfc9421
1249
+ ]).debug("Failed to cancel the response body from {url}: {error}", {
1250
+ url,
1251
+ error
1252
+ });
1253
+ });
1254
+ }
1255
+ async function doubleKnockCore(request, identity, options, openHop) {
1256
+ const { specDeterminer, log, tracerProvider, signal } = options;
1257
+ signal?.throwIfAborted();
1258
+ const maximumRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
1259
+ const visited = /* @__PURE__ */ new Set();
1260
+ const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
1261
+ let session = await openHop(request.url);
1262
+ try {
1263
+ for (let redirected = 0;; redirected++) {
1264
+ visited.add(request.url);
1265
+ const origin = new URL(request.url).origin;
1266
+ const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
1267
+ signal?.throwIfAborted();
1268
+ let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
1269
+ spec: firstTrySpec,
1270
+ tracerProvider,
1271
+ body
1272
+ });
1273
+ signal?.throwIfAborted();
1274
+ log?.(signedRequest);
1275
+ let response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
1276
+ const firstRedirected = isRedirectResponse(response);
1277
+ if (!firstRedirected && (response.status === 400 || response.status === 401 || response.status > 401)) {
1278
+ const logger = getLogger([
1279
+ "fedify",
1280
+ "sig",
1281
+ "http"
1282
+ ]);
1283
+ const acceptSigHeader = response.headers.get("Accept-Signature");
1284
+ if (acceptSigHeader != null) {
1285
+ const entries = validateAcceptSignature(parseAcceptSignature(acceptSigHeader));
1286
+ const localKeyId = identity.keyId.href;
1287
+ const localAlg = "rsa-v1_5-sha256";
1288
+ let fulfilled = false;
1289
+ let challengeRequest;
1290
+ for (const entry of entries) {
1291
+ signal?.throwIfAborted();
1292
+ const rfc9421 = fulfillAcceptSignature(entry, localKeyId, localAlg);
1293
+ if (rfc9421 == null) continue;
1294
+ logger.debug("Received Accept-Signature challenge; accumulating label {label} and components {components}.", {
1295
+ label: rfc9421.label,
1296
+ components: rfc9421.components
1297
+ });
1298
+ try {
1299
+ challengeRequest = await signRequest(challengeRequest ?? request, identity.privateKey, identity.keyId, {
1300
+ spec: "rfc9421",
1301
+ tracerProvider,
1302
+ body,
1303
+ rfc9421
1304
+ });
1305
+ fulfilled = true;
1306
+ } catch (error) {
1307
+ logger.debug("Failed to fulfill Accept-Signature challenge entry {label}: {error}", {
1308
+ label: entry.label,
1309
+ error
1310
+ });
1311
+ }
1312
+ }
1313
+ if (fulfilled && challengeRequest != null) {
1314
+ signal?.throwIfAborted();
1315
+ cancelResponseBody(response, request.url);
1316
+ signedRequest = challengeRequest;
1317
+ log?.(signedRequest);
1318
+ response = await session.fetch(signedRequest, {
1319
+ redirect: "manual",
1320
+ signal
1321
+ });
1322
+ }
1323
+ if (fulfilled && !isRedirectResponse(response)) {
1324
+ if (response.status < 300) {
1325
+ await specDeterminer?.rememberSpec(origin, "rfc9421");
1326
+ return {
1327
+ response,
1328
+ session
1329
+ };
1330
+ }
1331
+ if (response.status !== 400 && response.status !== 401) return {
1332
+ response,
1333
+ session
1334
+ };
1335
+ }
1336
+ }
1337
+ if (!isRedirectResponse(response)) {
1338
+ const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
1339
+ getLogger([
1340
+ "fedify",
1341
+ "sig",
1342
+ "http"
1343
+ ]).debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
1344
+ spec: firstTrySpec,
1345
+ secondSpec: spec,
1346
+ status: response.status,
1347
+ statusText: response.statusText
1237
1348
  });
1238
- fulfilled = true;
1239
- } catch (error) {
1240
- logger.debug("Failed to fulfill Accept-Signature challenge entry {label}: {error}", {
1241
- label: entry.label,
1242
- error
1349
+ cancelResponseBody(response, request.url);
1350
+ signal?.throwIfAborted();
1351
+ signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
1352
+ spec,
1353
+ tracerProvider,
1354
+ body
1243
1355
  });
1356
+ signal?.throwIfAborted();
1357
+ log?.(signedRequest);
1358
+ response = await fetchDoubleKnockRequest(session, request, signedRequest, signal);
1359
+ if (!isRedirectResponse(response) && response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
1244
1360
  }
1245
- }
1246
- if (fulfilled && challengeRequest != null) {
1247
- signal?.throwIfAborted();
1248
- signedRequest = challengeRequest;
1249
- log?.(signedRequest);
1250
- response = await fetch(signedRequest, {
1251
- redirect: "manual",
1252
- signal
1253
- });
1254
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) return doubleKnock(await createRedirectRequest(request, response.headers.get("Location"), body, options.validateRedirect), identity, {
1255
- ...options,
1256
- body
1257
- });
1258
- }
1259
- if (fulfilled && response.status < 300) {
1260
- await specDeterminer?.rememberSpec(origin, "rfc9421");
1261
- return response;
1262
- }
1263
- if (fulfilled && response.status !== 400 && response.status !== 401) return response;
1264
- }
1265
- const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
1266
- logger.debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
1267
- spec: firstTrySpec,
1268
- secondSpec: spec,
1269
- status: response.status,
1270
- statusText: response.statusText
1271
- });
1272
- signal?.throwIfAborted();
1273
- signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
1274
- spec,
1275
- tracerProvider,
1276
- body
1277
- });
1278
- signal?.throwIfAborted();
1279
- log?.(signedRequest);
1280
- response = await fetchDoubleKnockRequest(request, signedRequest, signal);
1281
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
1361
+ } else if (!firstRedirected) await specDeterminer?.rememberSpec(origin, firstTrySpec);
1362
+ if (!isRedirectResponse(response)) return {
1363
+ response,
1364
+ session
1365
+ };
1366
+ cancelResponseBody(response, request.url);
1367
+ await session.close();
1282
1368
  if (redirected >= maximumRedirection) throw new FetchError(request.url, `Too many redirections (${redirected + 1})`);
1283
- const redirectRequest = await createRedirectRequest(request, response.headers.get("Location"), body, options.validateRedirect);
1369
+ const location = response.headers.get("Location");
1370
+ const redirectRequest = await createRedirectRequest(request, location, body, options.validateRedirect);
1284
1371
  if (visited.has(redirectRequest.url)) throw new FetchError(request.url, `Redirect loop detected: ${redirectRequest.url}`);
1285
- return doubleKnockInternal(redirectRequest, identity, {
1286
- ...options,
1287
- body
1288
- }, redirected + 1, visited);
1289
- } else if (response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
1290
- } else await specDeterminer?.rememberSpec(origin, firstTrySpec);
1291
- return response;
1372
+ request = redirectRequest;
1373
+ session = await openHop(request.url);
1374
+ }
1375
+ } catch (error) {
1376
+ try {
1377
+ await session.close();
1378
+ } catch (closeError) {
1379
+ getLogger([
1380
+ "fedify",
1381
+ "sig",
1382
+ "http"
1383
+ ]).debug("Failed to close the session for {url}: {error}", {
1384
+ url: request.url,
1385
+ error: closeError
1386
+ });
1387
+ }
1388
+ throw error;
1389
+ }
1292
1390
  }
1293
1391
  /**
1294
1392
  * Performs a timing-safe equality comparison between two `Uint8Array` values.
@@ -1318,4 +1416,4 @@ function timingSafeEqual(a, b) {
1318
1416
  return result === 0;
1319
1417
  }
1320
1418
  //#endregion
1321
- export { listRequestSignatures as a, selectRequestSignature as c, validateMaxSignatures as d, verifyRequest as f, formatRfc9421SignatureParameters as i, signRequest as l, doubleKnock as n, parseRfc9421Signature as o, verifyRequestDetailed as p, formatRfc9421Signature as r, parseRfc9421SignatureInput as s, createRfc9421SignatureBase as t, timingSafeEqual as u };
1419
+ export { formatRfc9421SignatureParameters as a, parseRfc9421SignatureInput as c, timingSafeEqual as d, validateMaxSignatures as f, formatRfc9421Signature as i, selectRequestSignature as l, verifyRequestDetailed as m, doubleKnock as n, listRequestSignatures as o, verifyRequest as p, doubleKnockScoped as r, parseRfc9421Signature as s, createRfc9421SignatureBase as t, signRequest as u };