@promigence/sdk 0.1.0-beta.12 → 0.1.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/caller.js +10 -0
- package/dist/cjs/caller.js +10 -0
- package/dist/cjs/client.d.ts +16 -6
- package/dist/cjs/client.js +1180 -1129
- package/dist/cjs/core.d.ts +1 -1
- package/dist/cjs/core.js +1 -1
- package/dist/cjs/dispatcher.d.ts +2 -0
- package/dist/cjs/dispatcher.js +5 -0
- package/dist/cjs/http2.d.ts +11 -0
- package/dist/cjs/http2.js +335 -71
- package/dist/cjs/keyed.d.ts +6 -0
- package/dist/cjs/keyed.js +59 -0
- package/dist/cjs/proxy.js +11 -0
- package/dist/cjs/sandbox.d.ts +3 -1
- package/dist/cjs/sandbox.js +33 -20
- package/dist/cjs/trace.d.ts +2 -0
- package/dist/cjs/trace.js +17 -0
- package/dist/cjs/transport-worker.js +83 -14
- package/dist/client.d.ts +16 -6
- package/dist/client.js +1179 -1127
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/dispatcher.d.ts +2 -0
- package/dist/dispatcher.js +5 -0
- package/dist/http2.d.ts +11 -0
- package/dist/http2.js +335 -72
- package/dist/keyed.d.ts +6 -0
- package/dist/keyed.js +55 -0
- package/dist/proxy.js +11 -0
- package/dist/sandbox.d.ts +3 -1
- package/dist/sandbox.js +33 -20
- package/dist/trace.d.ts +2 -0
- package/dist/trace.js +16 -0
- package/dist/transport-worker.js +83 -14
- package/package.json +1 -1
package/dist/cjs/http2.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.warmTls = warmTls;
|
|
|
6
6
|
exports.parseSessions = parseSessions;
|
|
7
7
|
exports.extraOrders = extraOrders;
|
|
8
8
|
exports.lookupApiName = lookupApiName;
|
|
9
|
+
exports.sharedTransport = sharedTransport;
|
|
9
10
|
exports.warmTransport = warmTransport;
|
|
10
11
|
const http2 = require("node:http2");
|
|
11
12
|
const node_net_1 = require("node:net");
|
|
@@ -20,6 +21,7 @@ const MAX_SESSIONS = 4;
|
|
|
20
21
|
const FIRST_SESSIONS = 2;
|
|
21
22
|
const BURST_MIN = 8;
|
|
22
23
|
const HOLD_MS = 10;
|
|
24
|
+
const STANDBY_AFTER_MS = 300;
|
|
23
25
|
const LOAD_FIRST_USE_MS = 120_000;
|
|
24
26
|
const CONNECT_TIMEOUT_MS = 10_000;
|
|
25
27
|
const IDLE_CLOSE_MS = 10_000;
|
|
@@ -571,6 +573,7 @@ exports.tuning = {
|
|
|
571
573
|
holdMs: HOLD_MS,
|
|
572
574
|
burstMin: BURST_MIN,
|
|
573
575
|
loadFirstUseMs: LOAD_FIRST_USE_MS,
|
|
576
|
+
standbyAfterMs: STANDBY_AFTER_MS,
|
|
574
577
|
earlyPlatform: undefined,
|
|
575
578
|
earlyServers: undefined,
|
|
576
579
|
earlyHostsFile: undefined,
|
|
@@ -854,19 +857,21 @@ function apiLookup(asked, observe, ipv4First = v4First(), earlyOk = true) {
|
|
|
854
857
|
const earlies = new Map();
|
|
855
858
|
const MAX_EARLIES = 4;
|
|
856
859
|
const EARLY_MAX_AGE_MS = 60_000;
|
|
857
|
-
const
|
|
858
|
-
const
|
|
860
|
+
const LOOKUP_FIRST_MS = 150;
|
|
861
|
+
const LOOKUP_TRIES = 3;
|
|
862
|
+
const LOOKUP_AT_MS = [0, 100, 250];
|
|
863
|
+
const DNS_AT_LOAD = (() => {
|
|
859
864
|
try {
|
|
860
|
-
return nodeDns()
|
|
865
|
+
return nodeDns();
|
|
861
866
|
}
|
|
862
867
|
catch {
|
|
863
868
|
return undefined;
|
|
864
869
|
}
|
|
865
870
|
})();
|
|
871
|
+
const LOOKUP_AT_LOAD = DNS_AT_LOAD?.lookup;
|
|
866
872
|
function lookupReplaced() {
|
|
867
873
|
try {
|
|
868
|
-
|
|
869
|
-
return d !== undefined && d.lookup !== LOOKUP_AT_LOAD;
|
|
874
|
+
return DNS_AT_LOAD !== undefined && DNS_AT_LOAD.lookup !== LOOKUP_AT_LOAD;
|
|
870
875
|
}
|
|
871
876
|
catch {
|
|
872
877
|
return true;
|
|
@@ -875,8 +880,8 @@ function lookupReplaced() {
|
|
|
875
880
|
const EARLY_WORKER = `
|
|
876
881
|
const { workerData } = require("node:worker_threads");
|
|
877
882
|
const { isIPv4 } = require("node:net");
|
|
878
|
-
const { port, host, timeoutMs, servers, hostsFile } = workerData;
|
|
879
|
-
const done = (m) => { try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
883
|
+
const { port, host, timeoutMs, tries, atMs, servers, hostsFile } = workerData;
|
|
884
|
+
const done = (m) => { m.hr = Number(process.hrtime.bigint()) / 1e6; try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
880
885
|
try {
|
|
881
886
|
let named = false;
|
|
882
887
|
const v4 = [];
|
|
@@ -890,12 +895,28 @@ try {
|
|
|
890
895
|
} catch {}
|
|
891
896
|
if (named) done(v4.length > 0 ? { host, addrs: v4, ttl: 60, at: Date.now() } : { host, none: "hosts" });
|
|
892
897
|
else {
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
});
|
|
898
|
+
const { Resolver } = require("node:dns");
|
|
899
|
+
const rs = [];
|
|
900
|
+
const timers = [];
|
|
901
|
+
let fin = false, asked = 0, failed = 0;
|
|
902
|
+
const finish = (m) => { if (fin) return; fin = true; for (const t of timers) clearTimeout(t); for (const r of rs) { try { r.cancel(); } catch {} } done(m); };
|
|
903
|
+
const gaveUp = (why) => { if (++failed < asked) return; if (asked < atMs.length) ask(); else finish({ host, none: why }); };
|
|
904
|
+
const ask = () => {
|
|
905
|
+
if (fin || asked >= atMs.length) return;
|
|
906
|
+
const k = asked++;
|
|
907
|
+
try {
|
|
908
|
+
const r = new Resolver({ timeout: timeoutMs, tries });
|
|
909
|
+
const list = Array.isArray(servers) && servers.length > 0 ? servers : r.getServers();
|
|
910
|
+
if (list.length > 0) r.setServers(list.map((_, i) => list[(i + k) % list.length]));
|
|
911
|
+
rs.push(r);
|
|
912
|
+
r.resolve4(host, { ttl: true }, (err, recs) => {
|
|
913
|
+
if (fin) return;
|
|
914
|
+
if (!err && Array.isArray(recs) && recs.length > 0) return finish({ host, addrs: recs.map((x) => x.address), ttl: Math.min(...recs.map((x) => x.ttl)), at: Date.now() });
|
|
915
|
+
gaveUp((err && err.code) || "empty");
|
|
916
|
+
});
|
|
917
|
+
} catch { gaveUp("error"); }
|
|
918
|
+
};
|
|
919
|
+
for (const ms of atMs) { if (ms <= 0) ask(); else timers.push(setTimeout(ask, ms)); }
|
|
899
920
|
}
|
|
900
921
|
} catch { done({ host, none: "error" }); }
|
|
901
922
|
`;
|
|
@@ -926,7 +947,7 @@ function earlyLookupFor(host) {
|
|
|
926
947
|
const worker = new node_worker_threads_1.Worker(EARLY_WORKER, {
|
|
927
948
|
eval: true,
|
|
928
949
|
workerData: {
|
|
929
|
-
port: port2, host, timeoutMs:
|
|
950
|
+
port: port2, host, timeoutMs: LOOKUP_FIRST_MS, tries: LOOKUP_TRIES, atMs: LOOKUP_AT_MS,
|
|
930
951
|
servers: exports.tuning.earlyServers ?? (Array.isArray(earlySeam().servers) ? earlySeam().servers : undefined),
|
|
931
952
|
hostsFile: exports.tuning.earlyHostsFile ?? (typeof earlySeam().hostsFile === "string" ? earlySeam().hostsFile : undefined),
|
|
932
953
|
},
|
|
@@ -974,6 +995,10 @@ function settleEarly(e, message) {
|
|
|
974
995
|
else {
|
|
975
996
|
e.state = "none";
|
|
976
997
|
}
|
|
998
|
+
if (trace_js_1.TRACE_ON) {
|
|
999
|
+
const at = typeof m?.hr === "number" ? m.hr - (Number(process.hrtime.bigint()) / 1e6 - performance.now()) : performance.now();
|
|
1000
|
+
(0, trace_js_1.traceEventAt)(e.state === "answered" ? `dns:early-answer ${addrs.length}` : `dns:early-none ${typeof m?.none === "string" ? m.none : "gone"}`, at);
|
|
1001
|
+
}
|
|
977
1002
|
safely(() => { e.port.close(); });
|
|
978
1003
|
const waiters = e.waiters.splice(0);
|
|
979
1004
|
for (const w of waiters)
|
|
@@ -1300,12 +1325,13 @@ function targetOf(url, known, remember) {
|
|
|
1300
1325
|
}
|
|
1301
1326
|
function fetchInit(init, deadline) {
|
|
1302
1327
|
const timeout = AbortSignal.timeout(Math.max(0, Math.ceil(deadline - performance.now())));
|
|
1328
|
+
const dispatcher = init.dispatcher ?? init.dispatcherFor?.(init.timeoutMs);
|
|
1303
1329
|
return {
|
|
1304
1330
|
method: init.method,
|
|
1305
1331
|
headers: init.headers,
|
|
1306
1332
|
signal: init.signal === undefined ? timeout : AbortSignal.any([timeout, init.signal]),
|
|
1307
1333
|
...(init.body === undefined ? {} : { body: init.body }),
|
|
1308
|
-
...(
|
|
1334
|
+
...(dispatcher === undefined ? {} : { dispatcher }),
|
|
1309
1335
|
};
|
|
1310
1336
|
}
|
|
1311
1337
|
function fetchFailed(cause) {
|
|
@@ -1322,27 +1348,45 @@ class HandBack {
|
|
|
1322
1348
|
const UNSENT = new HandBack(0, 0);
|
|
1323
1349
|
const VIA_FETCH = Symbol("via-fetch");
|
|
1324
1350
|
const THREAD_GRACE_MS = 1_000;
|
|
1351
|
+
const THREAD_SWEEP_MS = 250;
|
|
1352
|
+
const THREAD_MARKS = 4096;
|
|
1325
1353
|
const THREAD_PHASES = ["w.got", "w.sent", "w.head", "w.end"];
|
|
1326
1354
|
class TransportThread {
|
|
1327
1355
|
origin;
|
|
1328
1356
|
worker;
|
|
1329
1357
|
port;
|
|
1358
|
+
acks;
|
|
1330
1359
|
state = "starting";
|
|
1331
1360
|
sessions = 0;
|
|
1332
1361
|
limit = DEFAULT_STREAM_LIMIT;
|
|
1333
1362
|
everReady = false;
|
|
1334
1363
|
closedReason = "";
|
|
1335
1364
|
onFail;
|
|
1365
|
+
startedAt = performance.now();
|
|
1366
|
+
startWaiters = [];
|
|
1336
1367
|
inflight = new Map();
|
|
1337
1368
|
nextId = 1;
|
|
1338
1369
|
listening = false;
|
|
1370
|
+
refd = false;
|
|
1371
|
+
sweep;
|
|
1372
|
+
freeMarks = [];
|
|
1373
|
+
messages = 0;
|
|
1374
|
+
outbox = [];
|
|
1375
|
+
turnOpen = false;
|
|
1376
|
+
posts = 0;
|
|
1339
1377
|
answered = 0;
|
|
1340
1378
|
pace = THREAD_DEFAULTS.pace;
|
|
1341
1379
|
hrOffset = Number(process.hrtime.bigint()) / 1e6 - performance.now();
|
|
1342
|
-
constructor(origin, worker, port) {
|
|
1380
|
+
constructor(origin, worker, port, acks) {
|
|
1343
1381
|
this.origin = origin;
|
|
1344
1382
|
this.worker = worker;
|
|
1345
1383
|
this.port = port;
|
|
1384
|
+
this.acks = acks;
|
|
1385
|
+
for (let i = acks.length - 1; i >= 0; i--)
|
|
1386
|
+
this.freeMarks.push(i);
|
|
1387
|
+
}
|
|
1388
|
+
acked(p) {
|
|
1389
|
+
return Atomics.load(this.acks, p.mark) === p.id;
|
|
1346
1390
|
}
|
|
1347
1391
|
poll() {
|
|
1348
1392
|
for (;;) {
|
|
@@ -1359,12 +1403,44 @@ class TransportThread {
|
|
|
1359
1403
|
}
|
|
1360
1404
|
}
|
|
1361
1405
|
ready() {
|
|
1362
|
-
if (this.state !== "closed")
|
|
1406
|
+
if ((!this.listening || this.state === "starting") && this.state !== "closed")
|
|
1363
1407
|
this.poll();
|
|
1364
|
-
return this.state === "ready" && this.sessions > 0 && this.inflight.size < this.sessions * this.limit;
|
|
1408
|
+
return this.state === "ready" && this.sessions > 0 && this.inflight.size < this.sessions * this.limit && this.freeMarks.length > 0;
|
|
1409
|
+
}
|
|
1410
|
+
starting() {
|
|
1411
|
+
if (this.state === "starting")
|
|
1412
|
+
this.poll();
|
|
1413
|
+
return this.state === "starting";
|
|
1414
|
+
}
|
|
1415
|
+
whenStarted(cb) {
|
|
1416
|
+
if (!this.starting()) {
|
|
1417
|
+
queueMicrotask(cb);
|
|
1418
|
+
return () => { };
|
|
1419
|
+
}
|
|
1420
|
+
this.startWaiters.push(cb);
|
|
1421
|
+
this.listen();
|
|
1422
|
+
return () => {
|
|
1423
|
+
const i = this.startWaiters.indexOf(cb);
|
|
1424
|
+
if (i >= 0)
|
|
1425
|
+
this.startWaiters.splice(i, 1);
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
started() {
|
|
1429
|
+
for (const cb of this.startWaiters.splice(0))
|
|
1430
|
+
safely(cb);
|
|
1431
|
+
}
|
|
1432
|
+
listen() {
|
|
1433
|
+
if (this.listening)
|
|
1434
|
+
return;
|
|
1435
|
+
this.listening = true;
|
|
1436
|
+
this.poll();
|
|
1437
|
+
this.port.on("message", (m) => { this.onMessage(m); });
|
|
1438
|
+
if (!this.refd)
|
|
1439
|
+
safely(() => { this.port.unref(); });
|
|
1365
1440
|
}
|
|
1366
1441
|
onMessage(m) {
|
|
1367
1442
|
const msg = m;
|
|
1443
|
+
this.messages++;
|
|
1368
1444
|
switch (msg?.t) {
|
|
1369
1445
|
case "ready":
|
|
1370
1446
|
if (this.state === "starting" || this.state === "ready") {
|
|
@@ -1384,8 +1460,16 @@ class TransportThread {
|
|
|
1384
1460
|
if (typeof c.index === "number" && typeof c.addr === "string" && typeof c.localPort === "number")
|
|
1385
1461
|
(0, trace_js_1.traceEventAt)(`thread:conn${c.index} ${c.addr} ${c.localPort}`, t);
|
|
1386
1462
|
}
|
|
1463
|
+
if (first)
|
|
1464
|
+
this.started();
|
|
1387
1465
|
}
|
|
1388
1466
|
return;
|
|
1467
|
+
case "ev": {
|
|
1468
|
+
const e = msg;
|
|
1469
|
+
if (typeof e.n === "string" && typeof e.hr === "number")
|
|
1470
|
+
(0, trace_js_1.traceEventAt)(`thread:w-${e.n}`, e.hr - this.hrOffset);
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1389
1473
|
case "sessions":
|
|
1390
1474
|
if (typeof msg.sessions === "number")
|
|
1391
1475
|
this.sessions = msg.sessions;
|
|
@@ -1396,17 +1480,12 @@ class TransportThread {
|
|
|
1396
1480
|
if (this.closedReason === "")
|
|
1397
1481
|
this.closedReason = msg.reason ?? "draining";
|
|
1398
1482
|
(0, trace_js_1.traceEvent)("thread:draining");
|
|
1483
|
+
this.started();
|
|
1399
1484
|
}
|
|
1400
1485
|
return;
|
|
1401
1486
|
case "closed":
|
|
1402
1487
|
this.close(msg.reason ?? "closed");
|
|
1403
1488
|
return;
|
|
1404
|
-
case "ack": {
|
|
1405
|
-
const p = this.inflight.get(msg.id ?? -1);
|
|
1406
|
-
if (p !== undefined)
|
|
1407
|
-
p.acked = true;
|
|
1408
|
-
return;
|
|
1409
|
-
}
|
|
1410
1489
|
case "res":
|
|
1411
1490
|
case "err":
|
|
1412
1491
|
this.settle(m);
|
|
@@ -1482,6 +1561,7 @@ class TransportThread {
|
|
|
1482
1561
|
this.port.postMessage({ t: "drain" });
|
|
1483
1562
|
}
|
|
1484
1563
|
catch { }
|
|
1564
|
+
this.started();
|
|
1485
1565
|
}
|
|
1486
1566
|
close(reason) {
|
|
1487
1567
|
if (this.state === "closed")
|
|
@@ -1490,10 +1570,13 @@ class TransportThread {
|
|
|
1490
1570
|
if (this.closedReason === "")
|
|
1491
1571
|
this.closedReason = reason;
|
|
1492
1572
|
(0, trace_js_1.traceEvent)("thread:closed");
|
|
1573
|
+
this.started();
|
|
1574
|
+
this.outbox = [];
|
|
1493
1575
|
for (const [id, p] of [...this.inflight]) {
|
|
1576
|
+
const sent = this.acked(p);
|
|
1494
1577
|
this.inflight.delete(id);
|
|
1495
1578
|
this.release(p);
|
|
1496
|
-
if (!
|
|
1579
|
+
if (!sent)
|
|
1497
1580
|
p.resolve(UNSENT);
|
|
1498
1581
|
else if (p.replayable)
|
|
1499
1582
|
p.resolve(new HandBack(0, 1));
|
|
@@ -1510,23 +1593,50 @@ class TransportThread {
|
|
|
1510
1593
|
}
|
|
1511
1594
|
}
|
|
1512
1595
|
ref() {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1596
|
+
this.listen();
|
|
1597
|
+
if (this.refd)
|
|
1598
|
+
return;
|
|
1599
|
+
this.refd = true;
|
|
1518
1600
|
safely(() => { this.port.ref(); });
|
|
1519
1601
|
safely(() => { this.worker.ref(); });
|
|
1602
|
+
this.sweep = setInterval(() => { this.sweepBackstops(); }, THREAD_SWEEP_MS);
|
|
1603
|
+
safely(() => { this.sweep?.unref?.(); });
|
|
1520
1604
|
}
|
|
1521
1605
|
unref() {
|
|
1606
|
+
if (this.sweep !== undefined) {
|
|
1607
|
+
clearInterval(this.sweep);
|
|
1608
|
+
this.sweep = undefined;
|
|
1609
|
+
}
|
|
1610
|
+
if (!this.refd)
|
|
1611
|
+
return;
|
|
1612
|
+
this.refd = false;
|
|
1522
1613
|
safely(() => { this.port.unref(); });
|
|
1523
1614
|
safely(() => { this.worker.unref(); });
|
|
1524
1615
|
}
|
|
1616
|
+
sweepBackstops() {
|
|
1617
|
+
this.poll();
|
|
1618
|
+
const now = performance.now();
|
|
1619
|
+
let late = false;
|
|
1620
|
+
for (const [id, p] of [...this.inflight]) {
|
|
1621
|
+
if (p.backstopAt > now)
|
|
1622
|
+
continue;
|
|
1623
|
+
late = true;
|
|
1624
|
+
this.inflight.delete(id);
|
|
1625
|
+
this.release(p);
|
|
1626
|
+
try {
|
|
1627
|
+
this.port.postMessage({ t: "cancel", id });
|
|
1628
|
+
}
|
|
1629
|
+
catch { }
|
|
1630
|
+
p.reject(timeoutReason());
|
|
1631
|
+
}
|
|
1632
|
+
if (late)
|
|
1633
|
+
this.close("no answer from the transport thread");
|
|
1634
|
+
}
|
|
1525
1635
|
release(p) {
|
|
1526
1636
|
if (p.signal !== undefined && p.onAbort !== undefined)
|
|
1527
1637
|
p.signal.removeEventListener("abort", p.onAbort);
|
|
1528
|
-
|
|
1529
|
-
|
|
1638
|
+
Atomics.store(this.acks, p.mark, 0);
|
|
1639
|
+
this.freeMarks.push(p.mark);
|
|
1530
1640
|
if (this.inflight.size === 0)
|
|
1531
1641
|
this.unref();
|
|
1532
1642
|
}
|
|
@@ -1551,8 +1661,11 @@ class TransportThread {
|
|
|
1551
1661
|
headers["content-length"] = Buffer.byteLength(init.body, "utf8");
|
|
1552
1662
|
const id = this.nextId++;
|
|
1553
1663
|
const tr = init.trace;
|
|
1664
|
+
const place = this.freeMarks.pop();
|
|
1665
|
+
if (place === undefined)
|
|
1666
|
+
return Promise.resolve(UNSENT);
|
|
1554
1667
|
return new Promise((resolve, reject) => {
|
|
1555
|
-
const p = { resolve, reject, method: init.method, replayable,
|
|
1668
|
+
const p = { id, resolve, reject, method: init.method, replayable, mark: place, backstopAt: performance.now() + remaining + THREAD_GRACE_MS, trace: tr };
|
|
1556
1669
|
const signal = init.signal;
|
|
1557
1670
|
if (signal !== undefined) {
|
|
1558
1671
|
p.signal = signal;
|
|
@@ -1561,47 +1674,59 @@ class TransportThread {
|
|
|
1561
1674
|
return;
|
|
1562
1675
|
this.inflight.delete(id);
|
|
1563
1676
|
this.release(p);
|
|
1564
|
-
|
|
1565
|
-
|
|
1677
|
+
const queued = this.outbox.findIndex((b) => b.id === id);
|
|
1678
|
+
if (queued >= 0)
|
|
1679
|
+
this.outbox.splice(queued, 1);
|
|
1680
|
+
else {
|
|
1681
|
+
try {
|
|
1682
|
+
this.port.postMessage({ t: "cancel", id });
|
|
1683
|
+
}
|
|
1684
|
+
catch { }
|
|
1566
1685
|
}
|
|
1567
|
-
catch { }
|
|
1568
1686
|
reject(signal.reason);
|
|
1569
1687
|
};
|
|
1570
1688
|
signal.addEventListener("abort", p.onAbort, { once: true });
|
|
1571
1689
|
}
|
|
1572
|
-
p.backstop = setTimeout(() => {
|
|
1573
|
-
this.poll();
|
|
1574
|
-
if (!this.inflight.has(id))
|
|
1575
|
-
return;
|
|
1576
|
-
this.inflight.delete(id);
|
|
1577
|
-
this.release(p);
|
|
1578
|
-
try {
|
|
1579
|
-
this.port.postMessage({ t: "cancel", id });
|
|
1580
|
-
}
|
|
1581
|
-
catch { }
|
|
1582
|
-
reject(timeoutReason());
|
|
1583
|
-
this.close("no answer from the transport thread");
|
|
1584
|
-
}, remaining + THREAD_GRACE_MS);
|
|
1585
|
-
safely(() => { p.backstop?.unref?.(); });
|
|
1586
1690
|
this.inflight.set(id, p);
|
|
1587
1691
|
this.ref();
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1692
|
+
const paced = this.pace === "all" || (this.pace === "creates" && init.method === "POST" && CREATE_PATH.test(target.path));
|
|
1693
|
+
this.outbox.push({ id, msg: { t: "req", id, mark: place, headers, sensitive: ["authorization"], body: init.body, timeoutMs: remaining, replayable, paced }, p, resolve, trace: tr });
|
|
1694
|
+
if (!this.turnOpen) {
|
|
1695
|
+
this.turnOpen = true;
|
|
1696
|
+
queueMicrotask(() => { this.turnOpen = false; this.handOver(); });
|
|
1697
|
+
this.handOver();
|
|
1591
1698
|
}
|
|
1592
|
-
|
|
1593
|
-
this.
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1699
|
+
else if (this.outbox.length >= OUTBOX_GROUP)
|
|
1700
|
+
this.handOver();
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
handOver() {
|
|
1704
|
+
if (this.outbox.length === 0)
|
|
1705
|
+
return;
|
|
1706
|
+
const batch = this.outbox;
|
|
1707
|
+
this.outbox = [];
|
|
1708
|
+
try {
|
|
1709
|
+
this.port.postMessage(batch.length === 1 ? batch[0].msg : { t: "reqs", list: batch.map((b) => b.msg) });
|
|
1710
|
+
this.posts++;
|
|
1711
|
+
}
|
|
1712
|
+
catch {
|
|
1713
|
+
for (const b of batch) {
|
|
1714
|
+
if (!this.inflight.has(b.id))
|
|
1715
|
+
continue;
|
|
1716
|
+
this.inflight.delete(b.id);
|
|
1717
|
+
this.release(b.p);
|
|
1718
|
+
b.resolve(UNSENT);
|
|
1597
1719
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
for (const b of batch)
|
|
1723
|
+
if (b.trace !== undefined) {
|
|
1724
|
+
(0, trace_js_1.mark)(b.trace, "sub");
|
|
1725
|
+
b.trace.via = "thread";
|
|
1601
1726
|
}
|
|
1602
|
-
});
|
|
1603
1727
|
}
|
|
1604
1728
|
}
|
|
1729
|
+
const OUTBOX_GROUP = 8;
|
|
1605
1730
|
let transportThread;
|
|
1606
1731
|
function threadFor(origin) {
|
|
1607
1732
|
const t = transportThread;
|
|
@@ -1609,6 +1734,55 @@ function threadFor(origin) {
|
|
|
1609
1734
|
return undefined;
|
|
1610
1735
|
return t.ready() && !lookupReplaced() ? t : undefined;
|
|
1611
1736
|
}
|
|
1737
|
+
function startingThread(origin) {
|
|
1738
|
+
const t = transportThread;
|
|
1739
|
+
if (t === undefined || t.origin !== origin || lookupReplaced())
|
|
1740
|
+
return undefined;
|
|
1741
|
+
return t.starting() ? t : undefined;
|
|
1742
|
+
}
|
|
1743
|
+
let raceTraced = false;
|
|
1744
|
+
function firstUp(t, pool, deadline, signal) {
|
|
1745
|
+
const first = !raceTraced;
|
|
1746
|
+
if (first) {
|
|
1747
|
+
raceTraced = true;
|
|
1748
|
+
(0, trace_js_1.traceEvent)("h2:race");
|
|
1749
|
+
}
|
|
1750
|
+
const stop = new AbortController();
|
|
1751
|
+
const both = signal === undefined ? stop.signal : AbortSignal.any([signal, stop.signal]);
|
|
1752
|
+
return new Promise((resolve, reject) => {
|
|
1753
|
+
let done = false;
|
|
1754
|
+
const forget = t.whenStarted(() => {
|
|
1755
|
+
if (done)
|
|
1756
|
+
return;
|
|
1757
|
+
const ready = threadFor(t.origin);
|
|
1758
|
+
if (ready === undefined)
|
|
1759
|
+
return;
|
|
1760
|
+
done = true;
|
|
1761
|
+
if (first)
|
|
1762
|
+
(0, trace_js_1.traceEvent)("h2:race-thread");
|
|
1763
|
+
stop.abort();
|
|
1764
|
+
resolve({ thread: ready });
|
|
1765
|
+
});
|
|
1766
|
+
pool.acquire(deadline, both).then((entry) => {
|
|
1767
|
+
if (done) {
|
|
1768
|
+
if (entry !== null)
|
|
1769
|
+
pool.end(entry);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
done = true;
|
|
1773
|
+
forget();
|
|
1774
|
+
if (first)
|
|
1775
|
+
(0, trace_js_1.traceEvent)("h2:race-pool");
|
|
1776
|
+
resolve({ entry });
|
|
1777
|
+
}, (err) => {
|
|
1778
|
+
if (done)
|
|
1779
|
+
return;
|
|
1780
|
+
done = true;
|
|
1781
|
+
forget();
|
|
1782
|
+
reject(err);
|
|
1783
|
+
});
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1612
1786
|
function ownFile() {
|
|
1613
1787
|
const frame = (new Error().stack ?? "").split("\n")[1] ?? "";
|
|
1614
1788
|
const m = /\((.*):\d+:\d+\)\s*$/.exec(frame) ?? /at (.*):\d+:\d+\s*$/.exec(frame);
|
|
@@ -1646,11 +1820,12 @@ function startTransportThread(origin, onFail) {
|
|
|
1646
1820
|
const knobs = threadKnobs();
|
|
1647
1821
|
const file = typeof seam.transportFile === "string" ? seam.transportFile : here.replace(/http2\.js$/, "transport-worker.js");
|
|
1648
1822
|
const { port1, port2 } = new node_worker_threads_1.MessageChannel();
|
|
1823
|
+
const acks = new Int32Array(new SharedArrayBuffer(4 * THREAD_MARKS));
|
|
1649
1824
|
const worker = new node_worker_threads_1.Worker(file.startsWith("file:") ? new URL(file) : file, {
|
|
1650
1825
|
workerData: {
|
|
1651
|
-
promigenceTransport: true, port: port2, origin: origin.origin, sessions: knobs.sessions, gapUs: knobs.gapUs,
|
|
1826
|
+
promigenceTransport: true, port: port2, acks, origin: origin.origin, sessions: knobs.sessions, gapUs: knobs.gapUs,
|
|
1652
1827
|
firstUseMs: exports.tuning.loadFirstUseMs, idleMs: IDLE_CLOSE_MS, connectTimeoutMs: CONNECT_TIMEOUT_MS,
|
|
1653
|
-
lookupTimeoutMs:
|
|
1828
|
+
lookupTimeoutMs: LOOKUP_FIRST_MS, lookupTries: LOOKUP_TRIES, lookupAtMs: LOOKUP_AT_MS, trace: trace_js_1.TRACE_ON,
|
|
1654
1829
|
servers: exports.tuning.earlyServers ?? (Array.isArray(seam.servers) ? seam.servers : undefined),
|
|
1655
1830
|
hostsFile: exports.tuning.earlyHostsFile ?? (typeof seam.hostsFile === "string" ? seam.hostsFile : undefined),
|
|
1656
1831
|
distrustedForTest: ca_guard_js_1.__caGuardInternals.testHashes(),
|
|
@@ -1661,13 +1836,18 @@ function startTransportThread(origin, onFail) {
|
|
|
1661
1836
|
});
|
|
1662
1837
|
worker.unref();
|
|
1663
1838
|
port1.unref();
|
|
1664
|
-
const t = new TransportThread(origin.origin, worker, port1);
|
|
1839
|
+
const t = new TransportThread(origin.origin, worker, port1, acks);
|
|
1665
1840
|
t.pace = knobs.pace;
|
|
1666
1841
|
t.onFail = onFail;
|
|
1667
1842
|
worker.on("error", () => { t.poll(); t.close("error"); });
|
|
1668
1843
|
worker.on("exit", () => { t.poll(); t.close("exit"); });
|
|
1669
1844
|
transportThread = t;
|
|
1670
1845
|
(0, trace_js_1.traceEvent)("thread:start");
|
|
1846
|
+
(0, trace_js_1.onTraceFlush)(() => {
|
|
1847
|
+
t.poll();
|
|
1848
|
+
for (const e of earlies.values())
|
|
1849
|
+
pollEarly(e);
|
|
1850
|
+
});
|
|
1671
1851
|
}
|
|
1672
1852
|
catch {
|
|
1673
1853
|
transportThread = undefined;
|
|
@@ -1683,6 +1863,18 @@ function knownPrefix(url) {
|
|
|
1683
1863
|
}
|
|
1684
1864
|
return undefined;
|
|
1685
1865
|
}
|
|
1866
|
+
let lastTargetUrl;
|
|
1867
|
+
let lastTargetRead;
|
|
1868
|
+
let targetReads = 0;
|
|
1869
|
+
function lastTarget(url) {
|
|
1870
|
+
if (url === lastTargetUrl)
|
|
1871
|
+
return lastTargetRead;
|
|
1872
|
+
targetReads++;
|
|
1873
|
+
const target = targetOf(url, knownPrefix(url), remember);
|
|
1874
|
+
lastTargetUrl = url;
|
|
1875
|
+
lastTargetRead = target;
|
|
1876
|
+
return target;
|
|
1877
|
+
}
|
|
1686
1878
|
function remember(p) {
|
|
1687
1879
|
for (let i = 0; i < prefixes.length; i++)
|
|
1688
1880
|
if (prefixes[i].text === p.text)
|
|
@@ -1718,7 +1910,7 @@ class Http2Transport {
|
|
|
1718
1910
|
const deadline = performance.now() + init.timeoutMs;
|
|
1719
1911
|
let target;
|
|
1720
1912
|
try {
|
|
1721
|
-
target =
|
|
1913
|
+
target = lastTarget(url);
|
|
1722
1914
|
}
|
|
1723
1915
|
catch {
|
|
1724
1916
|
return this.viaFetch(url, init, deadline);
|
|
@@ -1742,8 +1934,34 @@ class Http2Transport {
|
|
|
1742
1934
|
const pool = poolFor(target.origin);
|
|
1743
1935
|
if (!pool.usable())
|
|
1744
1936
|
return this.viaFetch(url, init, deadline);
|
|
1937
|
+
let raced = false;
|
|
1745
1938
|
for (;;) {
|
|
1746
|
-
|
|
1939
|
+
let entry = pool.tryAcquire();
|
|
1940
|
+
if (entry === undefined) {
|
|
1941
|
+
const starting = raced || resends > 0 || replays > 0 ? undefined : startingThread(target.origin);
|
|
1942
|
+
if (starting !== undefined) {
|
|
1943
|
+
raced = true;
|
|
1944
|
+
const won = await firstUp(starting, pool, deadline, init.signal);
|
|
1945
|
+
if (won.thread !== undefined) {
|
|
1946
|
+
if (tr !== undefined)
|
|
1947
|
+
(0, trace_js_1.mark)(tr, "race");
|
|
1948
|
+
const out = await won.thread.send(target, init, replayable, deadline);
|
|
1949
|
+
if (out === VIA_FETCH)
|
|
1950
|
+
return this.viaFetch(url, init, deadline);
|
|
1951
|
+
if (!(out instanceof HandBack))
|
|
1952
|
+
return out;
|
|
1953
|
+
resends = out.resends;
|
|
1954
|
+
replays = out.replays;
|
|
1955
|
+
if (tr !== undefined)
|
|
1956
|
+
delete tr.via;
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
entry = won.entry;
|
|
1960
|
+
}
|
|
1961
|
+
else {
|
|
1962
|
+
entry = await pool.acquire(deadline, init.signal);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1747
1965
|
if (tr !== undefined)
|
|
1748
1966
|
(0, trace_js_1.mark)(tr, "acq");
|
|
1749
1967
|
if (entry === null)
|
|
@@ -1779,6 +1997,15 @@ class Http2Transport {
|
|
|
1779
1997
|
}
|
|
1780
1998
|
}
|
|
1781
1999
|
exports.Http2Transport = Http2Transport;
|
|
2000
|
+
let sharedFallback;
|
|
2001
|
+
let shared;
|
|
2002
|
+
function sharedTransport(fallback) {
|
|
2003
|
+
if (shared === undefined || sharedFallback !== fallback) {
|
|
2004
|
+
shared = new Http2Transport(fallback);
|
|
2005
|
+
sharedFallback = fallback;
|
|
2006
|
+
}
|
|
2007
|
+
return shared;
|
|
2008
|
+
}
|
|
1782
2009
|
function warmTransport() {
|
|
1783
2010
|
const res = toResponse(200, { "content-type": "application/json" }, Buffer.from("{}"), "POST");
|
|
1784
2011
|
res.header("server-timing");
|
|
@@ -1791,6 +2018,9 @@ function warmTransport() {
|
|
|
1791
2018
|
exchange({ end() { } }, undefined, { origin: none, host: none, path: none }, undefined, 0).catch(() => { });
|
|
1792
2019
|
}
|
|
1793
2020
|
exports.__http2Internals = {
|
|
2021
|
+
targetReads() {
|
|
2022
|
+
return targetReads;
|
|
2023
|
+
},
|
|
1794
2024
|
pools() {
|
|
1795
2025
|
return [...pools.values()].map((p) => ({
|
|
1796
2026
|
origin: p.origin.origin,
|
|
@@ -1831,11 +2061,17 @@ exports.__http2Internals = {
|
|
|
1831
2061
|
if (t === undefined)
|
|
1832
2062
|
return undefined;
|
|
1833
2063
|
t.poll();
|
|
1834
|
-
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason };
|
|
2064
|
+
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason, messages: t.messages, posts: t.posts, startedAt: t.startedAt };
|
|
1835
2065
|
},
|
|
1836
2066
|
threadKnobs(env) {
|
|
1837
2067
|
return threadKnobs(env);
|
|
1838
2068
|
},
|
|
2069
|
+
wedgeThreadForTest() {
|
|
2070
|
+
try {
|
|
2071
|
+
transportThread?.port.postMessage({ t: "wedge-for-test" });
|
|
2072
|
+
}
|
|
2073
|
+
catch { }
|
|
2074
|
+
},
|
|
1839
2075
|
crashThreadForTest() {
|
|
1840
2076
|
try {
|
|
1841
2077
|
transportThread?.port.postMessage({ t: "crash-for-test" });
|
|
@@ -1874,6 +2110,15 @@ function isMainThread() {
|
|
|
1874
2110
|
return false;
|
|
1875
2111
|
}
|
|
1876
2112
|
}
|
|
2113
|
+
function standby(url) {
|
|
2114
|
+
try {
|
|
2115
|
+
(0, trace_js_1.traceEvent)("h2:standby");
|
|
2116
|
+
warmTls();
|
|
2117
|
+
poolFor(url.origin).warm(exports.tuning.loadFirstUseMs);
|
|
2118
|
+
}
|
|
2119
|
+
catch {
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
1877
2122
|
function connectAtLoad() {
|
|
1878
2123
|
try {
|
|
1879
2124
|
if (!isNode() || Number(process.versions.node.split(".")[0]) < 22)
|
|
@@ -1894,6 +2139,8 @@ function connectAtLoad() {
|
|
|
1894
2139
|
if (url.protocol === "https:" && !(0, dispatcher_js_1.customDispatcher)() && earlyPlatform() && v4First() && !threadOff && earlySeam().transportThread !== false) {
|
|
1895
2140
|
startTransportThread(url, () => { if (loadTurnRan)
|
|
1896
2141
|
warmAtLoad(); });
|
|
2142
|
+
if ((0, node_net_1.isIP)(host) === 0)
|
|
2143
|
+
earlyLookupFor(host);
|
|
1897
2144
|
}
|
|
1898
2145
|
else if (url.protocol === "https:" && !(0, dispatcher_js_1.customDispatcher)() && (0, node_net_1.isIP)(host) === 0 && earlyPlatform() && v4First())
|
|
1899
2146
|
earlyLookupFor(host);
|
|
@@ -1912,10 +2159,27 @@ function connectAtLoad() {
|
|
|
1912
2159
|
const url = new URL(raw ? raw : core_js_1.DEFAULT_API_URL);
|
|
1913
2160
|
if (url.protocol !== "https:" || (0, dispatcher_js_1.customDispatcher)())
|
|
1914
2161
|
return;
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
2162
|
+
const t = transportThread;
|
|
2163
|
+
if (t !== undefined && t.origin === url.origin) {
|
|
2164
|
+
t.poll();
|
|
2165
|
+
if (t.everReady)
|
|
1918
2166
|
return;
|
|
2167
|
+
if (t.state !== "closed") {
|
|
2168
|
+
const check = () => {
|
|
2169
|
+
t.poll();
|
|
2170
|
+
if (t.everReady || t.state === "closed")
|
|
2171
|
+
return;
|
|
2172
|
+
const wait = t.startedAt + exports.tuning.standbyAfterMs - performance.now();
|
|
2173
|
+
if (wait > 0) {
|
|
2174
|
+
const timer = setTimeout(check, wait);
|
|
2175
|
+
timer.unref?.();
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
standby(url);
|
|
2179
|
+
};
|
|
2180
|
+
check();
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
1919
2183
|
}
|
|
1920
2184
|
warmTls();
|
|
1921
2185
|
poolFor(url.origin).warm(exports.tuning.loadFirstUseMs);
|