@amritk/nish-x86_64-linux 0.18.0 → 0.21.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 (104) hide show
  1. package/INSTALL.md +58 -28
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +3 -2
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +99 -5
  22. package/runtime/nish.h +289 -19
  23. package/runtime/nish.mjs +39 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-aes.c +490 -0
  26. package/runtime/runtime-bytes.c +401 -0
  27. package/runtime/runtime-cross.c +366 -0
  28. package/runtime/runtime-host.c +8 -7
  29. package/runtime/runtime-net.c +93 -34
  30. package/runtime/runtime-os.c +214 -16
  31. package/runtime/runtime-parallel.c +590 -107
  32. package/runtime/runtime-simd.c +235 -11
  33. package/runtime/runtime-term.c +504 -0
  34. package/runtime/runtime-wasm.c +5 -14
  35. package/runtime/runtime.c +257 -17
  36. package/runtime/shim.mjs +6 -915
  37. package/runtime/sqlite/LICENSE.md +85 -0
  38. package/runtime/sqlite/README.md +45 -0
  39. package/runtime/sqlite/nish-sqlite.c +359 -0
  40. package/runtime/sqlite/sqlite3.c +271131 -0
  41. package/runtime/sqlite/sqlite3.h +14447 -0
  42. package/scripts/build.sh +437 -28
  43. package/std/LICENSE-fast-float +23 -0
  44. package/std/LICENSE-fdlibm +8 -0
  45. package/std/README.md +108 -58
  46. package/std/base64.ts +116 -0
  47. package/std/bytes.ts +419 -0
  48. package/std/cli.ts +328 -0
  49. package/std/collections.ts +6 -5
  50. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  51. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  52. package/std/crypto/aes.ts +238 -76
  53. package/std/crypto/base64url.ts +125 -0
  54. package/std/crypto/bcrypt.ts +760 -0
  55. package/std/crypto/chacha20poly1305.ts +43 -9
  56. package/std/crypto/ed25519.ts +840 -0
  57. package/std/crypto/hkdf.ts +304 -12
  58. package/std/crypto/hmac.ts +382 -54
  59. package/std/crypto/p256.ts +932 -61
  60. package/std/crypto/x25519.ts +55 -37
  61. package/std/crypto/x509-random.ts +71 -0
  62. package/std/crypto/x509.ts +20 -131
  63. package/std/json-tokenizer.ts +4237 -0
  64. package/std/json-typed.ts +1014 -0
  65. package/std/json-value.ts +1182 -0
  66. package/std/json.ts +1202 -34
  67. package/std/math.ts +608 -0
  68. package/std/net/buffer-pool.ts +151 -0
  69. package/std/net/hpack.ts +416 -112
  70. package/std/net/http-fields.ts +93 -0
  71. package/std/net/http1-server.ts +501 -62
  72. package/std/net/http1.ts +31 -6
  73. package/std/net/http2-tls.ts +112 -24
  74. package/std/net/http2.ts +333 -75
  75. package/std/net/http3-server.ts +261 -43
  76. package/std/net/http3.ts +187 -28
  77. package/std/net/postgres.ts +1875 -0
  78. package/std/net/prefork.ts +412 -0
  79. package/std/net/quic-listener.ts +108 -11
  80. package/std/net/quic-packet.ts +243 -82
  81. package/std/net/quic-recovery.ts +361 -30
  82. package/std/net/quic-stream.ts +424 -24
  83. package/std/net/quic.ts +519 -167
  84. package/std/net/router.ts +676 -0
  85. package/std/net/tls/codec.ts +426 -27
  86. package/std/net/tls/record-server.ts +478 -132
  87. package/std/net/tls/record.ts +133 -42
  88. package/std/net/tls/schedule.ts +464 -55
  89. package/std/net/tls/ticket.ts +610 -0
  90. package/std/net/tls-tcp.ts +196 -53
  91. package/std/net/tls.ts +1018 -260
  92. package/std/net/url.ts +571 -0
  93. package/std/random.ts +94 -0
  94. package/std/regex.ts +4274 -0
  95. package/std/sqlite.ts +391 -0
  96. package/std/term-io.ts +978 -0
  97. package/std/term-pixels.ts +874 -0
  98. package/std/term-probe.ts +459 -0
  99. package/std/term-rate.ts +993 -0
  100. package/std/term.ts +2453 -0
  101. package/std/text.ts +90 -7
  102. package/std/threads.ts +438 -14
  103. package/std/yaml-lines.ts +141 -0
  104. package/std/yaml.ts +4675 -0
@@ -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,18 @@
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
+ * Each flight goes with the ECN codepoint its connection marked it with
45
+ * (`QuicFlight.ecn`, RFC 9000 §13.4), so the peer's ACK_ECN counts are
46
+ * validated and a CE mark shrinks the window. Where the platform sends no
47
+ * ECN (Darwin, whose `udpSendTo` answers -95 to a marked send), the first
48
+ * marked send goes again unmarked and no slot marks after it. A -95 is also
49
+ * what a platform without segmentation offload answers, so a refused GSO
50
+ * send goes again one datagram at a time, still marked, and only a -95 to
51
+ * one of those turns marking off.
52
+ * The pacer counts the microseconds `clock` gives it, so on a path whose
53
+ * round trip is under a millisecond it neither waits for the next
54
+ * millisecond to earn credit nor holds a slot back for a whole one; without
55
+ * `clock` it counts whole milliseconds.
41
56
  * A slot whose HTTP/3 side sent GOAWAY and whose requests have all finished,
42
57
  * every byte acknowledged, is closed with H3_NO_ERROR; a closed slot is freed
43
58
  * once its CONNECTION_CLOSE is out.
@@ -51,11 +66,21 @@
51
66
  *
52
67
  * **Caps, fixed at start-up**: the slots, and inside each the QUIC
53
68
  * configuration's streams and buffers and the `Http3Config`'s field section,
54
- * body chunk and DATA frame. The constructor makes every slot's connections;
69
+ * body chunk and DATA frame. A slot's connections and address are built on
70
+ * the first Initial accepted into it and kept, so a pool sized for its peak
71
+ * costs at start-up only its per-slot tables (the index, the wheel, the
72
+ * queues), and holds at most `size` connections once the peak has come;
55
73
  * a datagram an established connection reads, and every datagram it sends,
56
74
  * allocates nothing; nor does a datagram no slot owns, which the listener
57
- * reads where it lies and answers into scratch made once, nor a handshake,
58
- * whose state is the slot's — `docs/security/http3.md` (H3-1, H3-3).
75
+ * reads where it lies and answers into scratch made once, nor a handshake
76
+ * into a slot built before, whose state is the slot's —
77
+ * `docs/security/http3.md` (H3-1, H3-3).
78
+ *
79
+ * **The Date.** Every response carries one `date` field (RFC 9110 §6.6.1)
80
+ * from the pool's `HttpDate`, which formats the IMF-fixdate at most once a
81
+ * second for every slot; trailers carry none, and a program that sends its
82
+ * own `date` sends that one instead. `server.date.pin(unixMs)` fixes it, for
83
+ * a test that compares bytes.
59
84
  *
60
85
  * Written from RFC 9114 §3 and RFC 9000 §5.2, not ported from another
61
86
  * implementation. Private names carry the `http3Server` prefix
@@ -63,6 +88,8 @@
63
88
  */
64
89
  import { udpRecvFrom, udpSendTo } from "nish:net"
65
90
  import { Secret } from "nish:secret"
91
+ import { BufferPool } from "nish/net/buffer-pool"
92
+ import { HttpDate } from "nish/net/http1-server"
66
93
  import { Http3Config, Http3Connection } from "nish/net/http3"
67
94
  import { H3_NO_ERROR, h3Count } from "nish/net/http3-frame"
68
95
  import {
@@ -70,10 +97,13 @@ import {
70
97
  QUIC_CONN_DATAGRAM_SIZE,
71
98
  QUIC_CONN_ENTROPY_SIZE,
72
99
  QUIC_CONN_LOCAL_CIDS,
100
+ QUIC_CONN_MAX_STREAM_DATA,
101
+ QUIC_CONN_MAX_STREAMS,
73
102
  QuicConnection,
74
103
  QuicServerConfig,
75
104
  } from "nish/net/quic"
76
105
  import { QUIC_MAX_CID_LENGTH } from "nish/net/quic-packet"
106
+ import { QuicStreamPools, quicStreamGrowFree } from "nish/net/quic-stream"
77
107
  import {
78
108
  QUIC_LISTEN_ACCEPT,
79
109
  QUIC_LISTENER_ANSWER_SIZE,
@@ -82,7 +112,7 @@ import {
82
112
  QuicFlight,
83
113
  QuicListener,
84
114
  QuicListenerAnswer,
85
- quicListenerPaceTime,
115
+ quicListenerFlightTime,
86
116
  quicListenerTakeFlight,
87
117
  } from "nish/net/quic-listener"
88
118
  import { TlsP256Signer } from "nish/net/tls"
@@ -100,8 +130,8 @@ export const H3_SERVER_ANSWER_INTERVAL: i64 = 100
100
130
  /** The address form `nish:net` reads and writes. */
101
131
  const H3_SERVER_ADDRESS: i32 = 18
102
132
 
103
- /** What `udpSendTo` answers where the platform has no segmentation offload. */
104
- const H3_SERVER_NO_GSO: i32 = -95
133
+ /** What `udpSendTo` answers where the platform has no segmentation offload, or sends no ECN. */
134
+ const H3_SERVER_UNSUPPORTED: i32 = -95
105
135
 
106
136
  /** The largest datagram read: a GRO receive of up to 64 KB. */
107
137
  const H3_SERVER_RECEIVE_SIZE: i32 = 65536
@@ -397,14 +427,29 @@ export class Http3Wheel {
397
427
  */
398
428
  export class Http3Server {
399
429
  quicConfig: QuicServerConfig
430
+ /** The HTTP/3 configuration each slot's `Http3Connection` is built with. */
431
+ config: Http3Config
400
432
  listener: QuicListener
401
433
  /** What the listener answers into, made once: its reply and IDs are refilled in their own room. */
402
434
  answer: QuicListenerAnswer
435
+ /** The slots built so far, 0 up: each built on the first Initial accepted into it, or the first ask for it. */
403
436
  quics: QuicConnection[]
404
437
  conns: Http3Connection[]
438
+ /**
439
+ * The `date` every slot's responses carry, formatted once a second for all
440
+ * of them: each slot's `Http3Connection.date` is its bytes, refreshed by
441
+ * `connection` and `ready`. `date.pin` fixes its clock.
442
+ */
443
+ date: HttpDate
405
444
  index: Http3CidIndex
406
445
  wheel: Http3Wheel
407
- /** Each slot's client address, as its first datagram came from. */
446
+ /**
447
+ * Each slot's client address, as its first datagram came from. Not
448
+ * validated until `quic(slot).addressValidated` is set (RFC 9000 §8.1): a
449
+ * Retry token the listener checked, with `retry` on, or a Handshake packet
450
+ * from the client. Before then it is the source of one UDP datagram, which
451
+ * anyone can forge, so a cap or an allow-list keyed on it waits for that.
452
+ */
408
453
  addresses: u8[][]
409
454
  /** What `receive` reads into, and where `flush` writes a flight. */
410
455
  rx: u8[]
@@ -421,17 +466,25 @@ export class Http3Server {
421
466
  /** The slots with datagrams for the program, a ring, and the slots with something to send, a stack. */
422
467
  readyRing: i32[]
423
468
  dirtyStack: i32[]
424
- /** Free slots, a stack. */
469
+ /** Released slots, a stack: a slot never built is taken from `used` instead. */
425
470
  free: i32[]
426
471
  /** Per slot: 1 while on the ready ring, 2 while on the dirty stack. */
427
472
  queued: i32[]
428
473
  /** When the answer budget was last topped up, -1 before the first answer. */
429
474
  answerRefilled: i64 = -1
475
+ /** The caller's clock in microseconds, from `clock`, or -1: what each connection's pacer counts. */
476
+ micros: i64 = -1
430
477
  fd: i32 = -1
431
478
  readyHead: i32 = 0
432
479
  readyCount: i32 = 0
433
480
  dirtyCount: i32 = 0
434
481
  freeCount: i32 = 0
482
+ /** How many slots the pool may hold, which `size` answers. */
483
+ slots: i32 = 1
484
+ /** The slots ever accepted into, 0 up: one past it has never held a connection, so is free without being on `free`. */
485
+ used: i32 = 0
486
+ /** Set by `stop`: a slot built after it is released as it is made, so it holds no keys. */
487
+ stopped: boolean = false
435
488
  /** What is left of the answer budget (see `answerRefilled`). */
436
489
  answerBudget: i32 = 0
437
490
  /** 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. */
@@ -439,34 +492,77 @@ export class Http3Server {
439
492
  refused: i32 = 0
440
493
  limited: i32 = 0
441
494
  sendErrors: i32 = 0
495
+ /** Whether the platform sends ECN marks: true until a marked send answers -95, and every slot's `ecnMarking` with it. */
496
+ ecn: boolean = true
497
+ /**
498
+ * The stream buffers every slot's connection borrows from, made by the
499
+ * constructor for the stream table `quicConfig` gives every slot: `null`
500
+ * when its streams have none. Each pool makes a buffer only when a stream
501
+ * first needs one, so an idle server holds none (QUIC-3). It is made even
502
+ * for a configuration `quicConnConfigFits` refuses; `build` shares it only
503
+ * with a connection whose stream buffers are its size.
504
+ */
505
+ streamPools: QuicStreamPools | null = null
506
+ /** The `bodyChunk`-byte read buffers every slot's `Http3Connection` borrows, one at most each. */
507
+ dataPool: BufferPool
508
+ /** The `maxFieldSectionSize`-byte HEADERS buffers every slot's `Http3Connection` borrows around a decode. */
509
+ sectionPool: BufferPool
442
510
 
443
511
  /**
444
512
  * A pool of `size` slots serving HTTP/3 under `config` over QUIC under
445
513
  * `quicConfig` on the UDP socket `fd`. `listenerEntropy` is the
446
514
  * listener's `QUIC_LISTENER_ENTROPY_SIZE` random bytes, whose first eight
447
515
  * also key the connection-ID index (wiped in the caller's array, as the
448
- * listener does). Every slot's connections are made here.
516
+ * listener does). No slot's connections are made here: each slot's are
517
+ * built on the first Initial accepted into it. From here on
518
+ * `quicConfig`'s `statelessResetKey` and `retryTokenKey` are the server's:
519
+ * `stop` wipes them, so the configuration is this server's alone.
449
520
  */
450
521
  constructor(quicConfig: QuicServerConfig, config: Http3Config, fd: i32, size: i32, listenerEntropy: u8[]) {
451
522
  if (size < 1 || size > 65536) {
452
523
  panic(`Http3Server: ${size} slots, outside 1 to 65536`)
453
524
  }
454
525
  this.quicConfig = quicConfig
526
+ this.config = config
455
527
  this.fd = fd
528
+ this.slots = size
456
529
  this.index = new Http3CidIndex(size, listenerEntropy)
457
530
  this.listener = new QuicListener(quicConfig, listenerEntropy)
458
531
  this.answer = new QuicListenerAnswer(QUIC_LISTENER_ANSWER_SIZE)
459
532
  this.wheel = new Http3Wheel(size)
460
533
  this.quics = []
461
534
  this.conns = []
535
+ this.date = new HttpDate()
536
+ // Formatted once now, so no slot ever sends the bytes before a first format.
537
+ this.date.current()
462
538
  this.addresses = []
463
539
  this.entropy = new Array<u8>(QUIC_CONN_ENTROPY_SIZE)
464
- for (let k: i32 = 0; k < size; k++) {
465
- crypto.getRandomValues(this.entropy)
466
- const quic = new QuicConnection(quicConfig, this.entropy)
467
- this.quics.push(quic)
468
- this.conns.push(new Http3Connection(config, quic))
469
- this.addresses.push(new Array<u8>(H3_SERVER_ADDRESS))
540
+ this.dataPool = new BufferPool("Http3Server.data", config.bodyChunk, size)
541
+ this.sectionPool = new BufferPool("Http3Server.section", config.maxFieldSectionSize, size)
542
+ // Room for every buffer each pool can lend, so that giving one back never grows the arena.
543
+ const none: u8[] = []
544
+ quicStreamGrowFree(this.dataPool.free, size, none)
545
+ quicStreamGrowFree(this.sectionPool.free, size, none)
546
+ // The stream table every slot's connection will have, read from the
547
+ // configuration as `QuicConnection` reads it: a shape outside its bounds
548
+ // gets no pools, and a connection whose configuration does not fit has no
549
+ // table to share them with.
550
+ const streamData: i64 = quicConfig.maxStreamData
551
+ const bidi: i64 = quicConfig.maxStreamsBidi
552
+ const uni: i64 = quicConfig.maxStreamsUni
553
+ const local: i64 = quicConfig.localStreams
554
+ if (
555
+ streamData >= 1 &&
556
+ streamData <= QUIC_CONN_MAX_STREAM_DATA &&
557
+ bidi >= 0 &&
558
+ bidi <= QUIC_CONN_MAX_STREAMS &&
559
+ uni >= 0 &&
560
+ uni <= QUIC_CONN_MAX_STREAMS &&
561
+ local >= 0 &&
562
+ local <= QUIC_CONN_MAX_STREAMS &&
563
+ bidi + uni + local > 0
564
+ ) {
565
+ this.streamPools = new QuicStreamPools(toI32(streamData), toI32(bidi + uni + local), size)
470
566
  }
471
567
  this.rx = new Array<u8>(H3_SERVER_RECEIVE_SIZE)
472
568
  this.tx = new Array<u8>(QUIC_LISTENER_FLIGHT_MAX * QUIC_CONN_DATAGRAM_SIZE)
@@ -478,22 +574,58 @@ export class Http3Server {
478
574
  this.state = new Array<i32>(size)
479
575
  this.readyRing = new Array<i32>(size)
480
576
  this.dirtyStack = new Array<i32>(size)
577
+ // Released slots only: one never used is taken at `used`.
481
578
  this.free = new Array<i32>(size)
482
579
  this.queued = new Array<i32>(size)
483
- for (let k: i32 = size - 1; k >= 0; k--) {
484
- this.free[this.freeCount] = k
485
- this.freeCount = this.freeCount + 1
580
+ }
581
+
582
+ /**
583
+ * Builds the next slot, `quics.length`: its QUIC connection under fresh
584
+ * entropy, sharing `streamPools` when its stream table is the one they
585
+ * were made for, its HTTP/3 connection on the pool's buffers and `date`,
586
+ * and its address.
587
+ */
588
+ build(): void {
589
+ crypto.getRandomValues(this.entropy)
590
+ const quic = new QuicConnection(this.quicConfig, this.entropy)
591
+ quic.ecnMarking = this.ecn
592
+ this.quics.push(quic)
593
+ const pools: QuicStreamPools | null = this.streamPools
594
+ if (pools !== null && pools.data.size === quic.streams.bufferSize && quic.streams.slots.length > 0) {
595
+ quic.streams.share(pools)
596
+ }
597
+ const conn: Http3Connection = new Http3Connection(this.config, quic)
598
+ conn.share(this.dataPool, this.sectionPool)
599
+ conn.date = this.date.current()
600
+ this.conns.push(conn)
601
+ this.addresses.push(new Array<u8>(H3_SERVER_ADDRESS))
602
+ if (this.stopped) {
603
+ conn.release()
604
+ }
605
+ }
606
+
607
+ /** `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. */
608
+ slotAt(slot: i32): i32 {
609
+ const at: i32 = slot >= 0 && slot < this.slots ? slot : H3_SERVER_ZERO
610
+ while (toI32(this.quics.length) <= at) {
611
+ this.build()
486
612
  }
613
+ return at
614
+ }
615
+
616
+ /** Whether every slot holds a connection: none released, and none left never used. */
617
+ full(): boolean {
618
+ return this.freeCount <= 0 && this.used >= this.slots
487
619
  }
488
620
 
489
- /** How many slots the pool has. */
621
+ /** How many slots the pool has, built or not. */
490
622
  size(): i32 {
491
- return toI32(this.quics.length)
623
+ return this.slots
492
624
  }
493
625
 
494
626
  /** How many slots hold a connection. */
495
627
  busy(): i32 {
496
- return this.size() - this.freeCount
628
+ return this.used - this.freeCount
497
629
  }
498
630
 
499
631
  /** Whether `slot` holds a connection. */
@@ -501,16 +633,20 @@ export class Http3Server {
501
633
  return slot >= 0 && slot < toI32(this.state.length) && this.state[slot] === H3_SERVER_BUSY
502
634
  }
503
635
 
504
- /** The HTTP/3 connection of `slot`; a slot out of range names the first. */
636
+ /**
637
+ * The HTTP/3 connection of `slot`, its `date` brought up to now; a slot out
638
+ * of range names the first. A slot not built yet is built here, with every
639
+ * slot before it, so a program that binds something to each slot's
640
+ * connection up front gets the one its slot keeps.
641
+ */
505
642
  connection(slot: i32): Http3Connection {
506
- const at: i32 = slot >= 0 && slot < toI32(this.conns.length) ? slot : H3_SERVER_ZERO
507
- return this.conns[at]
643
+ this.date.current()
644
+ return this.conns[this.slotAt(slot)]
508
645
  }
509
646
 
510
- /** The QUIC connection of `slot`; a slot out of range names the first. */
647
+ /** The QUIC connection of `slot`, built as `connection` builds it; a slot out of range names the first. */
511
648
  quic(slot: i32): QuicConnection {
512
- const at: i32 = slot >= 0 && slot < toI32(this.quics.length) ? slot : H3_SERVER_ZERO
513
- return this.quics[at]
649
+ return this.quics[this.slotAt(slot)]
514
650
  }
515
651
 
516
652
  /** Puts `slot` on the ready ring and the dirty stack, unless it is on them. */
@@ -538,6 +674,7 @@ export class Http3Server {
538
674
 
539
675
  /** The next slot with news for the program, or -1: call its connection's `next` until it has none. */
540
676
  ready(): i32 {
677
+ this.date.current()
541
678
  while (this.readyCount > 0) {
542
679
  const slot: i32 = this.readyRing[this.readyHead]
543
680
  this.readyHead = (this.readyHead + 1) % toI32(this.readyRing.length)
@@ -625,15 +762,23 @@ export class Http3Server {
625
762
  if (this.listener.handleWindow(buf, at, len, address, now, answer) !== QUIC_LISTEN_ACCEPT) {
626
763
  const reply: i32 = toI32(answer.reply.length)
627
764
  if (reply > 0) {
628
- this.send(answer.reply, H3_SERVER_ZERO, reply, address, H3_SERVER_ZERO)
765
+ this.send(answer.reply, H3_SERVER_ZERO, reply, address, H3_SERVER_ZERO, H3_SERVER_ZERO)
629
766
  }
630
767
  return
631
768
  }
632
- if (this.freeCount <= 0) {
769
+ // A slot released before is taken first, then the next never used, so the
770
+ // first accepts take slots 0, 1, 2 ... and a pool that never fills never
771
+ // builds its last slots.
772
+ let slot: i32 = 0
773
+ if (this.freeCount > 0) {
774
+ this.freeCount = this.freeCount - 1
775
+ slot = this.free[this.freeCount]
776
+ } else if (this.used < this.slots) {
777
+ slot = this.slotAt(this.used)
778
+ this.used = this.used + 1
779
+ } else {
633
780
  return
634
781
  }
635
- this.freeCount = this.freeCount - 1
636
- const slot: i32 = this.free[this.freeCount]
637
782
  crypto.getRandomValues(this.entropy)
638
783
  const quic: QuicConnection = this.quics[slot]
639
784
  quic.reset(this.entropy)
@@ -680,7 +825,7 @@ export class Http3Server {
680
825
  if ((first & 0x30) !== 0 || toI32(buf[at + 5]) < 8) {
681
826
  return false
682
827
  }
683
- if (this.freeCount <= 0) {
828
+ if (this.full()) {
684
829
  this.refused = h3Count(this.refused)
685
830
  return false
686
831
  }
@@ -713,9 +858,21 @@ export class Http3Server {
713
858
  return true
714
859
  }
715
860
 
861
+ /**
862
+ * Gives the server the caller's monotonic clock in microseconds: the
863
+ * instant the `now` of the calls that follow names, `monotonicNanos() /
864
+ * 1000` where `now` is `monotonicNanos() / 1000000`. The pacer counts it;
865
+ * every timer still counts `now`. One that is not inside `now`'s
866
+ * millisecond is not read.
867
+ */
868
+ clock(micros: i64): void {
869
+ this.micros = micros
870
+ }
871
+
716
872
  /** Hands `slot`'s connection a datagram, signs when the handshake asks, and refiles its IDs and timer. */
717
873
  serve(slot: i32, buf: u8[], at: i32, len: i32, now: i64, key: Secret<u8[]>): void {
718
874
  const quic: QuicConnection = this.quics[slot]
875
+ quic.recovery.micros = this.micros
719
876
  quic.receiveWindow(buf, at, len, now)
720
877
  const input: u8[] | null = quic.signatureInput()
721
878
  if (input !== null) {
@@ -758,21 +915,40 @@ export class Http3Server {
758
915
  }
759
916
  }
760
917
 
761
- /** Sends `buf[at .. at + len)` to `to`, cut into `segment`-byte datagrams when it is above 0; answers whether it went. */
762
- send(buf: u8[], at: i32, len: i32, to: u8[], segment: i32): boolean {
763
- const sent: i32 = udpSendTo(this.fd, buf, at, len, to, segment, H3_SERVER_ZERO)
918
+ /**
919
+ * Sends `buf[at .. at + len)` to `to` with the ECN codepoint `ecn`, cut
920
+ * into `segment`-byte datagrams when it is above 0; answers whether it went.
921
+ */
922
+ send(buf: u8[], at: i32, len: i32, to: u8[], segment: i32, ecn: i32): boolean {
923
+ const sent: i32 = udpSendTo(this.fd, buf, at, len, to, segment, ecn)
764
924
  if (sent >= 0) {
765
925
  return true
766
926
  }
767
- if (sent !== H3_SERVER_NO_GSO || segment <= 0) {
927
+ if (sent !== H3_SERVER_UNSUPPORTED || (segment <= 0 && ecn === 0)) {
768
928
  this.sendErrors = h3Count(this.sendErrors)
769
929
  return false
770
930
  }
771
- // No segmentation offload here: one datagram at a time.
931
+ // -95: no segmentation offload, or no ECN, or both. One datagram at a
932
+ // time, still marked: a -95 to a datagram with no segment is the mark's.
933
+ let mark: i32 = ecn
934
+ if (segment <= 0) {
935
+ this.unmark()
936
+ mark = H3_SERVER_ZERO
937
+ }
938
+ const step: i32 = segment > 0 ? segment : len
772
939
  let p: i32 = at
773
940
  while (p < at + len) {
774
- const n: i32 = at + len - p < segment ? at + len - p : segment
775
- if (udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, H3_SERVER_ZERO) < 0) {
941
+ const n: i32 = at + len - p < step ? at + len - p : step
942
+ let one: i32 = udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, mark)
943
+ if (one === H3_SERVER_UNSUPPORTED && mark !== 0) {
944
+ // No ECN here: this datagram and the rest go unmarked and no slot
945
+ // marks again. The packets just counted as marked fail their
946
+ // connection's validation.
947
+ this.unmark()
948
+ mark = H3_SERVER_ZERO
949
+ one = udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, mark)
950
+ }
951
+ if (one < 0) {
776
952
  this.sendErrors = h3Count(this.sendErrors)
777
953
  return false
778
954
  }
@@ -781,6 +957,14 @@ export class Http3Server {
781
957
  return true
782
958
  }
783
959
 
960
+ /** The platform sends no ECN: no slot's connection marks from now on. */
961
+ unmark(): void {
962
+ this.ecn = false
963
+ for (let slot: i32 = 0; slot < toI32(this.quics.length); slot++) {
964
+ this.quics[slot].ecnMarking = false
965
+ }
966
+ }
967
+
784
968
  /**
785
969
  * Sends what every touched slot has, as far as its pacer lets it, closes a
786
970
  * slot whose HTTP/3 side is done after GOAWAY, frees a slot whose
@@ -801,6 +985,7 @@ export class Http3Server {
801
985
  /** `flush` of one slot. */
802
986
  flushSlot(slot: i32, now: i64): void {
803
987
  const quic: QuicConnection = this.quics[slot]
988
+ quic.recovery.micros = this.micros
804
989
  if (this.conns[slot].isDone() && !quic.closed()) {
805
990
  quic.close(H3_NO_ERROR)
806
991
  }
@@ -825,7 +1010,7 @@ export class Http3Server {
825
1010
  break
826
1011
  }
827
1012
  const segment: i32 = this.flight.count > 1 ? this.flight.segment : H3_SERVER_ZERO
828
- if (!this.send(this.tx, H3_SERVER_ZERO, n, to, segment)) {
1013
+ if (!this.send(this.tx, H3_SERVER_ZERO, n, to, segment, this.flight.ecn)) {
829
1014
  break
830
1015
  }
831
1016
  retried = false
@@ -834,7 +1019,7 @@ export class Http3Server {
834
1019
  // The CONNECTION_CLOSE goes whatever the pacer says: nothing follows it.
835
1020
  const n: i32 = quic.takeDatagramInto(this.tx, H3_SERVER_ZERO, now)
836
1021
  if (n > 0) {
837
- this.send(this.tx, H3_SERVER_ZERO, n, to, H3_SERVER_ZERO)
1022
+ this.send(this.tx, H3_SERVER_ZERO, n, to, H3_SERVER_ZERO, quic.datagramEcn)
838
1023
  }
839
1024
  this.release(slot)
840
1025
  return
@@ -843,7 +1028,7 @@ export class Http3Server {
843
1028
  // The pacer answers a time, not a delay: the slot wakes at it when it
844
1029
  // comes before the connection's own deadline.
845
1030
  let due: i64 = quic.deadline()
846
- const paced: i64 = quicListenerPaceTime(quic, now)
1031
+ const paced: i64 = quicListenerFlightTime(quic, now)
847
1032
  if (paced > now && (due < 0 || paced < due)) {
848
1033
  due = paced
849
1034
  }
@@ -862,7 +1047,7 @@ export class Http3Server {
862
1047
  release(slot: i32): void {
863
1048
  this.unindex(slot)
864
1049
  this.wheel.unfile(slot)
865
- this.quics[slot].release()
1050
+ this.conns[slot].release()
866
1051
  this.state[slot] = H3_SERVER_FREE
867
1052
  if (this.freeCount < toI32(this.free.length)) {
868
1053
  this.free[this.freeCount] = slot
@@ -870,6 +1055,39 @@ export class Http3Server {
870
1055
  }
871
1056
  }
872
1057
 
1058
+ /**
1059
+ * Stops the server for good and wipes the keys it holds: every built slot's
1060
+ * connection is released (`QuicConnection.release`, which sends nothing),
1061
+ * as is one built after, by `connection` or `quic`; the listener is
1062
+ * released; and the configuration's `statelessResetKey` and
1063
+ * `retryTokenKey`, the index's salt, the connections' entropy scratch
1064
+ * and the listener's last answer (a Retry's or a reset's token) are
1065
+ * zeroed. From then on every datagram is dropped and no connection is
1066
+ * accepted. The configuration's two keys are the server's once it is made
1067
+ * with them: a program that keeps a copy of either, to start the next
1068
+ * server with, wipes its own copy. The keys are zeroed in place, so give
1069
+ * each server a `QuicServerConfig` of its own: any other listener,
1070
+ * connection or server made with the same one would go on MACing Retry
1071
+ * tokens and deriving stateless reset tokens under an all-zero key, which
1072
+ * anyone can forge.
1073
+ */
1074
+ stop(): void {
1075
+ this.stopped = true
1076
+ for (let slot: i32 = 0; slot < toI32(this.quics.length); slot++) {
1077
+ if (this.holds(slot)) {
1078
+ this.release(slot)
1079
+ } else {
1080
+ this.conns[slot].release()
1081
+ }
1082
+ }
1083
+ this.listener.release()
1084
+ secureZero(this.quicConfig.statelessResetKey)
1085
+ secureZero(this.quicConfig.retryTokenKey)
1086
+ secureZero(this.index.salt)
1087
+ secureZero(this.entropy)
1088
+ this.answer.wipe()
1089
+ }
1090
+
873
1091
  /** Runs the timers due by `now`, and marks each slot it ran for `flush`. */
874
1092
  tick(now: i64): void {
875
1093
  const wheel: Http3Wheel = this.wheel