@ai-ecoverse/slicc-kernel 1.49.1 → 1.50.0

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.
@@ -1574,6 +1574,8 @@ var KernelSocket = class _KernelSocket {
1574
1574
  reset = false;
1575
1575
  local;
1576
1576
  peer;
1577
+ peerCred;
1578
+ listenerCred;
1577
1579
  link;
1578
1580
  readShut = false;
1579
1581
  writeShut = false;
@@ -1649,7 +1651,7 @@ var KernelSocket = class _KernelSocket {
1649
1651
  this.local = this.net.bind(this, addr);
1650
1652
  this.boundAt = this.local;
1651
1653
  }
1652
- listen(backlog) {
1654
+ listen(backlog, cred) {
1653
1655
  if (this.state === "connected") throw new KernelError("EISCONN");
1654
1656
  if (this.state !== "open" && this.state !== "listening") throw new KernelError("EINVAL");
1655
1657
  if (!this.local) {
@@ -1658,6 +1660,7 @@ var KernelSocket = class _KernelSocket {
1658
1660
  }
1659
1661
  this.backlog = Math.min(Math.max(backlog, 1), SOMAXCONN);
1660
1662
  this.state = "listening";
1663
+ this.listenerCred ??= cred;
1661
1664
  }
1662
1665
  async accept(signal, nonblock = false) {
1663
1666
  for (; ; ) {
@@ -1674,7 +1677,7 @@ var KernelSocket = class _KernelSocket {
1674
1677
  this.changes.wake();
1675
1678
  return true;
1676
1679
  }
1677
- connect(addr, viaUplink = true) {
1680
+ connect(addr, viaUplink = true, cred) {
1678
1681
  if (this.state === "connected") throw new KernelError("EISCONN");
1679
1682
  if (this.state === "connecting") throw new KernelError("EALREADY");
1680
1683
  if (this.state !== "open") throw new KernelError("EINVAL");
@@ -1692,7 +1695,9 @@ var KernelSocket = class _KernelSocket {
1692
1695
  const server = new _KernelSocket(this.net, this.domain);
1693
1696
  server.local = addr.family === "inet" ? { ...addr, host: canonicalHost(addr.host) } : addr;
1694
1697
  server.peer = this.local;
1698
+ server.peerCred = cred;
1695
1699
  this.peer = server.local;
1700
+ this.peerCred = target.listenerCred;
1696
1701
  link(this, server);
1697
1702
  if (!target.offer(server)) {
1698
1703
  this.unlink();
@@ -1811,9 +1816,10 @@ var KernelSocket = class _KernelSocket {
1811
1816
  this.boundAt = void 0;
1812
1817
  this.changes.wake();
1813
1818
  }
1814
- static pair(net, domain) {
1819
+ static pair(net, domain, cred) {
1815
1820
  const a = new _KernelSocket(net, domain);
1816
1821
  const b = new _KernelSocket(net, domain);
1822
+ a.peerCred = b.peerCred = cred;
1817
1823
  const name2 = domain === "inet" ? { family: "inet", host: "127.0.0.1", port: 0 } : { family: "unix", path: "" };
1818
1824
  a.local = b.local = a.peer = b.peer = name2;
1819
1825
  link(a, b);
@@ -2142,6 +2148,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2142
2148
  net: "head",
2143
2149
  nid: call.nid,
2144
2150
  ...response.redirected ? { url: response.url, redirected: true } : {},
2151
+ ...response.redirects ? { redirects: response.redirects } : {},
2145
2152
  status: response.status,
2146
2153
  statusText: response.statusText,
2147
2154
  headers: response.headers
@@ -3698,7 +3705,7 @@ var RealmProxy = class {
3698
3705
  const waiting = new AbortController();
3699
3706
  void watchHangup(ctx.socket, abort, waiting.signal);
3700
3707
  try {
3701
- response = await this.upstream(req, url, body, abort.signal);
3708
+ response = await this.upstream(req, url, body, abort.signal, ctx.socket.peerCred?.pid);
3702
3709
  } finally {
3703
3710
  waiting.abort();
3704
3711
  }
@@ -3724,15 +3731,18 @@ var RealmProxy = class {
3724
3731
  await conn.write(latin1Bytes("HTTP/1.1 100 Continue\r\n\r\n"), this.stop.signal);
3725
3732
  }
3726
3733
  }
3727
- async upstream(req, url, body, signal) {
3734
+ async upstream(req, url, body, signal, owner) {
3728
3735
  try {
3729
- const fetching = this.options.transport.fetch({
3730
- url: toHostLoopback(url),
3731
- method: req.method.toUpperCase(),
3732
- headers: forwardRequestHeaders(req.headers),
3733
- body: req.method === "GET" || req.method === "HEAD" ? void 0 : body,
3734
- signal
3735
- });
3736
+ const fetching = this.options.transport.fetch(
3737
+ {
3738
+ url: toHostLoopback(url),
3739
+ method: req.method.toUpperCase(),
3740
+ headers: forwardRequestHeaders(req.headers),
3741
+ body: req.method === "GET" || req.method === "HEAD" ? void 0 : body,
3742
+ signal
3743
+ },
3744
+ owner
3745
+ );
3736
3746
  return await untilAborted(fetching, signal);
3737
3747
  } catch (e) {
3738
3748
  if (signal.aborted) throw e;
@@ -3744,13 +3754,13 @@ var RealmProxy = class {
3744
3754
  }
3745
3755
  async relay(conn, method, minor, response, keepAlive) {
3746
3756
  const status = response.status;
3747
- const bodiless = method === "HEAD" || status === 204 || status === 304 || status < 200;
3757
+ const bodiless2 = method === "HEAD" || status === 204 || status === 304 || status < 200;
3748
3758
  const headers = forwardResponseHeaders(response.headers, {
3749
3759
  encodedBodies: this.options.transport.traits.encodedBodies,
3750
- bodiless
3760
+ bodiless: bodiless2
3751
3761
  });
3752
- const chunked = !bodiless && minor >= 1;
3753
- const keep = keepAlive && (bodiless || chunked);
3762
+ const chunked = !bodiless2 && minor >= 1;
3763
+ const keep = keepAlive && (bodiless2 || chunked);
3754
3764
  headers.push(["Connection", keep ? "keep-alive" : "close"]);
3755
3765
  if (chunked) headers.push(["Transfer-Encoding", "chunked"]);
3756
3766
  const signal = this.stop.signal;
@@ -3758,7 +3768,7 @@ var RealmProxy = class {
3758
3768
  responseHead(status, response.statusText || REASON[status] || "", headers),
3759
3769
  signal
3760
3770
  );
3761
- if (bodiless) {
3771
+ if (bodiless2) {
3762
3772
  await response.cancel();
3763
3773
  return keep;
3764
3774
  }
@@ -4720,7 +4730,7 @@ var RemoteTransport = class {
4720
4730
  finish();
4721
4731
  throw e;
4722
4732
  }
4723
- const { status, statusText, headers, url, redirected } = head;
4733
+ const { status, statusText, headers, url, redirected, redirects } = head;
4724
4734
  const read = async () => {
4725
4735
  if (finished) return void 0;
4726
4736
  const reply = await this.ask({ net: "read", nid }).catch((e) => {
@@ -4738,6 +4748,7 @@ var RemoteTransport = class {
4738
4748
  };
4739
4749
  return {
4740
4750
  ...redirected ? { url, redirected } : {},
4751
+ ...redirects ? { redirects } : {},
4741
4752
  status,
4742
4753
  statusText,
4743
4754
  headers,
@@ -7302,9 +7313,9 @@ var SAB_I_KILLED = 13;
7302
7313
  var SAB_I_ASYNC = 14;
7303
7314
  var SAB_I_STOP = 15;
7304
7315
  var U32 = 2 ** 32;
7305
- function storeU53(header2, lo, hi, value) {
7306
- Atomics.store(header2, hi, Math.floor(value / U32));
7307
- Atomics.store(header2, lo, value % U32 | 0);
7316
+ function storeU53(header3, lo, hi, value) {
7317
+ Atomics.store(header3, hi, Math.floor(value / U32));
7318
+ Atomics.store(header3, lo, value % U32 | 0);
7308
7319
  }
7309
7320
  var SAB_STATE_READY = 2;
7310
7321
  var SAB_STATUS_BYTES = 1;
@@ -7347,7 +7358,7 @@ function sabViews(sab) {
7347
7358
  var PENDING_TTL_MS = SYNC_FS_REQUEST_TIMEOUT_MS + 5e3;
7348
7359
  function attachSyncSabResponder(port, sab, token, opts) {
7349
7360
  const views = sabViews(sab);
7350
- const { header: header2, window } = views;
7361
+ const { header: header3, window } = views;
7351
7362
  const pending = /* @__PURE__ */ new Map();
7352
7363
  let disposed = false;
7353
7364
  const { dispatch: dispatch2 } = opts;
@@ -7358,20 +7369,20 @@ function attachSyncSabResponder(port, sab, token, opts) {
7358
7369
  pending.delete(id2);
7359
7370
  }
7360
7371
  function publish(id2, entry, offset) {
7361
- if (Atomics.load(header2, SAB_I_REQ) !== id2) {
7372
+ if (Atomics.load(header3, SAB_I_REQ) !== id2) {
7362
7373
  drop(id2);
7363
7374
  return;
7364
7375
  }
7365
7376
  const chunk2 = Math.min(window.byteLength, entry.payload.byteLength - offset);
7366
7377
  if (chunk2 > 0) window.set(entry.payload.subarray(offset, offset + chunk2));
7367
- Atomics.store(header2, SAB_I_STATUS, entry.status);
7368
- storeU53(header2, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
7369
- Atomics.store(header2, SAB_I_CHUNK, chunk2);
7370
- storeU53(header2, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
7371
- Atomics.store(header2, SAB_I_SEQ, id2);
7372
- Atomics.store(header2, SAB_I_STATE, SAB_STATE_READY);
7373
- Atomics.add(header2, SAB_I_PUB, 1);
7374
- Atomics.notify(header2, SAB_I_PUB);
7378
+ Atomics.store(header3, SAB_I_STATUS, entry.status);
7379
+ storeU53(header3, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
7380
+ Atomics.store(header3, SAB_I_CHUNK, chunk2);
7381
+ storeU53(header3, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
7382
+ Atomics.store(header3, SAB_I_SEQ, id2);
7383
+ Atomics.store(header3, SAB_I_STATE, SAB_STATE_READY);
7384
+ Atomics.add(header3, SAB_I_PUB, 1);
7385
+ Atomics.notify(header3, SAB_I_PUB);
7375
7386
  if (offset + chunk2 >= entry.payload.byteLength) drop(id2);
7376
7387
  }
7377
7388
  function settle3(id2, result) {
@@ -7746,8 +7757,10 @@ var HttpHandles = class {
7746
7757
  open = /* @__PURE__ */ new Map();
7747
7758
  nextHandle = 0;
7748
7759
  ws;
7749
- constructor(transport) {
7760
+ owner;
7761
+ constructor(transport, owner) {
7750
7762
  this.transport = transport;
7763
+ this.owner = owner;
7751
7764
  this.ws = new WsHandles(transport, () => ++this.nextHandle);
7752
7765
  }
7753
7766
  syscall(req) {
@@ -7791,13 +7804,16 @@ var HttpHandles = class {
7791
7804
  }
7792
7805
  async respond(handle, req, entry) {
7793
7806
  try {
7794
- const response = await this.transport.fetch({
7795
- url: req.url,
7796
- method: req.method,
7797
- headers: req.headers,
7798
- ...req.body ? { body: req.body } : {},
7799
- signal: entry.abort.signal
7800
- });
7807
+ const response = await this.transport.fetch(
7808
+ {
7809
+ url: req.url,
7810
+ method: req.method,
7811
+ headers: req.headers,
7812
+ ...req.body ? { body: req.body } : {},
7813
+ signal: entry.abort.signal
7814
+ },
7815
+ this.owner
7816
+ );
7801
7817
  entry.body = response.body[Symbol.asyncIterator]();
7802
7818
  entry.cancel = () => response.cancel();
7803
7819
  if (!this.open.has(handle)) await entry.cancel().catch(() => void 0);
@@ -8523,7 +8539,7 @@ async function socketSyscall(req, proc) {
8523
8539
  case "sock-open":
8524
8540
  return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
8525
8541
  case "sock-pair": {
8526
- const [a, b] = KernelSocket.pair(net, req.domain);
8542
+ const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
8527
8543
  const first = fds.install(new OpenFile(a), 3);
8528
8544
  try {
8529
8545
  return ok([first, fds.install(new OpenFile(b), 3)]);
@@ -8536,12 +8552,12 @@ async function socketSyscall(req, proc) {
8536
8552
  socketAt(fds, req.fd).bind(req.addr);
8537
8553
  return ok();
8538
8554
  case "sock-listen":
8539
- socketAt(fds, req.fd).listen(req.backlog);
8555
+ socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
8540
8556
  return ok();
8541
8557
  case "sock-accept":
8542
8558
  return ok(await accept(proc, req.fd, req.nonblock));
8543
8559
  case "sock-connect": {
8544
- const dialing = socketAt(fds, req.fd).connect(req.addr);
8560
+ const dialing = socketAt(fds, req.fd).connect(req.addr, true, proc.cred?.());
8545
8561
  if (req.nonblock) {
8546
8562
  dialing?.catch(() => void 0);
8547
8563
  throw new KernelError("EINPROGRESS");
@@ -9957,7 +9973,8 @@ var WasmProcess = class {
9957
9973
  fds: this.fds,
9958
9974
  net: this.net,
9959
9975
  blocking: () => this.blockingSignal(cancelled),
9960
- resolve: (name2, family, blocking) => resolver.resolve(name2, family, blocking)
9976
+ resolve: (name2, family, blocking) => resolver.resolve(name2, family, blocking),
9977
+ cred: () => ({ pid: this.pid, uid: this.cred.euid, gid: this.cred.egid })
9961
9978
  });
9962
9979
  }
9963
9980
  tty(fd) {
@@ -10173,12 +10190,12 @@ var Dying = class {
10173
10190
  timer;
10174
10191
  busy = 0;
10175
10192
  finish;
10176
- start(code, sig, header2, process, finish) {
10193
+ start(code, sig, header3, process, finish) {
10177
10194
  if (this.ending) return;
10178
10195
  this.ending = { code, sig };
10179
10196
  this.finish = finish;
10180
10197
  this.arm();
10181
- Atomics.store(header2, SAB_I_KILLED, code);
10198
+ Atomics.store(header3, SAB_I_KILLED, code);
10182
10199
  process.die();
10183
10200
  }
10184
10201
  async serving(work) {
@@ -10215,22 +10232,22 @@ function killThread(thread, caught, sig) {
10215
10232
  function threadCancel(thread) {
10216
10233
  return Atomics.load(thread.header, SAB_I_SIGNALS) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
10217
10234
  }
10218
- function notifiers(header2) {
10235
+ function notifiers(header3) {
10219
10236
  return {
10220
10237
  onPending(sig) {
10221
- Atomics.or(header2, SAB_I_SIGNALS, sigbit(sig));
10222
- Atomics.notify(header2, SAB_I_SIGNALS);
10238
+ Atomics.or(header3, SAB_I_SIGNALS, sigbit(sig));
10239
+ Atomics.notify(header3, SAB_I_SIGNALS);
10223
10240
  },
10224
10241
  onAsync() {
10225
- Atomics.add(header2, SAB_I_ASYNC, 1);
10226
- Atomics.notify(header2, SAB_I_ASYNC);
10242
+ Atomics.add(header3, SAB_I_ASYNC, 1);
10243
+ Atomics.notify(header3, SAB_I_ASYNC);
10227
10244
  }
10228
10245
  };
10229
10246
  }
10230
- function publishStops(process, header2) {
10247
+ function publishStops(process, header3) {
10231
10248
  process.onState((state) => {
10232
- Atomics.store(header2, SAB_I_STOP, state === "stopped" ? 1 : 0);
10233
- Atomics.notify(header2, SAB_I_STOP);
10249
+ Atomics.store(header3, SAB_I_STOP, state === "stopped" ? 1 : 0);
10250
+ Atomics.notify(header3, SAB_I_STOP);
10234
10251
  });
10235
10252
  }
10236
10253
  function transferOf(opts, lane) {
@@ -10298,7 +10315,7 @@ function holdingToken(opts, holds) {
10298
10315
  renamed: (from, to) => holds.renamed(from, to)
10299
10316
  });
10300
10317
  }
10301
- function processOptions(opts, header2, hooks) {
10318
+ function processOptions(opts, header3, hooks) {
10302
10319
  return {
10303
10320
  ...inherited(opts),
10304
10321
  ...opts.identity ? { identity: opts.identity } : {},
@@ -10322,27 +10339,27 @@ function processOptions(opts, header2, hooks) {
10322
10339
  locks: opts.locks,
10323
10340
  onReap: opts.onReap,
10324
10341
  resolver: opts.resolver,
10325
- ...notifiers(header2),
10326
- onTimer: (which) => void Atomics.or(header2, SAB_I_TIMERS, 1 << which),
10327
- hasPending: () => Atomics.load(header2, SAB_I_SIGNALS) !== 0 || Atomics.load(header2, SAB_I_TIMERS) !== 0,
10342
+ ...notifiers(header3),
10343
+ onTimer: (which) => void Atomics.or(header3, SAB_I_TIMERS, 1 << which),
10344
+ hasPending: () => Atomics.load(header3, SAB_I_SIGNALS) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
10328
10345
  raise: hooks.raise,
10329
10346
  threadKill: hooks.threadKill,
10330
- ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header2, SAB_I_SIGNALS) } : {}
10347
+ ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {}
10331
10348
  };
10332
10349
  }
10333
10350
  function spawnWasmProcess(opts) {
10334
10351
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
10335
- const header2 = new Int32Array(sab, 0, SAB_HEADER_I32);
10352
+ const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
10336
10353
  const threadSignals = /* @__PURE__ */ new Map();
10337
10354
  const process = new WasmProcess(
10338
10355
  opts.pid,
10339
10356
  opts.fds,
10340
- processOptions(opts, header2, {
10357
+ processOptions(opts, header3, {
10341
10358
  raise: (sig) => signal(sig),
10342
10359
  threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig)
10343
10360
  })
10344
10361
  );
10345
- publishStops(process, header2);
10362
+ publishStops(process, header3);
10346
10363
  const holds = new HeldPaths();
10347
10364
  opts.held?.add(holds);
10348
10365
  const token = holdingToken(opts, holds);
@@ -10454,7 +10471,7 @@ function spawnWasmProcess(opts) {
10454
10471
  if (process.signal(sig) !== "terminate") return;
10455
10472
  const code = sig === SIG.KILL ? 137 : 128 + sig;
10456
10473
  if (opts.program.abi !== "wasi" || done4) finish(code, sig);
10457
- else dying.start(code, sig, header2, process, () => finish(code, sig));
10474
+ else dying.start(code, sig, header3, process, () => finish(code, sig));
10458
10475
  };
10459
10476
  const kill = (code = 137) => finish(code, code > 128 && code < 160 ? code - 128 : void 0);
10460
10477
  return {
@@ -10464,7 +10481,7 @@ function spawnWasmProcess(opts) {
10464
10481
  signal,
10465
10482
  termsig: () => endedBy,
10466
10483
  onState: (listener) => process.onState(listener),
10467
- memory: () => Atomics.load(header2, SAB_I_MEMORY) * 65536,
10484
+ memory: () => Atomics.load(header3, SAB_I_MEMORY) * 65536,
10468
10485
  ignoredSignals: (fork) => process.inheritable(fork),
10469
10486
  signalMasks: () => process.signalMasks(),
10470
10487
  umask: () => process.umask,
@@ -10659,6 +10676,89 @@ var JobTable = class {
10659
10676
  }
10660
10677
  };
10661
10678
 
10679
+ // src/kernel/net/redirect-hops.ts
10680
+ var HELD_MS = 3e4;
10681
+ var HELD_MAX = 64;
10682
+ var HELD_PER_OWNER = 8;
10683
+ var MATCHED = ["range", "if-none-match", "if-modified-since", "if-range", "accept-encoding"];
10684
+ var KERNEL = -1;
10685
+ function header2(headers, name2) {
10686
+ const values = headers.filter(([n]) => n.toLowerCase() === name2).map(([, v]) => v);
10687
+ return values.length > 0 ? values.join(", ") : void 0;
10688
+ }
10689
+ function fingerprint(headers) {
10690
+ const fields = MATCHED.map((name2) => header2(headers, name2) ?? "");
10691
+ return JSON.stringify([...fields, header2(headers, "authorization") !== void 0]);
10692
+ }
10693
+ function followedWith(status, method) {
10694
+ if (status === 303 && method !== "GET" && method !== "HEAD") return "GET";
10695
+ if ((status === 301 || status === 302) && method === "POST") return "GET";
10696
+ return method;
10697
+ }
10698
+ function bodiless(hop) {
10699
+ return {
10700
+ url: hop.url,
10701
+ status: hop.status,
10702
+ statusText: "",
10703
+ headers: hop.headers,
10704
+ body: (async function* () {
10705
+ })(),
10706
+ cancel: async () => void 0
10707
+ };
10708
+ }
10709
+ function replayingHops(inner, heldMs = HELD_MS) {
10710
+ if (!inner.traits.redirectHops || inner.traits.manualRedirects) return inner;
10711
+ const held = /* @__PURE__ */ new Map();
10712
+ const drop = (key) => {
10713
+ const entry = held.get(key);
10714
+ if (!entry) return;
10715
+ held.delete(key);
10716
+ clearTimeout(entry.timer);
10717
+ void entry.final.cancel().catch(() => void 0);
10718
+ };
10719
+ const keyOf2 = (owner, method, url) => `${owner} ${method} ${url}`;
10720
+ const hold = (key, chain) => {
10721
+ drop(key);
10722
+ const mine = [...held.keys()].filter((k) => held.get(k)?.owner === chain.owner);
10723
+ if (mine.length >= HELD_PER_OWNER) drop(mine[0]);
10724
+ if (held.size >= HELD_MAX) drop(held.keys().next().value);
10725
+ const timer = setTimeout(() => drop(key), heldMs);
10726
+ timer.unref?.();
10727
+ held.set(key, { ...chain, timer });
10728
+ };
10729
+ const replay = (chain) => {
10730
+ const [hop, ...rest2] = chain.hops;
10731
+ const location = hop && header2(hop.headers, "location");
10732
+ if (!hop || location === void 0) return chain.final;
10733
+ const next2 = new URL(location, hop.url).href;
10734
+ const method = followedWith(hop.status, chain.method);
10735
+ hold(keyOf2(chain.owner, method, next2), { ...chain, method, hops: rest2 });
10736
+ return bodiless(hop);
10737
+ };
10738
+ return {
10739
+ traits: { ...inner.traits, manualRedirects: true },
10740
+ ...inner.websocket ? { websocket: inner.websocket.bind(inner) } : {},
10741
+ async fetch(request2, owner = KERNEL) {
10742
+ const key = keyOf2(owner, request2.method, request2.url);
10743
+ const waiting = held.get(key);
10744
+ if (waiting && waiting.fingerprint === fingerprint(request2.headers)) {
10745
+ held.delete(key);
10746
+ clearTimeout(waiting.timer);
10747
+ return replay(waiting);
10748
+ }
10749
+ const response = await inner.fetch(request2);
10750
+ if (!response.redirects?.length) return response;
10751
+ return replay({
10752
+ owner,
10753
+ method: request2.method,
10754
+ fingerprint: fingerprint(request2.headers),
10755
+ hops: response.redirects,
10756
+ final: response
10757
+ });
10758
+ }
10759
+ };
10760
+ }
10761
+
10662
10762
  // src/kernel/settling.ts
10663
10763
  var SETUP_OPS = /* @__PURE__ */ new Set([
10664
10764
  "proc-list",
@@ -12075,7 +12175,7 @@ var Launcher = class {
12075
12175
  this.hostname = hostname;
12076
12176
  this.env = { ...networkEnv(hostname), SLICC_CDP_URL: this.cdp.url, ...options.env };
12077
12177
  this.ca = kernelCa(options.caStore ?? memoryCaStore());
12078
- this.transport = options.transport ?? missingTransport();
12178
+ this.transport = replayingHops(options.transport ?? missingTransport());
12079
12179
  this.uplink = options.uplink;
12080
12180
  this.routes = new Routes(options.uplink?.traits.ipv6 === true);
12081
12181
  if (options.uplink?.routes) this.routes.set(options.uplink.routes);
@@ -12332,7 +12432,7 @@ var Launcher = class {
12332
12432
  jobs: this.jobs,
12333
12433
  ptys: this.ptys,
12334
12434
  net: this.net,
12335
- http: new HttpHandles(this.transport),
12435
+ http: new HttpHandles(this.transport, pid),
12336
12436
  locks: this.locks,
12337
12437
  resolver: this.resolver,
12338
12438
  onReap: (child) => this.reaped(child),