@amritk/nish-x86_64-linux 0.17.0 → 0.20.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.
Files changed (61) hide show
  1. package/INSTALL.md +43 -25
  2. package/bin/nish +0 -0
  3. package/package.json +2 -2
  4. package/runtime/nish.d.ts +55 -5
  5. package/runtime/nish.h +231 -20
  6. package/runtime/nish.mjs +16 -0
  7. package/runtime/runtime-aes.c +490 -0
  8. package/runtime/runtime-cross.c +366 -0
  9. package/runtime/runtime-host.c +7 -6
  10. package/runtime/runtime-net.c +93 -34
  11. package/runtime/runtime-os.c +213 -15
  12. package/runtime/runtime-parallel.c +590 -107
  13. package/runtime/runtime-simd.c +203 -0
  14. package/runtime/runtime-wasm.c +16 -3
  15. package/runtime/runtime.c +89 -23
  16. package/runtime/shim.mjs +71 -1
  17. package/scripts/build.sh +225 -20
  18. package/std/README.md +86 -55
  19. package/std/bytes.ts +163 -0
  20. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  21. package/std/crypto/aes.ts +238 -76
  22. package/std/crypto/chacha20poly1305.ts +43 -9
  23. package/std/crypto/ed25519.ts +840 -0
  24. package/std/crypto/hkdf.ts +304 -12
  25. package/std/crypto/hmac.ts +245 -53
  26. package/std/crypto/p256.ts +1376 -42
  27. package/std/crypto/x25519.ts +55 -37
  28. package/std/crypto/x509-random.ts +71 -0
  29. package/std/crypto/x509.ts +9 -9
  30. package/std/json-tokenizer.ts +1296 -0
  31. package/std/json-typed.ts +1014 -0
  32. package/std/json-value.ts +1163 -0
  33. package/std/json.ts +1412 -70
  34. package/std/net/buffer-pool.ts +151 -0
  35. package/std/net/hpack.ts +416 -112
  36. package/std/net/http-fields.ts +93 -0
  37. package/std/net/http1-server.ts +417 -55
  38. package/std/net/http1.ts +31 -6
  39. package/std/net/http2-tls.ts +112 -24
  40. package/std/net/http2.ts +333 -75
  41. package/std/net/http3-server.ts +231 -45
  42. package/std/net/http3.ts +187 -28
  43. package/std/net/prefork.ts +412 -0
  44. package/std/net/quic-conn-params.ts +86 -16
  45. package/std/net/quic-listener.ts +576 -219
  46. package/std/net/quic-packet.ts +443 -58
  47. package/std/net/quic-recovery.ts +361 -30
  48. package/std/net/quic-stream.ts +434 -28
  49. package/std/net/quic.ts +922 -206
  50. package/std/net/router.ts +676 -0
  51. package/std/net/tls/codec.ts +426 -27
  52. package/std/net/tls/record-server.ts +478 -132
  53. package/std/net/tls/record.ts +133 -42
  54. package/std/net/tls/schedule.ts +464 -55
  55. package/std/net/tls/ticket.ts +610 -0
  56. package/std/net/tls-tcp.ts +204 -57
  57. package/std/net/tls.ts +1132 -305
  58. package/std/net/url.ts +571 -0
  59. package/std/random.ts +94 -0
  60. package/std/text.ts +77 -0
  61. package/std/threads.ts +438 -14
@@ -9,6 +9,7 @@
9
9
  * // quicConfig.alpn is ["h3"]; pollAdd(loop, fd, 1, 0)
10
10
  * // each time round the loop, with `now` in milliseconds:
11
11
  * pollWait(loop, ready, server.timeout(now));
12
+ * server.clock(micros); // the same instant in microseconds, for the pacer
12
13
  * server.receive(now, key); // every datagram waiting; signs handshakes with `key`
13
14
  * server.tick(now); // the connections' timers
14
15
  * let slot: i32 = server.ready();
@@ -19,6 +20,8 @@
19
20
  * slot = server.ready();
20
21
  * }
21
22
  * server.flush(now); // what every touched slot has to send
23
+ * // at shutdown:
24
+ * server.stop(); // releases every slot, wipes the static keys
22
25
  *
23
26
  * **Routing.** A datagram goes to the slot that owns its first packet's
24
27
  * Destination Connection ID, found through a hash index of every slot's
@@ -38,6 +41,10 @@
38
41
  * **Sending.** `flush` writes each touched slot's datagrams with
39
42
  * `quicListenerTakeFlight`, a paced flight at a time in one `udpSendTo` with
40
43
  * GSO where the platform has it and one datagram at a time where it does not.
44
+ * The pacer counts the microseconds `clock` gives it, so on a path whose
45
+ * round trip is under a millisecond it neither waits for the next
46
+ * millisecond to earn credit nor holds a slot back for a whole one; without
47
+ * `clock` it counts whole milliseconds.
41
48
  * A slot whose HTTP/3 side sent GOAWAY and whose requests have all finished,
42
49
  * every byte acknowledged, is closed with H3_NO_ERROR; a closed slot is freed
43
50
  * once its CONNECTION_CLOSE is out.
@@ -51,11 +58,21 @@
51
58
  *
52
59
  * **Caps, fixed at start-up**: the slots, and inside each the QUIC
53
60
  * configuration's streams and buffers and the `Http3Config`'s field section,
54
- * body chunk and DATA frame. The constructor makes every slot's connections;
61
+ * body chunk and DATA frame. A slot's connections and address are built on
62
+ * the first Initial accepted into it and kept, so a pool sized for its peak
63
+ * costs at start-up only its per-slot tables (the index, the wheel, the
64
+ * queues), and holds at most `size` connections once the peak has come;
55
65
  * a datagram an established connection reads, and every datagram it sends,
56
- * allocates nothing. What does allocate is a new connection's — the listener's
57
- * work on a datagram no slot owns, and the QUIC handshake (TLS-3) —
58
- * `docs/security/http3.md` lists it.
66
+ * allocates nothing; nor does a datagram no slot owns, which the listener
67
+ * reads where it lies and answers into scratch made once, nor a handshake
68
+ * into a slot built before, whose state is the slot's —
69
+ * `docs/security/http3.md` (H3-1, H3-3).
70
+ *
71
+ * **The Date.** Every response carries one `date` field (RFC 9110 §6.6.1)
72
+ * from the pool's `HttpDate`, which formats the IMF-fixdate at most once a
73
+ * second for every slot; trailers carry none, and a program that sends its
74
+ * own `date` sends that one instead. `server.date.pin(unixMs)` fixes it, for
75
+ * a test that compares bytes.
59
76
  *
60
77
  * Written from RFC 9114 §3 and RFC 9000 §5.2, not ported from another
61
78
  * implementation. Private names carry the `http3Server` prefix
@@ -63,6 +80,8 @@
63
80
  */
64
81
  import { udpRecvFrom, udpSendTo } from "nish:net"
65
82
  import { Secret } from "nish:secret"
83
+ import { BufferPool } from "nish/net/buffer-pool"
84
+ import { HttpDate } from "nish/net/http1-server"
66
85
  import { Http3Config, Http3Connection } from "nish/net/http3"
67
86
  import { H3_NO_ERROR, h3Count } from "nish/net/http3-frame"
68
87
  import {
@@ -70,21 +89,25 @@ import {
70
89
  QUIC_CONN_DATAGRAM_SIZE,
71
90
  QUIC_CONN_ENTROPY_SIZE,
72
91
  QUIC_CONN_LOCAL_CIDS,
92
+ QUIC_CONN_MAX_STREAM_DATA,
93
+ QUIC_CONN_MAX_STREAMS,
73
94
  QuicConnection,
74
95
  QuicServerConfig,
75
96
  } from "nish/net/quic"
76
97
  import { QUIC_MAX_CID_LENGTH } from "nish/net/quic-packet"
98
+ import { QuicStreamPools, quicStreamGrowFree } from "nish/net/quic-stream"
77
99
  import {
78
100
  QUIC_LISTEN_ACCEPT,
101
+ QUIC_LISTENER_ANSWER_SIZE,
79
102
  QUIC_LISTENER_FLIGHT_MAX,
80
103
  QUIC_LISTENER_MIN_RESET,
81
104
  QuicFlight,
82
105
  QuicListener,
83
106
  QuicListenerAnswer,
84
- quicListenerPaceTime,
107
+ quicListenerFlightTime,
85
108
  quicListenerTakeFlight,
86
109
  } from "nish/net/quic-listener"
87
- import { tlsSignEcdsaP256 } from "nish/net/tls"
110
+ import { TlsP256Signer } from "nish/net/tls"
88
111
 
89
112
  /** The timer wheel's buckets, one millisecond each: its horizon. */
90
113
  export const H3_SERVER_WHEEL: i32 = 2048
@@ -396,12 +419,29 @@ export class Http3Wheel {
396
419
  */
397
420
  export class Http3Server {
398
421
  quicConfig: QuicServerConfig
422
+ /** The HTTP/3 configuration each slot's `Http3Connection` is built with. */
423
+ config: Http3Config
399
424
  listener: QuicListener
425
+ /** What the listener answers into, made once: its reply and IDs are refilled in their own room. */
426
+ answer: QuicListenerAnswer
427
+ /** The slots built so far, 0 up: each built on the first Initial accepted into it, or the first ask for it. */
400
428
  quics: QuicConnection[]
401
429
  conns: Http3Connection[]
430
+ /**
431
+ * The `date` every slot's responses carry, formatted once a second for all
432
+ * of them: each slot's `Http3Connection.date` is its bytes, refreshed by
433
+ * `connection` and `ready`. `date.pin` fixes its clock.
434
+ */
435
+ date: HttpDate
402
436
  index: Http3CidIndex
403
437
  wheel: Http3Wheel
404
- /** Each slot's client address, as its first datagram came from. */
438
+ /**
439
+ * Each slot's client address, as its first datagram came from. Not
440
+ * validated until `quic(slot).addressValidated` is set (RFC 9000 §8.1): a
441
+ * Retry token the listener checked, with `retry` on, or a Handshake packet
442
+ * from the client. Before then it is the source of one UDP datagram, which
443
+ * anyone can forge, so a cap or an allow-list keyed on it waits for that.
444
+ */
405
445
  addresses: u8[][]
406
446
  /** What `receive` reads into, and where `flush` writes a flight. */
407
447
  rx: u8[]
@@ -410,23 +450,33 @@ export class Http3Server {
410
450
  meta: i32[]
411
451
  entropy: u8[]
412
452
  flight: QuicFlight
453
+ /** Signs every slot's CertificateVerify in `serve`, one for the server, keeping nothing per signature. */
454
+ signer: TlsP256Signer
413
455
  /** Each slot's connection-ID index signature, so it is refiled only when its IDs changed. */
414
456
  signatures: i64[]
415
457
  state: i32[]
416
458
  /** The slots with datagrams for the program, a ring, and the slots with something to send, a stack. */
417
459
  readyRing: i32[]
418
460
  dirtyStack: i32[]
419
- /** Free slots, a stack. */
461
+ /** Released slots, a stack: a slot never built is taken from `used` instead. */
420
462
  free: i32[]
421
463
  /** Per slot: 1 while on the ready ring, 2 while on the dirty stack. */
422
464
  queued: i32[]
423
465
  /** When the answer budget was last topped up, -1 before the first answer. */
424
466
  answerRefilled: i64 = -1
467
+ /** The caller's clock in microseconds, from `clock`, or -1: what each connection's pacer counts. */
468
+ micros: i64 = -1
425
469
  fd: i32 = -1
426
470
  readyHead: i32 = 0
427
471
  readyCount: i32 = 0
428
472
  dirtyCount: i32 = 0
429
473
  freeCount: i32 = 0
474
+ /** How many slots the pool may hold, which `size` answers. */
475
+ slots: i32 = 1
476
+ /** The slots ever accepted into, 0 up: one past it has never held a connection, so is free without being on `free`. */
477
+ used: i32 = 0
478
+ /** Set by `stop`: a slot built after it is released as it is made, so it holds no keys. */
479
+ stopped: boolean = false
430
480
  /** What is left of the answer budget (see `answerRefilled`). */
431
481
  answerBudget: i32 = 0
432
482
  /** Counters for a log or a test, saturating: connections accepted, Initials dropped for want of a slot, datagrams past the answer budget, sends that failed. */
@@ -434,59 +484,137 @@ export class Http3Server {
434
484
  refused: i32 = 0
435
485
  limited: i32 = 0
436
486
  sendErrors: i32 = 0
487
+ /**
488
+ * The stream buffers every slot's connection borrows from, made by the
489
+ * constructor for the stream table `quicConfig` gives every slot: `null`
490
+ * when its streams have none. Each pool makes a buffer only when a stream
491
+ * first needs one, so an idle server holds none (QUIC-3). It is made even
492
+ * for a configuration `quicConnConfigFits` refuses; `build` shares it only
493
+ * with a connection whose stream buffers are its size.
494
+ */
495
+ streamPools: QuicStreamPools | null = null
496
+ /** The `bodyChunk`-byte read buffers every slot's `Http3Connection` borrows, one at most each. */
497
+ dataPool: BufferPool
498
+ /** The `maxFieldSectionSize`-byte HEADERS buffers every slot's `Http3Connection` borrows around a decode. */
499
+ sectionPool: BufferPool
437
500
 
438
501
  /**
439
502
  * A pool of `size` slots serving HTTP/3 under `config` over QUIC under
440
503
  * `quicConfig` on the UDP socket `fd`. `listenerEntropy` is the
441
504
  * listener's `QUIC_LISTENER_ENTROPY_SIZE` random bytes, whose first eight
442
505
  * also key the connection-ID index (wiped in the caller's array, as the
443
- * listener does). Every slot's connections are made here.
506
+ * listener does). No slot's connections are made here: each slot's are
507
+ * built on the first Initial accepted into it. From here on
508
+ * `quicConfig`'s `statelessResetKey` and `retryTokenKey` are the server's:
509
+ * `stop` wipes them, so the configuration is this server's alone.
444
510
  */
445
511
  constructor(quicConfig: QuicServerConfig, config: Http3Config, fd: i32, size: i32, listenerEntropy: u8[]) {
446
512
  if (size < 1 || size > 65536) {
447
513
  panic(`Http3Server: ${size} slots, outside 1 to 65536`)
448
514
  }
449
515
  this.quicConfig = quicConfig
516
+ this.config = config
450
517
  this.fd = fd
518
+ this.slots = size
451
519
  this.index = new Http3CidIndex(size, listenerEntropy)
452
520
  this.listener = new QuicListener(quicConfig, listenerEntropy)
521
+ this.answer = new QuicListenerAnswer(QUIC_LISTENER_ANSWER_SIZE)
453
522
  this.wheel = new Http3Wheel(size)
454
523
  this.quics = []
455
524
  this.conns = []
525
+ this.date = new HttpDate()
526
+ // Formatted once now, so no slot ever sends the bytes before a first format.
527
+ this.date.current()
456
528
  this.addresses = []
457
529
  this.entropy = new Array<u8>(QUIC_CONN_ENTROPY_SIZE)
458
- for (let k: i32 = 0; k < size; k++) {
459
- crypto.getRandomValues(this.entropy)
460
- const quic = new QuicConnection(quicConfig, this.entropy)
461
- this.quics.push(quic)
462
- this.conns.push(new Http3Connection(config, quic))
463
- this.addresses.push(new Array<u8>(H3_SERVER_ADDRESS))
530
+ this.dataPool = new BufferPool("Http3Server.data", config.bodyChunk, size)
531
+ this.sectionPool = new BufferPool("Http3Server.section", config.maxFieldSectionSize, size)
532
+ // Room for every buffer each pool can lend, so that giving one back never grows the arena.
533
+ const none: u8[] = []
534
+ quicStreamGrowFree(this.dataPool.free, size, none)
535
+ quicStreamGrowFree(this.sectionPool.free, size, none)
536
+ // The stream table every slot's connection will have, read from the
537
+ // configuration as `QuicConnection` reads it: a shape outside its bounds
538
+ // gets no pools, and a connection whose configuration does not fit has no
539
+ // table to share them with.
540
+ const streamData: i64 = quicConfig.maxStreamData
541
+ const bidi: i64 = quicConfig.maxStreamsBidi
542
+ const uni: i64 = quicConfig.maxStreamsUni
543
+ const local: i64 = quicConfig.localStreams
544
+ if (
545
+ streamData >= 1 &&
546
+ streamData <= QUIC_CONN_MAX_STREAM_DATA &&
547
+ bidi >= 0 &&
548
+ bidi <= QUIC_CONN_MAX_STREAMS &&
549
+ uni >= 0 &&
550
+ uni <= QUIC_CONN_MAX_STREAMS &&
551
+ local >= 0 &&
552
+ local <= QUIC_CONN_MAX_STREAMS &&
553
+ bidi + uni + local > 0
554
+ ) {
555
+ this.streamPools = new QuicStreamPools(toI32(streamData), toI32(bidi + uni + local), size)
464
556
  }
465
557
  this.rx = new Array<u8>(H3_SERVER_RECEIVE_SIZE)
466
558
  this.tx = new Array<u8>(QUIC_LISTENER_FLIGHT_MAX * QUIC_CONN_DATAGRAM_SIZE)
467
559
  this.from = new Array<u8>(H3_SERVER_ADDRESS)
468
560
  this.meta = [H3_SERVER_ZERO, H3_SERVER_ZERO]
469
561
  this.flight = new QuicFlight()
562
+ this.signer = new TlsP256Signer()
470
563
  this.signatures = new Array<i64>(size)
471
564
  this.state = new Array<i32>(size)
472
565
  this.readyRing = new Array<i32>(size)
473
566
  this.dirtyStack = new Array<i32>(size)
567
+ // Released slots only: one never used is taken at `used`.
474
568
  this.free = new Array<i32>(size)
475
569
  this.queued = new Array<i32>(size)
476
- for (let k: i32 = size - 1; k >= 0; k--) {
477
- this.free[this.freeCount] = k
478
- this.freeCount = this.freeCount + 1
570
+ }
571
+
572
+ /**
573
+ * Builds the next slot, `quics.length`: its QUIC connection under fresh
574
+ * entropy, sharing `streamPools` when its stream table is the one they
575
+ * were made for, its HTTP/3 connection on the pool's buffers and `date`,
576
+ * and its address.
577
+ */
578
+ build(): void {
579
+ crypto.getRandomValues(this.entropy)
580
+ const quic = new QuicConnection(this.quicConfig, this.entropy)
581
+ this.quics.push(quic)
582
+ const pools: QuicStreamPools | null = this.streamPools
583
+ if (pools !== null && pools.data.size === quic.streams.bufferSize && quic.streams.slots.length > 0) {
584
+ quic.streams.share(pools)
585
+ }
586
+ const conn: Http3Connection = new Http3Connection(this.config, quic)
587
+ conn.share(this.dataPool, this.sectionPool)
588
+ conn.date = this.date.current()
589
+ this.conns.push(conn)
590
+ this.addresses.push(new Array<u8>(H3_SERVER_ADDRESS))
591
+ if (this.stopped) {
592
+ conn.release()
479
593
  }
480
594
  }
481
595
 
482
- /** How many slots the pool has. */
596
+ /** `slot` when it is one of the pool's, built with every slot before it if it was not; 0 when it is out of range. */
597
+ slotAt(slot: i32): i32 {
598
+ const at: i32 = slot >= 0 && slot < this.slots ? slot : H3_SERVER_ZERO
599
+ while (toI32(this.quics.length) <= at) {
600
+ this.build()
601
+ }
602
+ return at
603
+ }
604
+
605
+ /** Whether every slot holds a connection: none released, and none left never used. */
606
+ full(): boolean {
607
+ return this.freeCount <= 0 && this.used >= this.slots
608
+ }
609
+
610
+ /** How many slots the pool has, built or not. */
483
611
  size(): i32 {
484
- return toI32(this.quics.length)
612
+ return this.slots
485
613
  }
486
614
 
487
615
  /** How many slots hold a connection. */
488
616
  busy(): i32 {
489
- return this.size() - this.freeCount
617
+ return this.used - this.freeCount
490
618
  }
491
619
 
492
620
  /** Whether `slot` holds a connection. */
@@ -494,16 +622,20 @@ export class Http3Server {
494
622
  return slot >= 0 && slot < toI32(this.state.length) && this.state[slot] === H3_SERVER_BUSY
495
623
  }
496
624
 
497
- /** The HTTP/3 connection of `slot`; a slot out of range names the first. */
625
+ /**
626
+ * The HTTP/3 connection of `slot`, its `date` brought up to now; a slot out
627
+ * of range names the first. A slot not built yet is built here, with every
628
+ * slot before it, so a program that binds something to each slot's
629
+ * connection up front gets the one its slot keeps.
630
+ */
498
631
  connection(slot: i32): Http3Connection {
499
- const at: i32 = slot >= 0 && slot < toI32(this.conns.length) ? slot : H3_SERVER_ZERO
500
- return this.conns[at]
632
+ this.date.current()
633
+ return this.conns[this.slotAt(slot)]
501
634
  }
502
635
 
503
- /** The QUIC connection of `slot`; a slot out of range names the first. */
636
+ /** The QUIC connection of `slot`, built as `connection` builds it; a slot out of range names the first. */
504
637
  quic(slot: i32): QuicConnection {
505
- const at: i32 = slot >= 0 && slot < toI32(this.quics.length) ? slot : H3_SERVER_ZERO
506
- return this.quics[at]
638
+ return this.quics[this.slotAt(slot)]
507
639
  }
508
640
 
509
641
  /** Puts `slot` on the ready ring and the dirty stack, unless it is on them. */
@@ -531,6 +663,7 @@ export class Http3Server {
531
663
 
532
664
  /** The next slot with news for the program, or -1: call its connection's `next` until it has none. */
533
665
  ready(): i32 {
666
+ this.date.current()
534
667
  while (this.readyCount > 0) {
535
668
  const slot: i32 = this.readyRing[this.readyHead]
536
669
  this.readyHead = (this.readyHead + 1) % toI32(this.readyRing.length)
@@ -605,28 +738,36 @@ export class Http3Server {
605
738
  this.unowned(buf, at, len, address, now, key)
606
739
  }
607
740
 
608
- /** A datagram no slot owns: the listener decides, and may make a connection in a free slot. */
741
+ /**
742
+ * A datagram no slot owns: the listener decides, reading it where it lies
743
+ * in `buf` and answering into `answer`, and may make a connection in a
744
+ * free slot, which reads the same window. Nothing is copied (H3-3).
745
+ */
609
746
  unowned(buf: u8[], at: i32, len: i32, address: u8[], now: i64, key: Secret<u8[]>): void {
610
747
  if (!this.worthListening(buf, at, len, now)) {
611
748
  return
612
749
  }
613
- const datagram: u8[] = new Array<u8>(len)
614
- for (let k: i32 = 0; k < len && k < toI32(datagram.length) && at + k < toI32(buf.length); k++) {
615
- datagram[k] = buf[at + k]
616
- }
617
- const answer: QuicListenerAnswer = this.listener.handle(datagram, address, now)
618
- if (answer.kind !== QUIC_LISTEN_ACCEPT) {
750
+ const answer: QuicListenerAnswer = this.answer
751
+ if (this.listener.handleWindow(buf, at, len, address, now, answer) !== QUIC_LISTEN_ACCEPT) {
619
752
  const reply: i32 = toI32(answer.reply.length)
620
753
  if (reply > 0) {
621
754
  this.send(answer.reply, H3_SERVER_ZERO, reply, address, H3_SERVER_ZERO)
622
755
  }
623
756
  return
624
757
  }
625
- if (this.freeCount <= 0) {
758
+ // A slot released before is taken first, then the next never used, so the
759
+ // first accepts take slots 0, 1, 2 ... and a pool that never fills never
760
+ // builds its last slots.
761
+ let slot: i32 = 0
762
+ if (this.freeCount > 0) {
763
+ this.freeCount = this.freeCount - 1
764
+ slot = this.free[this.freeCount]
765
+ } else if (this.used < this.slots) {
766
+ slot = this.slotAt(this.used)
767
+ this.used = this.used + 1
768
+ } else {
626
769
  return
627
770
  }
628
- this.freeCount = this.freeCount - 1
629
- const slot: i32 = this.free[this.freeCount]
630
771
  crypto.getRandomValues(this.entropy)
631
772
  const quic: QuicConnection = this.quics[slot]
632
773
  quic.reset(this.entropy)
@@ -646,9 +787,8 @@ export class Http3Server {
646
787
 
647
788
  /**
648
789
  * Whether the listener could answer `buf[at .. at + len)` at all, read
649
- * from its first bytes: what it would drop without a word, it costs
650
- * nothing to drop here, before the copy and the parse the listener makes
651
- * (H3-3). That is a short header too short for a stateless reset; a long
790
+ * from its first bytes: what it would drop without a word is dropped
791
+ * here, before the listener parses it. That is a short header too short for a stateless reset; a long
652
792
  * one shorter than a full Initial, which neither Version Negotiation nor a
653
793
  * new connection answers (RFC 9000 §14.1, §6.1); version 0; a version 1
654
794
  * packet that is not an Initial, or whose DCID is under 8 bytes (§7.2);
@@ -674,7 +814,7 @@ export class Http3Server {
674
814
  if ((first & 0x30) !== 0 || toI32(buf[at + 5]) < 8) {
675
815
  return false
676
816
  }
677
- if (this.freeCount <= 0) {
817
+ if (this.full()) {
678
818
  this.refused = h3Count(this.refused)
679
819
  return false
680
820
  }
@@ -686,7 +826,7 @@ export class Http3Server {
686
826
  * `H3_SERVER_ANSWER_BURST` at once, then one per
687
827
  * `H3_SERVER_ANSWER_INTERVAL` milliseconds — topping it up first. Past it
688
828
  * the datagram is dropped before the listener sees it, so a flood of
689
- * garbage costs nothing (H3-3); counted in `limited`.
829
+ * garbage costs no answer and no MAC past the budget (H3-3); counted in `limited`.
690
830
  */
691
831
  takeAnswer(now: i64): boolean {
692
832
  if (this.answerRefilled < 0 || now < this.answerRefilled) {
@@ -707,13 +847,25 @@ export class Http3Server {
707
847
  return true
708
848
  }
709
849
 
850
+ /**
851
+ * Gives the server the caller's monotonic clock in microseconds: the
852
+ * instant the `now` of the calls that follow names, `monotonicNanos() /
853
+ * 1000` where `now` is `monotonicNanos() / 1000000`. The pacer counts it;
854
+ * every timer still counts `now`. One that is not inside `now`'s
855
+ * millisecond is not read.
856
+ */
857
+ clock(micros: i64): void {
858
+ this.micros = micros
859
+ }
860
+
710
861
  /** Hands `slot`'s connection a datagram, signs when the handshake asks, and refiles its IDs and timer. */
711
862
  serve(slot: i32, buf: u8[], at: i32, len: i32, now: i64, key: Secret<u8[]>): void {
712
863
  const quic: QuicConnection = this.quics[slot]
864
+ quic.recovery.micros = this.micros
713
865
  quic.receiveWindow(buf, at, len, now)
714
866
  const input: u8[] | null = quic.signatureInput()
715
867
  if (input !== null) {
716
- const signature: u8[] | null = tlsSignEcdsaP256(key, input)
868
+ const signature: u8[] | null = this.signer.sign(key, input)
717
869
  if (signature !== null) {
718
870
  quic.sign(signature)
719
871
  }
@@ -795,6 +947,7 @@ export class Http3Server {
795
947
  /** `flush` of one slot. */
796
948
  flushSlot(slot: i32, now: i64): void {
797
949
  const quic: QuicConnection = this.quics[slot]
950
+ quic.recovery.micros = this.micros
798
951
  if (this.conns[slot].isDone() && !quic.closed()) {
799
952
  quic.close(H3_NO_ERROR)
800
953
  }
@@ -837,7 +990,7 @@ export class Http3Server {
837
990
  // The pacer answers a time, not a delay: the slot wakes at it when it
838
991
  // comes before the connection's own deadline.
839
992
  let due: i64 = quic.deadline()
840
- const paced: i64 = quicListenerPaceTime(quic, now)
993
+ const paced: i64 = quicListenerFlightTime(quic, now)
841
994
  if (paced > now && (due < 0 || paced < due)) {
842
995
  due = paced
843
996
  }
@@ -856,7 +1009,7 @@ export class Http3Server {
856
1009
  release(slot: i32): void {
857
1010
  this.unindex(slot)
858
1011
  this.wheel.unfile(slot)
859
- this.quics[slot].release()
1012
+ this.conns[slot].release()
860
1013
  this.state[slot] = H3_SERVER_FREE
861
1014
  if (this.freeCount < toI32(this.free.length)) {
862
1015
  this.free[this.freeCount] = slot
@@ -864,6 +1017,39 @@ export class Http3Server {
864
1017
  }
865
1018
  }
866
1019
 
1020
+ /**
1021
+ * Stops the server for good and wipes the keys it holds: every built slot's
1022
+ * connection is released (`QuicConnection.release`, which sends nothing),
1023
+ * as is one built after, by `connection` or `quic`; the listener is
1024
+ * released; and the configuration's `statelessResetKey` and
1025
+ * `retryTokenKey`, the index's salt, the connections' entropy scratch
1026
+ * and the listener's last answer (a Retry's or a reset's token) are
1027
+ * zeroed. From then on every datagram is dropped and no connection is
1028
+ * accepted. The configuration's two keys are the server's once it is made
1029
+ * with them: a program that keeps a copy of either, to start the next
1030
+ * server with, wipes its own copy. The keys are zeroed in place, so give
1031
+ * each server a `QuicServerConfig` of its own: any other listener,
1032
+ * connection or server made with the same one would go on MACing Retry
1033
+ * tokens and deriving stateless reset tokens under an all-zero key, which
1034
+ * anyone can forge.
1035
+ */
1036
+ stop(): void {
1037
+ this.stopped = true
1038
+ for (let slot: i32 = 0; slot < toI32(this.quics.length); slot++) {
1039
+ if (this.holds(slot)) {
1040
+ this.release(slot)
1041
+ } else {
1042
+ this.conns[slot].release()
1043
+ }
1044
+ }
1045
+ this.listener.release()
1046
+ secureZero(this.quicConfig.statelessResetKey)
1047
+ secureZero(this.quicConfig.retryTokenKey)
1048
+ secureZero(this.index.salt)
1049
+ secureZero(this.entropy)
1050
+ this.answer.wipe()
1051
+ }
1052
+
867
1053
  /** Runs the timers due by `now`, and marks each slot it ran for `flush`. */
868
1054
  tick(now: i64): void {
869
1055
  const wheel: Http3Wheel = this.wheel