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