@fedify/webfinger 2.3.0-dev.994 → 2.3.0-pr.809.37
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/deno.json +2 -2
- package/dist/lookup.test.cjs +518 -143
- package/dist/lookup.test.js +520 -145
- package/dist/mod.cjs +148 -18
- package/dist/mod.d.cts +33 -2
- package/dist/mod.d.ts +33 -2
- package/dist/mod.js +148 -18
- package/package.json +7 -7
- package/src/lookup.test.ts +793 -236
- package/src/lookup.ts +253 -22
package/dist/lookup.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { test } from "@fedify/fixture";
|
|
1
|
+
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
2
2
|
import { withTimeout } from "es-toolkit";
|
|
3
|
-
import { deepStrictEqual } from "node:assert/strict";
|
|
3
|
+
import { deepStrictEqual, ok } from "node:assert/strict";
|
|
4
4
|
import { UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -11,7 +11,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
11
11
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
12
|
var __getProtoOf = Object.getPrototypeOf;
|
|
13
13
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
-
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
14
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
15
15
|
var __copyProps = (to, from, except, desc) => {
|
|
16
16
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
17
17
|
key = keys[i];
|
|
@@ -1227,7 +1227,7 @@ var esm_default = new class FetchMock {
|
|
|
1227
1227
|
//#endregion
|
|
1228
1228
|
//#region deno.json
|
|
1229
1229
|
var name = "@fedify/webfinger";
|
|
1230
|
-
var version = "2.3.0-
|
|
1230
|
+
var version = "2.3.0-pr.809.37+0574ba5c";
|
|
1231
1231
|
//#endregion
|
|
1232
1232
|
//#region src/lookup.ts
|
|
1233
1233
|
const logger = getLogger([
|
|
@@ -1236,6 +1236,58 @@ const logger = getLogger([
|
|
|
1236
1236
|
"lookup"
|
|
1237
1237
|
]);
|
|
1238
1238
|
const DEFAULT_MAX_REDIRECTION = 5;
|
|
1239
|
+
const WEBFINGER_HISTOGRAM_BUCKETS = [
|
|
1240
|
+
5,
|
|
1241
|
+
10,
|
|
1242
|
+
25,
|
|
1243
|
+
50,
|
|
1244
|
+
75,
|
|
1245
|
+
100,
|
|
1246
|
+
250,
|
|
1247
|
+
500,
|
|
1248
|
+
750,
|
|
1249
|
+
1e3,
|
|
1250
|
+
2500,
|
|
1251
|
+
5e3,
|
|
1252
|
+
7500,
|
|
1253
|
+
1e4
|
|
1254
|
+
];
|
|
1255
|
+
const webFingerInstruments = /* @__PURE__ */ new WeakMap();
|
|
1256
|
+
function getWebFingerInstruments(meterProvider) {
|
|
1257
|
+
let instruments = webFingerInstruments.get(meterProvider);
|
|
1258
|
+
if (instruments == null) {
|
|
1259
|
+
const meter = meterProvider.getMeter(name, version);
|
|
1260
|
+
instruments = {
|
|
1261
|
+
lookup: meter.createCounter("webfinger.lookup", {
|
|
1262
|
+
description: "Outgoing WebFinger lookup attempts.",
|
|
1263
|
+
unit: "{lookup}"
|
|
1264
|
+
}),
|
|
1265
|
+
lookupDuration: meter.createHistogram("webfinger.lookup.duration", {
|
|
1266
|
+
description: "Duration of outgoing WebFinger lookups.",
|
|
1267
|
+
unit: "ms",
|
|
1268
|
+
advice: { explicitBucketBoundaries: [...WEBFINGER_HISTOGRAM_BUCKETS] }
|
|
1269
|
+
})
|
|
1270
|
+
};
|
|
1271
|
+
webFingerInstruments.set(meterProvider, instruments);
|
|
1272
|
+
}
|
|
1273
|
+
return instruments;
|
|
1274
|
+
}
|
|
1275
|
+
function getResourceScheme(resource) {
|
|
1276
|
+
if (typeof resource === "string") {
|
|
1277
|
+
const colon = resource.indexOf(":");
|
|
1278
|
+
return colon > 0 ? resource.substring(0, colon).toLowerCase() : "";
|
|
1279
|
+
}
|
|
1280
|
+
return resource.protocol.replace(/:$/, "").toLowerCase();
|
|
1281
|
+
}
|
|
1282
|
+
const WEBFINGER_LOOKUP_SCHEME_WHITELIST = new Set([
|
|
1283
|
+
"acct",
|
|
1284
|
+
"http",
|
|
1285
|
+
"https",
|
|
1286
|
+
"mailto"
|
|
1287
|
+
]);
|
|
1288
|
+
function getMetricResourceScheme(scheme) {
|
|
1289
|
+
return WEBFINGER_LOOKUP_SCHEME_WHITELIST.has(scheme) ? scheme : "other";
|
|
1290
|
+
}
|
|
1239
1291
|
/**
|
|
1240
1292
|
* Looks up a WebFinger resource.
|
|
1241
1293
|
* @param resource The resource URL to look up.
|
|
@@ -1244,17 +1296,25 @@ const DEFAULT_MAX_REDIRECTION = 5;
|
|
|
1244
1296
|
* @since 0.2.0
|
|
1245
1297
|
*/
|
|
1246
1298
|
async function lookupWebFinger(resource, options = {}) {
|
|
1247
|
-
|
|
1299
|
+
const tracer = (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version);
|
|
1300
|
+
const scheme = getResourceScheme(resource);
|
|
1301
|
+
return await tracer.startActiveSpan("webfinger.lookup", {
|
|
1248
1302
|
kind: SpanKind.CLIENT,
|
|
1249
1303
|
attributes: {
|
|
1250
1304
|
"webfinger.resource": resource.toString(),
|
|
1251
|
-
"webfinger.resource.scheme":
|
|
1305
|
+
"webfinger.resource.scheme": scheme
|
|
1252
1306
|
}
|
|
1253
1307
|
}, async (span) => {
|
|
1308
|
+
const meterProvider = options.meterProvider;
|
|
1309
|
+
const start = meterProvider == null ? 0 : performance.now();
|
|
1310
|
+
let outcome = {
|
|
1311
|
+
resource: null,
|
|
1312
|
+
result: "error"
|
|
1313
|
+
};
|
|
1254
1314
|
try {
|
|
1255
|
-
|
|
1256
|
-
span.setStatus({ code:
|
|
1257
|
-
return
|
|
1315
|
+
outcome = await lookupWebFingerInternal(resource, options);
|
|
1316
|
+
span.setStatus({ code: outcome.resource === null ? SpanStatusCode.ERROR : SpanStatusCode.OK });
|
|
1317
|
+
return outcome.resource;
|
|
1258
1318
|
} catch (error) {
|
|
1259
1319
|
span.setStatus({
|
|
1260
1320
|
code: SpanStatusCode.ERROR,
|
|
@@ -1262,19 +1322,41 @@ async function lookupWebFinger(resource, options = {}) {
|
|
|
1262
1322
|
});
|
|
1263
1323
|
throw error;
|
|
1264
1324
|
} finally {
|
|
1325
|
+
if (meterProvider != null) recordWebFingerLookup(meterProvider, Math.max(0, performance.now() - start), scheme, outcome);
|
|
1265
1326
|
span.end();
|
|
1266
1327
|
}
|
|
1267
1328
|
});
|
|
1268
1329
|
}
|
|
1330
|
+
function recordWebFingerLookup(meterProvider, durationMs, scheme, outcome) {
|
|
1331
|
+
const attributes = {
|
|
1332
|
+
"webfinger.lookup.result": outcome.result,
|
|
1333
|
+
"webfinger.resource.scheme": getMetricResourceScheme(scheme)
|
|
1334
|
+
};
|
|
1335
|
+
if (outcome.remoteHost != null) attributes["activitypub.remote.host"] = outcome.remoteHost;
|
|
1336
|
+
if (outcome.statusCode != null) attributes["http.response.status_code"] = outcome.statusCode;
|
|
1337
|
+
const instruments = getWebFingerInstruments(meterProvider);
|
|
1338
|
+
instruments.lookup.add(1, attributes);
|
|
1339
|
+
instruments.lookupDuration.record(durationMs, attributes);
|
|
1340
|
+
}
|
|
1269
1341
|
async function lookupWebFingerInternal(resource, options = {}) {
|
|
1270
1342
|
if (typeof resource === "string") resource = new URL(resource);
|
|
1271
1343
|
let protocol = "https:";
|
|
1272
1344
|
let server;
|
|
1273
|
-
if (resource.protocol === "acct:") {
|
|
1345
|
+
if (resource.protocol === "acct:" || resource.protocol === "mailto:") {
|
|
1274
1346
|
const atPos = resource.pathname.lastIndexOf("@");
|
|
1275
|
-
if (atPos < 0) return
|
|
1347
|
+
if (atPos < 0) return {
|
|
1348
|
+
resource: null,
|
|
1349
|
+
result: "invalid"
|
|
1350
|
+
};
|
|
1276
1351
|
server = resource.pathname.substring(atPos + 1);
|
|
1277
|
-
if (server === "") return
|
|
1352
|
+
if (server === "" || /[/?#]/.test(server)) return {
|
|
1353
|
+
resource: null,
|
|
1354
|
+
result: "invalid"
|
|
1355
|
+
};
|
|
1356
|
+
if (resource.protocol === "acct:" && (resource.search !== "" || resource.hash !== "")) return {
|
|
1357
|
+
resource: null,
|
|
1358
|
+
result: "invalid"
|
|
1359
|
+
};
|
|
1278
1360
|
} else {
|
|
1279
1361
|
protocol = resource.protocol;
|
|
1280
1362
|
server = resource.host;
|
|
@@ -1283,6 +1365,7 @@ async function lookupWebFingerInternal(resource, options = {}) {
|
|
|
1283
1365
|
url.searchParams.set("resource", resource.href);
|
|
1284
1366
|
let redirected = 0;
|
|
1285
1367
|
while (true) {
|
|
1368
|
+
const remoteHost = url.host;
|
|
1286
1369
|
logger.debug("Fetching WebFinger resource descriptor from {url}...", { url: url.href });
|
|
1287
1370
|
let response;
|
|
1288
1371
|
if (options.allowPrivateAddress !== true) try {
|
|
@@ -1290,7 +1373,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
|
|
|
1290
1373
|
} catch (e) {
|
|
1291
1374
|
if (e instanceof UrlError) {
|
|
1292
1375
|
logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
|
|
1293
|
-
return
|
|
1376
|
+
return {
|
|
1377
|
+
resource: null,
|
|
1378
|
+
result: "network_error",
|
|
1379
|
+
remoteHost
|
|
1380
|
+
};
|
|
1294
1381
|
}
|
|
1295
1382
|
throw e;
|
|
1296
1383
|
}
|
|
@@ -1308,22 +1395,50 @@ async function lookupWebFingerInternal(resource, options = {}) {
|
|
|
1308
1395
|
url: url.href,
|
|
1309
1396
|
error
|
|
1310
1397
|
});
|
|
1311
|
-
return
|
|
1398
|
+
return {
|
|
1399
|
+
resource: null,
|
|
1400
|
+
result: "network_error",
|
|
1401
|
+
remoteHost
|
|
1402
|
+
};
|
|
1312
1403
|
}
|
|
1313
1404
|
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
1314
1405
|
redirected++;
|
|
1315
1406
|
const maxRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
|
|
1316
|
-
if (redirected
|
|
1407
|
+
if (redirected > maxRedirection) {
|
|
1317
1408
|
logger.error("Too many redirections ({redirections}) while fetching WebFinger resource descriptor.", { redirections: redirected });
|
|
1318
|
-
return
|
|
1409
|
+
return {
|
|
1410
|
+
resource: null,
|
|
1411
|
+
result: "invalid",
|
|
1412
|
+
statusCode: response.status,
|
|
1413
|
+
remoteHost
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
let redirectedUrl;
|
|
1417
|
+
try {
|
|
1418
|
+
redirectedUrl = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? url : response.url);
|
|
1419
|
+
} catch (e) {
|
|
1420
|
+
logger.error("Invalid Location header while following WebFinger redirect: {error}", {
|
|
1421
|
+
url: url.href,
|
|
1422
|
+
error: e
|
|
1423
|
+
});
|
|
1424
|
+
return {
|
|
1425
|
+
resource: null,
|
|
1426
|
+
result: "invalid",
|
|
1427
|
+
statusCode: response.status,
|
|
1428
|
+
remoteHost
|
|
1429
|
+
};
|
|
1319
1430
|
}
|
|
1320
|
-
const redirectedUrl = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? url : response.url);
|
|
1321
1431
|
if (redirectedUrl.protocol !== url.protocol) {
|
|
1322
1432
|
logger.error("Redirected to a different protocol ({protocol} to {redirectedProtocol}) while fetching WebFinger resource descriptor.", {
|
|
1323
1433
|
protocol: url.protocol,
|
|
1324
1434
|
redirectedProtocol: redirectedUrl.protocol
|
|
1325
1435
|
});
|
|
1326
|
-
return
|
|
1436
|
+
return {
|
|
1437
|
+
resource: null,
|
|
1438
|
+
result: "invalid",
|
|
1439
|
+
statusCode: response.status,
|
|
1440
|
+
remoteHost
|
|
1441
|
+
};
|
|
1327
1442
|
}
|
|
1328
1443
|
url = redirectedUrl;
|
|
1329
1444
|
continue;
|
|
@@ -1334,14 +1449,29 @@ async function lookupWebFingerInternal(resource, options = {}) {
|
|
|
1334
1449
|
status: response.status,
|
|
1335
1450
|
statusText: response.statusText
|
|
1336
1451
|
});
|
|
1337
|
-
return
|
|
1452
|
+
return {
|
|
1453
|
+
resource: null,
|
|
1454
|
+
result: response.status === 404 || response.status === 410 ? "not_found" : "error",
|
|
1455
|
+
statusCode: response.status,
|
|
1456
|
+
remoteHost
|
|
1457
|
+
};
|
|
1338
1458
|
}
|
|
1339
1459
|
try {
|
|
1340
|
-
return
|
|
1460
|
+
return {
|
|
1461
|
+
resource: await response.json(),
|
|
1462
|
+
result: "found",
|
|
1463
|
+
statusCode: response.status,
|
|
1464
|
+
remoteHost
|
|
1465
|
+
};
|
|
1341
1466
|
} catch (e) {
|
|
1342
1467
|
if (e instanceof SyntaxError) {
|
|
1343
1468
|
logger.debug("Failed to parse WebFinger resource descriptor as JSON: {error}", { error: e });
|
|
1344
|
-
return
|
|
1469
|
+
return {
|
|
1470
|
+
resource: null,
|
|
1471
|
+
result: "invalid",
|
|
1472
|
+
statusCode: response.status,
|
|
1473
|
+
remoteHost
|
|
1474
|
+
};
|
|
1345
1475
|
}
|
|
1346
1476
|
throw e;
|
|
1347
1477
|
}
|
|
@@ -1359,52 +1489,64 @@ test({
|
|
|
1359
1489
|
deepStrictEqual(await lookupWebFinger(new URL("acct:johndoe")), null);
|
|
1360
1490
|
deepStrictEqual(await lookupWebFinger("acct:johndoe@"), null);
|
|
1361
1491
|
deepStrictEqual(await lookupWebFinger(new URL("acct:johndoe@")), null);
|
|
1492
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com/exploit"), null);
|
|
1493
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com?x=1"), null);
|
|
1494
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com#frag"), null);
|
|
1362
1495
|
});
|
|
1363
1496
|
await t.step("connection refused", async () => {
|
|
1364
1497
|
deepStrictEqual(await lookupWebFinger("acct:johndoe@fedify-test.internal"), null);
|
|
1365
1498
|
deepStrictEqual(await lookupWebFinger("https://fedify-test.internal/foo"), null);
|
|
1366
1499
|
});
|
|
1367
1500
|
esm_default.spyGlobal();
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1501
|
+
try {
|
|
1502
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", { status: 404 });
|
|
1503
|
+
await t.step("not found", async () => {
|
|
1504
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com"), null);
|
|
1505
|
+
deepStrictEqual(await lookupWebFinger("https://example.com/foo"), null);
|
|
1506
|
+
});
|
|
1507
|
+
const expected = {
|
|
1508
|
+
subject: "acct:johndoe@example.com",
|
|
1509
|
+
links: []
|
|
1510
|
+
};
|
|
1511
|
+
esm_default.removeRoutes();
|
|
1512
|
+
esm_default.get("https://example.com/.well-known/webfinger?resource=acct%3Ajohndoe%40example.com", { body: expected });
|
|
1513
|
+
await t.step("acct", async () => {
|
|
1514
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com"), expected);
|
|
1515
|
+
});
|
|
1516
|
+
const expected2 = {
|
|
1517
|
+
subject: "https://example.com/foo",
|
|
1518
|
+
links: []
|
|
1519
|
+
};
|
|
1520
|
+
esm_default.removeRoutes();
|
|
1521
|
+
esm_default.get("https://example.com/.well-known/webfinger?resource=https%3A%2F%2Fexample.com%2Ffoo", { body: expected2 });
|
|
1522
|
+
await t.step("https", async () => {
|
|
1523
|
+
deepStrictEqual(await lookupWebFinger("https://example.com/foo"), expected2);
|
|
1524
|
+
});
|
|
1525
|
+
const mailtoExpected = {
|
|
1526
|
+
subject: "mailto:juliet@example.com",
|
|
1527
|
+
links: []
|
|
1528
|
+
};
|
|
1529
|
+
esm_default.removeRoutes();
|
|
1530
|
+
esm_default.get("https://example.com/.well-known/webfinger?resource=mailto%3Ajuliet%40example.com", { body: mailtoExpected });
|
|
1531
|
+
await t.step("mailto", async () => {
|
|
1532
|
+
deepStrictEqual(await lookupWebFinger("mailto:juliet@example.com"), mailtoExpected);
|
|
1533
|
+
});
|
|
1534
|
+
const mailtoQueryExpected = {
|
|
1535
|
+
subject: "mailto:juliet@example.com?subject=Hi",
|
|
1536
|
+
links: []
|
|
1537
|
+
};
|
|
1538
|
+
esm_default.removeRoutes();
|
|
1539
|
+
esm_default.get("https://example.com/.well-known/webfinger?resource=mailto%3Ajuliet%40example.com%3Fsubject%3DHi", { body: mailtoQueryExpected });
|
|
1540
|
+
await t.step("mailto with hfields", async () => {
|
|
1541
|
+
deepStrictEqual(await lookupWebFinger("mailto:juliet@example.com?subject=Hi"), mailtoQueryExpected);
|
|
1542
|
+
});
|
|
1543
|
+
esm_default.removeRoutes();
|
|
1544
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", { body: "not json" });
|
|
1545
|
+
await t.step("invalid response", async () => {
|
|
1546
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com"), null);
|
|
1547
|
+
});
|
|
1548
|
+
esm_default.removeRoutes();
|
|
1549
|
+
esm_default.get("begin:https://localhost/.well-known/webfinger?", {
|
|
1408
1550
|
subject: "acct:test@localhost",
|
|
1409
1551
|
links: [{
|
|
1410
1552
|
rel: "self",
|
|
@@ -1412,109 +1554,342 @@ test({
|
|
|
1412
1554
|
href: "https://localhost/actor"
|
|
1413
1555
|
}]
|
|
1414
1556
|
});
|
|
1557
|
+
await t.step("private address", async () => {
|
|
1558
|
+
deepStrictEqual(await lookupWebFinger("acct:test@localhost"), null);
|
|
1559
|
+
deepStrictEqual(await lookupWebFinger("acct:test@localhost", { allowPrivateAddress: true }), {
|
|
1560
|
+
subject: "acct:test@localhost",
|
|
1561
|
+
links: [{
|
|
1562
|
+
rel: "self",
|
|
1563
|
+
type: "application/activity+json",
|
|
1564
|
+
href: "https://localhost/actor"
|
|
1565
|
+
}]
|
|
1566
|
+
});
|
|
1567
|
+
});
|
|
1568
|
+
esm_default.removeRoutes();
|
|
1569
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", {
|
|
1570
|
+
status: 302,
|
|
1571
|
+
headers: { Location: "/.well-known/webfinger2" }
|
|
1572
|
+
});
|
|
1573
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger2", { body: expected });
|
|
1574
|
+
await t.step("redirection", async () => {
|
|
1575
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com"), expected);
|
|
1576
|
+
});
|
|
1577
|
+
esm_default.removeRoutes();
|
|
1578
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", {
|
|
1579
|
+
status: 302,
|
|
1580
|
+
headers: { Location: "/.well-known/webfinger" }
|
|
1581
|
+
});
|
|
1582
|
+
await t.step("infinite redirection", async () => {
|
|
1583
|
+
deepStrictEqual(await withTimeout(() => lookupWebFinger("acct:johndoe@example.com"), 2e3), null);
|
|
1584
|
+
});
|
|
1585
|
+
esm_default.removeRoutes();
|
|
1586
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", {
|
|
1587
|
+
status: 302,
|
|
1588
|
+
headers: { Location: "ftp://example.com/" }
|
|
1589
|
+
});
|
|
1590
|
+
await t.step("redirection to different protocol", async () => {
|
|
1591
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com"), null);
|
|
1592
|
+
});
|
|
1593
|
+
esm_default.removeRoutes();
|
|
1594
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", {
|
|
1595
|
+
status: 302,
|
|
1596
|
+
headers: { Location: "https://localhost/" }
|
|
1597
|
+
});
|
|
1598
|
+
await t.step("redirection to private address", async () => {
|
|
1599
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com"), null);
|
|
1600
|
+
});
|
|
1601
|
+
esm_default.removeRoutes();
|
|
1602
|
+
let redirectCount = 0;
|
|
1603
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger", () => {
|
|
1604
|
+
redirectCount++;
|
|
1605
|
+
if (redirectCount < 3) return {
|
|
1606
|
+
status: 302,
|
|
1607
|
+
headers: { Location: `/.well-known/webfinger?redirect=${redirectCount}` }
|
|
1608
|
+
};
|
|
1609
|
+
return { body: expected };
|
|
1610
|
+
});
|
|
1611
|
+
await t.step("custom maxRedirection", async () => {
|
|
1612
|
+
redirectCount = 0;
|
|
1613
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { maxRedirection: 1 }), null);
|
|
1614
|
+
redirectCount = 0;
|
|
1615
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { maxRedirection: 2 }), expected);
|
|
1616
|
+
redirectCount = 0;
|
|
1617
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { maxRedirection: 3 }), expected);
|
|
1618
|
+
redirectCount = 0;
|
|
1619
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com"), expected);
|
|
1620
|
+
});
|
|
1621
|
+
await t.step("maxRedirection: 1 follows exactly one redirect", async () => {
|
|
1622
|
+
esm_default.removeRoutes();
|
|
1623
|
+
let count = 0;
|
|
1624
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger", () => {
|
|
1625
|
+
count++;
|
|
1626
|
+
return count < 2 ? {
|
|
1627
|
+
status: 302,
|
|
1628
|
+
headers: { Location: "/.well-known/webfinger?after=1" }
|
|
1629
|
+
} : { body: expected };
|
|
1630
|
+
});
|
|
1631
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { maxRedirection: 1 }), expected);
|
|
1632
|
+
esm_default.removeRoutes();
|
|
1633
|
+
count = 0;
|
|
1634
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger", () => {
|
|
1635
|
+
count++;
|
|
1636
|
+
return count < 3 ? {
|
|
1637
|
+
status: 302,
|
|
1638
|
+
headers: { Location: `/.well-known/webfinger?after=${count}` }
|
|
1639
|
+
} : { body: expected };
|
|
1640
|
+
});
|
|
1641
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { maxRedirection: 1 }), null);
|
|
1642
|
+
});
|
|
1643
|
+
esm_default.removeRoutes();
|
|
1644
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", () => new Promise((resolve) => {
|
|
1645
|
+
const timeoutId = setTimeout(() => {
|
|
1646
|
+
resolve({ body: expected });
|
|
1647
|
+
}, 1e3);
|
|
1648
|
+
return () => clearTimeout(timeoutId);
|
|
1649
|
+
}));
|
|
1650
|
+
await t.step("request cancellation", async () => {
|
|
1651
|
+
const controller = new AbortController();
|
|
1652
|
+
const promise = lookupWebFinger("acct:johndoe@example.com", { signal: controller.signal });
|
|
1653
|
+
controller.abort();
|
|
1654
|
+
deepStrictEqual(await promise, null);
|
|
1655
|
+
});
|
|
1656
|
+
esm_default.removeRoutes();
|
|
1657
|
+
let redirectCount2 = 0;
|
|
1658
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger", () => {
|
|
1659
|
+
redirectCount2++;
|
|
1660
|
+
if (redirectCount2 === 1) return {
|
|
1661
|
+
status: 302,
|
|
1662
|
+
headers: { Location: "/.well-known/webfinger2" }
|
|
1663
|
+
};
|
|
1664
|
+
return new Promise((resolve) => {
|
|
1665
|
+
const timeoutId = setTimeout(() => {
|
|
1666
|
+
resolve({ body: expected });
|
|
1667
|
+
}, 1e3);
|
|
1668
|
+
return () => clearTimeout(timeoutId);
|
|
1669
|
+
});
|
|
1670
|
+
});
|
|
1671
|
+
await t.step("cancellation during redirection", async () => {
|
|
1672
|
+
const controller = new AbortController();
|
|
1673
|
+
const promise = lookupWebFinger("acct:johndoe@example.com", { signal: controller.signal });
|
|
1674
|
+
setTimeout(() => controller.abort(), 100);
|
|
1675
|
+
deepStrictEqual(await promise, null);
|
|
1676
|
+
});
|
|
1677
|
+
esm_default.removeRoutes();
|
|
1678
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", () => new Promise((resolve) => {
|
|
1679
|
+
const timeoutId = setTimeout(() => {
|
|
1680
|
+
resolve({ body: expected });
|
|
1681
|
+
}, 500);
|
|
1682
|
+
return () => clearTimeout(timeoutId);
|
|
1683
|
+
}));
|
|
1684
|
+
await t.step("cancellation with immediate abort", async () => {
|
|
1685
|
+
const controller = new AbortController();
|
|
1686
|
+
controller.abort();
|
|
1687
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { signal: controller.signal }), null);
|
|
1688
|
+
});
|
|
1689
|
+
esm_default.removeRoutes();
|
|
1690
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", { body: expected });
|
|
1691
|
+
await t.step("successful request with signal", async () => {
|
|
1692
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { signal: new AbortController().signal }), expected);
|
|
1693
|
+
});
|
|
1694
|
+
} finally {
|
|
1695
|
+
esm_default.removeRoutes();
|
|
1696
|
+
esm_default.hardReset();
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
});
|
|
1700
|
+
test("lookupWebFinger() records webfinger.lookup counter and duration", {
|
|
1701
|
+
sanitizeOps: false,
|
|
1702
|
+
sanitizeResources: false
|
|
1703
|
+
}, async (t) => {
|
|
1704
|
+
esm_default.spyGlobal();
|
|
1705
|
+
try {
|
|
1706
|
+
const expected = {
|
|
1707
|
+
subject: "acct:johndoe@example.com",
|
|
1708
|
+
links: []
|
|
1709
|
+
};
|
|
1710
|
+
await t.step("records result=found for a successful acct lookup", async () => {
|
|
1711
|
+
esm_default.removeRoutes();
|
|
1712
|
+
esm_default.get("https://example.com/.well-known/webfinger?resource=acct%3Ajohndoe%40example.com", { body: expected });
|
|
1713
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1714
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { meterProvider }), expected);
|
|
1715
|
+
const counters = recorder.getMeasurements("webfinger.lookup");
|
|
1716
|
+
deepStrictEqual(counters.length, 1);
|
|
1717
|
+
deepStrictEqual(counters[0].type, "counter");
|
|
1718
|
+
deepStrictEqual(counters[0].value, 1);
|
|
1719
|
+
deepStrictEqual(counters[0].attributes["webfinger.lookup.result"], "found");
|
|
1720
|
+
deepStrictEqual(counters[0].attributes["webfinger.resource.scheme"], "acct");
|
|
1721
|
+
deepStrictEqual(counters[0].attributes["activitypub.remote.host"], "example.com");
|
|
1722
|
+
deepStrictEqual(counters[0].attributes["http.response.status_code"], 200);
|
|
1723
|
+
const durations = recorder.getMeasurements("webfinger.lookup.duration");
|
|
1724
|
+
deepStrictEqual(durations.length, 1);
|
|
1725
|
+
deepStrictEqual(durations[0].type, "histogram");
|
|
1726
|
+
deepStrictEqual(durations[0].attributes["webfinger.lookup.result"], "found");
|
|
1727
|
+
deepStrictEqual(durations[0].attributes["webfinger.resource.scheme"], "acct");
|
|
1728
|
+
ok(typeof durations[0].value === "number" && durations[0].value >= 0);
|
|
1415
1729
|
});
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1730
|
+
await t.step("records scheme=https for an https resource lookup", async () => {
|
|
1731
|
+
esm_default.removeRoutes();
|
|
1732
|
+
esm_default.get("https://example.com/.well-known/webfinger?resource=https%3A%2F%2Fexample.com%2Ffoo", { body: {
|
|
1733
|
+
subject: "https://example.com/foo",
|
|
1734
|
+
links: []
|
|
1735
|
+
} });
|
|
1736
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1737
|
+
await lookupWebFinger("https://example.com/foo", { meterProvider });
|
|
1738
|
+
const counters = recorder.getMeasurements("webfinger.lookup");
|
|
1739
|
+
deepStrictEqual(counters.length, 1);
|
|
1740
|
+
deepStrictEqual(counters[0].attributes["webfinger.resource.scheme"], "https");
|
|
1741
|
+
deepStrictEqual(counters[0].attributes["webfinger.lookup.result"], "found");
|
|
1420
1742
|
});
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1743
|
+
await t.step("records non-default ports for URL resources", async () => {
|
|
1744
|
+
esm_default.removeRoutes();
|
|
1745
|
+
esm_default.get("https://example.com:8443/.well-known/webfinger?resource=https%3A%2F%2Fexample.com%3A8443%2Ffoo", { body: {
|
|
1746
|
+
subject: "https://example.com:8443/foo",
|
|
1747
|
+
links: []
|
|
1748
|
+
} });
|
|
1749
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1750
|
+
await lookupWebFinger("https://example.com:8443/foo", { meterProvider });
|
|
1751
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1752
|
+
ok(counter != null);
|
|
1753
|
+
deepStrictEqual(counter.attributes["activitypub.remote.host"], "example.com:8443");
|
|
1424
1754
|
});
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
status:
|
|
1428
|
-
|
|
1755
|
+
await t.step("records result=not_found with status 404", async () => {
|
|
1756
|
+
esm_default.removeRoutes();
|
|
1757
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", { status: 404 });
|
|
1758
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1759
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { meterProvider }), null);
|
|
1760
|
+
const counters = recorder.getMeasurements("webfinger.lookup");
|
|
1761
|
+
deepStrictEqual(counters.length, 1);
|
|
1762
|
+
deepStrictEqual(counters[0].attributes["webfinger.lookup.result"], "not_found");
|
|
1763
|
+
deepStrictEqual(counters[0].attributes["http.response.status_code"], 404);
|
|
1764
|
+
deepStrictEqual(counters[0].attributes["activitypub.remote.host"], "example.com");
|
|
1765
|
+
const durations = recorder.getMeasurements("webfinger.lookup.duration");
|
|
1766
|
+
deepStrictEqual(durations.length, 1);
|
|
1767
|
+
deepStrictEqual(durations[0].attributes["webfinger.lookup.result"], "not_found");
|
|
1429
1768
|
});
|
|
1430
|
-
await t.step("
|
|
1431
|
-
|
|
1769
|
+
await t.step("records result=not_found with status 410", async () => {
|
|
1770
|
+
esm_default.removeRoutes();
|
|
1771
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", { status: 410 });
|
|
1772
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1773
|
+
await lookupWebFinger("acct:johndoe@example.com", { meterProvider });
|
|
1774
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1775
|
+
ok(counter != null);
|
|
1776
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "not_found");
|
|
1777
|
+
deepStrictEqual(counter.attributes["http.response.status_code"], 410);
|
|
1432
1778
|
});
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
status:
|
|
1436
|
-
|
|
1779
|
+
await t.step("records result=error for non-2xx, non-404/410 HTTP responses", async () => {
|
|
1780
|
+
esm_default.removeRoutes();
|
|
1781
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", { status: 500 });
|
|
1782
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1783
|
+
await lookupWebFinger("acct:johndoe@example.com", { meterProvider });
|
|
1784
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1785
|
+
ok(counter != null);
|
|
1786
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "error");
|
|
1787
|
+
deepStrictEqual(counter.attributes["http.response.status_code"], 500);
|
|
1437
1788
|
});
|
|
1438
|
-
await t.step("
|
|
1439
|
-
|
|
1789
|
+
await t.step("records result=invalid for malformed JSON bodies", async () => {
|
|
1790
|
+
esm_default.removeRoutes();
|
|
1791
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", { body: "not json" });
|
|
1792
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1793
|
+
await lookupWebFinger("acct:johndoe@example.com", { meterProvider });
|
|
1794
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1795
|
+
ok(counter != null);
|
|
1796
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "invalid");
|
|
1797
|
+
deepStrictEqual(counter.attributes["http.response.status_code"], 200);
|
|
1440
1798
|
});
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1799
|
+
await t.step("records result=network_error when fetch never reaches the remote", async () => {
|
|
1800
|
+
esm_default.removeRoutes();
|
|
1801
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1802
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe@fedify-test.internal", { meterProvider }), null);
|
|
1803
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1804
|
+
ok(counter != null);
|
|
1805
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "network_error");
|
|
1806
|
+
deepStrictEqual("http.response.status_code" in counter.attributes, false, "no HTTP response means no status code attribute");
|
|
1807
|
+
deepStrictEqual(counter.attributes["activitypub.remote.host"], "fedify-test.internal");
|
|
1445
1808
|
});
|
|
1446
|
-
await t.step("
|
|
1447
|
-
|
|
1809
|
+
await t.step("records result=invalid for malformed acct: resources", async () => {
|
|
1810
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1811
|
+
deepStrictEqual(await lookupWebFinger("acct:johndoe", { meterProvider }), null);
|
|
1812
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1813
|
+
ok(counter != null);
|
|
1814
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "invalid");
|
|
1815
|
+
deepStrictEqual(counter.attributes["webfinger.resource.scheme"], "acct");
|
|
1816
|
+
deepStrictEqual("activitypub.remote.host" in counter.attributes, false, "a malformed acct resource has no usable remote host");
|
|
1448
1817
|
});
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
redirectCount++;
|
|
1453
|
-
if (redirectCount < 3) return {
|
|
1818
|
+
await t.step("records result=invalid when the redirect chain exceeds maxRedirection", async () => {
|
|
1819
|
+
esm_default.removeRoutes();
|
|
1820
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger", {
|
|
1454
1821
|
status: 302,
|
|
1455
|
-
headers: { Location:
|
|
1456
|
-
};
|
|
1457
|
-
|
|
1822
|
+
headers: { Location: "/.well-known/webfinger" }
|
|
1823
|
+
});
|
|
1824
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1825
|
+
deepStrictEqual(await withTimeout(() => lookupWebFinger("acct:johndoe@example.com", {
|
|
1826
|
+
meterProvider,
|
|
1827
|
+
maxRedirection: 3
|
|
1828
|
+
}), 2e3), null);
|
|
1829
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1830
|
+
ok(counter != null);
|
|
1831
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "invalid");
|
|
1832
|
+
deepStrictEqual(counter.attributes["http.response.status_code"], 302);
|
|
1833
|
+
deepStrictEqual(counter.attributes["activitypub.remote.host"], "example.com");
|
|
1458
1834
|
});
|
|
1459
|
-
await t.step("
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1835
|
+
await t.step("records result=invalid for cross-protocol redirects", async () => {
|
|
1836
|
+
esm_default.removeRoutes();
|
|
1837
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", {
|
|
1838
|
+
status: 302,
|
|
1839
|
+
headers: { Location: "ftp://example.com/" }
|
|
1840
|
+
});
|
|
1841
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1842
|
+
await lookupWebFinger("acct:johndoe@example.com", { meterProvider });
|
|
1843
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1844
|
+
ok(counter != null);
|
|
1845
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "invalid");
|
|
1846
|
+
deepStrictEqual(counter.attributes["http.response.status_code"], 302);
|
|
1466
1847
|
});
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
const
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
deepStrictEqual(
|
|
1848
|
+
await t.step("records result=network_error when a redirect points to a private address", async () => {
|
|
1849
|
+
esm_default.removeRoutes();
|
|
1850
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", {
|
|
1851
|
+
status: 302,
|
|
1852
|
+
headers: { Location: "https://localhost/" }
|
|
1853
|
+
});
|
|
1854
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1855
|
+
await lookupWebFinger("acct:johndoe@example.com", { meterProvider });
|
|
1856
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1857
|
+
ok(counter != null);
|
|
1858
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "network_error");
|
|
1859
|
+
deepStrictEqual(counter.attributes["activitypub.remote.host"], "localhost", "remote.host reflects the latest URL we attempted, even after a redirect");
|
|
1479
1860
|
});
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
redirectCount2++;
|
|
1484
|
-
if (redirectCount2 === 1) return {
|
|
1861
|
+
await t.step("records result=invalid for malformed Location headers", async () => {
|
|
1862
|
+
esm_default.removeRoutes();
|
|
1863
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger?", {
|
|
1485
1864
|
status: 302,
|
|
1486
|
-
headers: { Location: "
|
|
1487
|
-
};
|
|
1488
|
-
return new Promise((resolve) => {
|
|
1489
|
-
const timeoutId = setTimeout(() => {
|
|
1490
|
-
resolve({ body: expected });
|
|
1491
|
-
}, 1e3);
|
|
1492
|
-
return () => clearTimeout(timeoutId);
|
|
1865
|
+
headers: { Location: "http://[bad" }
|
|
1493
1866
|
});
|
|
1867
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1868
|
+
await lookupWebFinger("acct:johndoe@example.com", { meterProvider });
|
|
1869
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1870
|
+
ok(counter != null);
|
|
1871
|
+
deepStrictEqual(counter.attributes["webfinger.lookup.result"], "invalid");
|
|
1872
|
+
deepStrictEqual(counter.attributes["http.response.status_code"], 302);
|
|
1873
|
+
deepStrictEqual(counter.attributes["activitypub.remote.host"], "example.com");
|
|
1494
1874
|
});
|
|
1495
|
-
await t.step("
|
|
1496
|
-
|
|
1497
|
-
const
|
|
1498
|
-
|
|
1499
|
-
|
|
1875
|
+
await t.step("buckets unknown resource schemes as 'other' to keep metric cardinality bounded", async () => {
|
|
1876
|
+
esm_default.removeRoutes();
|
|
1877
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
1878
|
+
await lookupWebFinger("ssh://example.com/foo", { meterProvider });
|
|
1879
|
+
const counter = recorder.getMeasurement("webfinger.lookup");
|
|
1880
|
+
ok(counter != null);
|
|
1881
|
+
deepStrictEqual(counter.attributes["webfinger.resource.scheme"], "other");
|
|
1500
1882
|
});
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
await t.step("cancellation with immediate abort", async () => {
|
|
1509
|
-
const controller = new AbortController();
|
|
1510
|
-
controller.abort();
|
|
1511
|
-
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { signal: controller.signal }), null);
|
|
1883
|
+
await t.step("omits measurements when no meterProvider is provided", async () => {
|
|
1884
|
+
esm_default.removeRoutes();
|
|
1885
|
+
esm_default.get("https://example.com/.well-known/webfinger?resource=acct%3Ajohndoe%40example.com", { body: expected });
|
|
1886
|
+
const [_unused, recorder] = createTestMeterProvider();
|
|
1887
|
+
await lookupWebFinger("acct:johndoe@example.com");
|
|
1888
|
+
deepStrictEqual(recorder.getMeasurements("webfinger.lookup").length, 0);
|
|
1889
|
+
deepStrictEqual(recorder.getMeasurements("webfinger.lookup.duration").length, 0);
|
|
1512
1890
|
});
|
|
1891
|
+
} finally {
|
|
1513
1892
|
esm_default.removeRoutes();
|
|
1514
|
-
esm_default.get("begin:https://example.com/.well-known/webfinger?", { body: expected });
|
|
1515
|
-
await t.step("successful request with signal", async () => {
|
|
1516
|
-
deepStrictEqual(await lookupWebFinger("acct:johndoe@example.com", { signal: new AbortController().signal }), expected);
|
|
1517
|
-
});
|
|
1518
1893
|
esm_default.hardReset();
|
|
1519
1894
|
}
|
|
1520
1895
|
});
|