@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 (58) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +50 -13
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-simd.c +203 -0
  11. package/runtime/runtime-wasm.c +12 -0
  12. package/runtime/runtime.c +66 -18
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +14 -12
  15. package/std/README.md +168 -36
  16. package/std/crypto/aes.ts +51 -8
  17. package/std/crypto/base64url.ts +25 -12
  18. package/std/crypto/chacha20poly1305.ts +43 -29
  19. package/std/crypto/ct.ts +6 -1
  20. package/std/crypto/hkdf.ts +364 -3
  21. package/std/crypto/hmac.ts +307 -7
  22. package/std/crypto/p256.ts +618 -34
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +3 -2
  25. package/std/crypto/x25519.ts +88 -36
  26. package/std/crypto/x509.ts +130 -47
  27. package/std/json.ts +297 -41
  28. package/std/net/hpack.ts +1094 -0
  29. package/std/net/http-fields.ts +604 -0
  30. package/std/net/http1-server.ts +1389 -0
  31. package/std/net/http1.ts +1492 -0
  32. package/std/net/http2-frame.ts +745 -0
  33. package/std/net/http2-tls.ts +216 -0
  34. package/std/net/http2.ts +1433 -0
  35. package/std/net/http3-frame.ts +457 -0
  36. package/std/net/http3-server.ts +911 -0
  37. package/std/net/http3.ts +2145 -0
  38. package/std/net/qpack.ts +1163 -0
  39. package/std/net/quic-conn-ack.ts +206 -0
  40. package/std/net/quic-conn-cid.ts +425 -0
  41. package/std/net/quic-conn-params.ts +462 -0
  42. package/std/net/quic-datagram.ts +130 -0
  43. package/std/net/quic-frame.ts +1206 -0
  44. package/std/net/quic-listener.ts +929 -0
  45. package/std/net/quic-packet.ts +1698 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1609 -0
  48. package/std/net/quic.ts +3578 -0
  49. package/std/net/tls/codec.ts +1089 -0
  50. package/std/net/tls/record-server.ts +1173 -0
  51. package/std/net/tls/record.ts +621 -0
  52. package/std/net/tls/schedule.ts +429 -0
  53. package/std/net/tls-tcp.ts +414 -0
  54. package/std/net/tls.ts +1437 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
  58. package/std/text.ts +77 -0
@@ -0,0 +1,1206 @@
1
+ /**
2
+ * `nish/net/quic-frame` — the frames of QUIC version 1 (RFC 9000 §19) and
3
+ * RFC 9221's DATAGRAM, read from a decrypted packet payload and written into
4
+ * one, and the transport error codes a connection closes with (§20.1).
5
+ *
6
+ * import { QuicFrame, quicParseFrame, quicPushAck, QUIC_FRAME_CRYPTO } from "nish/net/quic-frame";
7
+ *
8
+ * const frame = new QuicFrame(); // one per connection, reused
9
+ * let at: i32 = 0;
10
+ * while (at < toI32(payload.length)) {
11
+ * const error: i64 = quicParseFrame(frame, payload, at, toI32(payload.length));
12
+ * if (error !== 0) { … close the connection with `error` … }
13
+ * if (frame.type === QUIC_FRAME_CRYPTO) { … payload[frame.dataStart .. + frame.dataLength) … }
14
+ * at = frame.end;
15
+ * }
16
+ *
17
+ * **Every frame type of RFC 9000 is read**, 0x00 to 0x1e, and RFC 9221's
18
+ * DATAGRAM, 0x30 and 0x31, so a type outside those is the one parse error a
19
+ * well-formed packet cannot produce. What
20
+ * a frame *means* is `nish/net/quic`'s: this module checks only what §19 says
21
+ * of the encoding itself, and answers the transport error the RFC names for
22
+ * each break — `QUIC_ERROR_FRAME_ENCODING` for a truncated field, a length
23
+ * past the packet, an ACK range below packet number zero, an offset past
24
+ * 2^62 − 1, a stream count past 2^60, an empty NEW_TOKEN, a connection ID
25
+ * outside 1 to 20 bytes or a Retire Prior To above its own sequence number;
26
+ * `QUIC_ERROR_PROTOCOL_VIOLATION` for a frame type spelled in more bytes than
27
+ * it needs (§12.4). Every read is bounds-checked against the window the caller
28
+ * gives, so nothing a peer sends can make this module panic.
29
+ *
30
+ * **No copy of the bulk.** A CRYPTO, STREAM or DATAGRAM frame's data, a
31
+ * NEW_TOKEN's token, a PATH_CHALLENGE's eight bytes and a CONNECTION_CLOSE's
32
+ * reason are left in the payload and described by `dataStart` and
33
+ * `dataLength`, and a NEW_CONNECTION_ID's ID and token by windows of their
34
+ * own, so a connection reassembles straight from the packet. One `QuicFrame`
35
+ * is reused for every frame of every packet: `quicParseFrame` overwrites
36
+ * every field it reads and resets the rest, and stores nothing but numbers,
37
+ * so a payload made inside an arena block can be read inside it.
38
+ *
39
+ * **The writers** come in two forms. A `put` writer writes one frame into a
40
+ * caller's buffer at an offset, before an end, and answers the offset past
41
+ * it, or -1 with nothing written; a `push` writer appends one to an array
42
+ * and answers whether it could, by growing the array and calling the `put`
43
+ * form, so each frame has one encoder. Either refuses a value no varint
44
+ * holds or a window outside the array given. Their inputs are this
45
+ * endpoint's own, never a peer's. `quicFrameAllowed` is RFC 9000 §12.4's
46
+ * table of which frames each packet type may carry.
47
+ *
48
+ * Written from RFC 9000 §12.4, §19 and §20 and RFC 9221 §4, in this module's own structure;
49
+ * nothing here is ported from another implementation. Private names carry the
50
+ * `quicFrame` prefix because a `std/` module's private functions share the
51
+ * importing program's flat symbol namespace (`docs/wp26-stdlib.md` §3e).
52
+ */
53
+ import {
54
+ QUIC_MAX_CID_LENGTH,
55
+ QUIC_MAX_VARINT,
56
+ QUIC_PACKET_HANDSHAKE,
57
+ QUIC_PACKET_INITIAL,
58
+ QUIC_PACKET_SHORT,
59
+ QUIC_PACKET_ZERO_RTT,
60
+ quicPacketCopy,
61
+ quicPacketGrow,
62
+ quicVarintLength,
63
+ quicVarintPut,
64
+ quicVarintRead,
65
+ quicVarintSize,
66
+ } from "nish/net/quic-packet"
67
+
68
+ // ---- Frame types (RFC 9000 §19, Table 3 of §12.4) ------------------------------
69
+
70
+ export const QUIC_FRAME_PADDING: i32 = 0x00
71
+ export const QUIC_FRAME_PING: i32 = 0x01
72
+ /** ACK without ECN counts. */
73
+ export const QUIC_FRAME_ACK: i32 = 0x02
74
+ /** ACK with the three ECN counts after the ranges. */
75
+ export const QUIC_FRAME_ACK_ECN: i32 = 0x03
76
+ export const QUIC_FRAME_RESET_STREAM: i32 = 0x04
77
+ export const QUIC_FRAME_STOP_SENDING: i32 = 0x05
78
+ export const QUIC_FRAME_CRYPTO: i32 = 0x06
79
+ export const QUIC_FRAME_NEW_TOKEN: i32 = 0x07
80
+ /**
81
+ * The first of the eight STREAM types, 0x08 to 0x0f. `quicParseFrame` answers
82
+ * every one of them as this type, with the three low bits read into
83
+ * `offset`, `dataLength` and `fin` (§19.8).
84
+ */
85
+ export const QUIC_FRAME_STREAM: i32 = 0x08
86
+ export const QUIC_FRAME_MAX_DATA: i32 = 0x10
87
+ export const QUIC_FRAME_MAX_STREAM_DATA: i32 = 0x11
88
+ export const QUIC_FRAME_MAX_STREAMS_BIDI: i32 = 0x12
89
+ export const QUIC_FRAME_MAX_STREAMS_UNI: i32 = 0x13
90
+ export const QUIC_FRAME_DATA_BLOCKED: i32 = 0x14
91
+ export const QUIC_FRAME_STREAM_DATA_BLOCKED: i32 = 0x15
92
+ export const QUIC_FRAME_STREAMS_BLOCKED_BIDI: i32 = 0x16
93
+ export const QUIC_FRAME_STREAMS_BLOCKED_UNI: i32 = 0x17
94
+ export const QUIC_FRAME_NEW_CONNECTION_ID: i32 = 0x18
95
+ export const QUIC_FRAME_RETIRE_CONNECTION_ID: i32 = 0x19
96
+ export const QUIC_FRAME_PATH_CHALLENGE: i32 = 0x1a
97
+ export const QUIC_FRAME_PATH_RESPONSE: i32 = 0x1b
98
+ /** CONNECTION_CLOSE with a transport error code and the frame type that caused it. */
99
+ export const QUIC_FRAME_CONNECTION_CLOSE: i32 = 0x1c
100
+ /** CONNECTION_CLOSE with an application error code, allowed only in 1-RTT packets. */
101
+ export const QUIC_FRAME_CONNECTION_CLOSE_APP: i32 = 0x1d
102
+ export const QUIC_FRAME_HANDSHAKE_DONE: i32 = 0x1e
103
+ /**
104
+ * DATAGRAM (RFC 9221 §4): an unreliable application payload. Type 0x30 runs
105
+ * to the end of the packet and 0x31 carries a Length; `quicParseFrame`
106
+ * answers both as this type, with `fin` set for 0x31 (it had a Length).
107
+ */
108
+ export const QUIC_FRAME_DATAGRAM: i32 = 0x30
109
+ /** The DATAGRAM type with a Length field (RFC 9221 §4), which `quicPutDatagram` always writes. */
110
+ export const QUIC_FRAME_DATAGRAM_LENGTH: i32 = 0x31
111
+
112
+ // ---- Transport error codes (RFC 9000 §20.1) ------------------------------------
113
+
114
+ export const QUIC_ERROR_NO_ERROR: i64 = 0x00
115
+ export const QUIC_ERROR_INTERNAL: i64 = 0x01
116
+ export const QUIC_ERROR_CONNECTION_REFUSED: i64 = 0x02
117
+ export const QUIC_ERROR_FLOW_CONTROL: i64 = 0x03
118
+ export const QUIC_ERROR_STREAM_LIMIT: i64 = 0x04
119
+ export const QUIC_ERROR_STREAM_STATE: i64 = 0x05
120
+ export const QUIC_ERROR_FINAL_SIZE: i64 = 0x06
121
+ export const QUIC_ERROR_FRAME_ENCODING: i64 = 0x07
122
+ export const QUIC_ERROR_TRANSPORT_PARAMETER: i64 = 0x08
123
+ export const QUIC_ERROR_CONNECTION_ID_LIMIT: i64 = 0x09
124
+ export const QUIC_ERROR_PROTOCOL_VIOLATION: i64 = 0x0a
125
+ export const QUIC_ERROR_INVALID_TOKEN: i64 = 0x0b
126
+ export const QUIC_ERROR_APPLICATION: i64 = 0x0c
127
+ export const QUIC_ERROR_CRYPTO_BUFFER_EXCEEDED: i64 = 0x0d
128
+ export const QUIC_ERROR_KEY_UPDATE: i64 = 0x0e
129
+ export const QUIC_ERROR_AEAD_LIMIT_REACHED: i64 = 0x0f
130
+ export const QUIC_ERROR_NO_VIABLE_PATH: i64 = 0x10
131
+ /** CRYPTO_ERROR is this plus the TLS alert description (RFC 9001 §4.8): 0x0100 to 0x01ff. */
132
+ export const QUIC_ERROR_CRYPTO: i64 = 0x0100
133
+
134
+ /**
135
+ * The most ACK ranges a `QuicFrame` keeps. A frame may list more, and every
136
+ * one is still checked; the ones past this are counted in `ackRangeTotal`
137
+ * but not stored. Loss recovery, which would read them, is WP34 Q3.
138
+ */
139
+ export const QUIC_FRAME_MAX_ACK_RANGES: i32 = 32
140
+
141
+ /** A stream count's ceiling, 2^60 (RFC 9000 §4.6, §19.11). Spelled as a product: an `i64` literal past 2^53 is refused. */
142
+ const QUIC_FRAME_MAX_STREAMS: i64 = 1073741824 * 1073741824
143
+
144
+ /** The length of a stateless reset token (RFC 9000 §10.3). */
145
+ export const QUIC_RESET_TOKEN_SIZE: i32 = 16
146
+
147
+ /** The length of a PATH_CHALLENGE's and a PATH_RESPONSE's data (RFC 9000 §19.17). */
148
+ export const QUIC_PATH_DATA_SIZE: i32 = 8
149
+
150
+ /**
151
+ * One frame as `quicParseFrame` read it. Which fields mean something depends
152
+ * on `type`; the rest hold zero, or empty arrays.
153
+ *
154
+ * - ACK and ACK_ECN: `largest`, `ackDelay` (still scaled by the peer's
155
+ * exponent), the ranges as `[smallest, largest]` pairs from the highest
156
+ * down in `ackRanges` (`ackRangeCount` pairs stored, `ackRangeTotal` read),
157
+ * and for ACK_ECN the counts `ect0`, `ect1` and `ce`.
158
+ * - CRYPTO: `offset` and the data `dataStart`, `dataLength`.
159
+ * - STREAM: `streamId`, `offset`, the data, and `fin`.
160
+ * - NEW_TOKEN and PATH_CHALLENGE / PATH_RESPONSE: the token or data.
161
+ * - RESET_STREAM: `streamId`, `errorCode`, the final size in `value`.
162
+ * - STOP_SENDING: `streamId`, `errorCode`.
163
+ * - MAX_DATA, MAX_STREAMS_*, DATA_BLOCKED, STREAMS_BLOCKED_*: `value`.
164
+ * - MAX_STREAM_DATA, STREAM_DATA_BLOCKED: `streamId` and `value`.
165
+ * - NEW_CONNECTION_ID: the sequence number in `value`, `retirePriorTo`,
166
+ * the ID's window `connectionIdStart` / `connectionIdLength` and the
167
+ * reset token's `resetTokenStart` (16 bytes) in the payload.
168
+ * RETIRE_CONNECTION_ID: `value`.
169
+ * - DATAGRAM: the payload as the data, and `fin` when the frame carried a
170
+ * Length (type 0x31).
171
+ * - CONNECTION_CLOSE: `errorCode`, `frameType` (0x1c only) and the reason
172
+ * phrase as the data.
173
+ */
174
+ export class QuicFrame {
175
+ streamId: i64 = 0
176
+ offset: i64 = 0
177
+ value: i64 = 0
178
+ errorCode: i64 = 0
179
+ frameType: i64 = 0
180
+ retirePriorTo: i64 = 0
181
+ largest: i64 = 0
182
+ ackDelay: i64 = 0
183
+ ackRanges: i64[]
184
+ ackRangeTotal: i64 = 0
185
+ ect0: i64 = 0
186
+ ect1: i64 = 0
187
+ ce: i64 = 0
188
+ /** The frame type, with every STREAM type answered as `QUIC_FRAME_STREAM`. */
189
+ type: i32 = 0
190
+ /** One past the frame's last byte, where the next frame starts. */
191
+ end: i32 = 0
192
+ dataStart: i32 = 0
193
+ dataLength: i32 = 0
194
+ ackRangeCount: i32 = 0
195
+ connectionIdStart: i32 = 0
196
+ connectionIdLength: i32 = 0
197
+ resetTokenStart: i32 = 0
198
+ fin: boolean = false
199
+
200
+ constructor() {
201
+ // Sized once, so reading an ACK stores nothing new into the frame.
202
+ this.ackRanges = new Array<i64>(QUIC_FRAME_MAX_ACK_RANGES * 2)
203
+ }
204
+ }
205
+
206
+ /**
207
+ * The cursor `quicParseFrame` reads with: a window of the payload, and
208
+ * `failed` once a read ran past it, after which every read fails too. So a
209
+ * frame's fields are read in order and `failed` tested once. It holds only
210
+ * positions; the payload is passed beside it, so reading a frame stores no
211
+ * pointer to it anywhere and a payload made inside an arena block can be
212
+ * read inside it.
213
+ */
214
+ class QuicFrameReader {
215
+ at: i32
216
+ end: i32
217
+ failed: boolean = false
218
+
219
+ constructor(at: i32, end: i32) {
220
+ this.at = at
221
+ this.end = end
222
+ }
223
+ }
224
+
225
+ /** The next varint, or 0 and a failed reader when it does not fit in the window. */
226
+ const quicFrameVarint = (r: QuicFrameReader, d: u8[]): i64 => {
227
+ if (r.failed) {
228
+ return 0
229
+ }
230
+ const value: i64 = quicVarintRead(d, r.at, r.end)
231
+ if (value < 0) {
232
+ r.failed = true
233
+ return 0
234
+ }
235
+ r.at = r.at + quicVarintLength(d, r.at)
236
+ return value
237
+ }
238
+
239
+ /**
240
+ * Skips `length` bytes, answering where they start; a length past the window
241
+ * fails the reader and answers the current position.
242
+ */
243
+ const quicFrameSkip = (r: QuicFrameReader, length: i64): i32 => {
244
+ const at: i32 = r.at
245
+ if (r.failed || length < 0 || length > toI64(r.end - at)) {
246
+ r.failed = true
247
+ return at
248
+ }
249
+ r.at = at + toI32(length)
250
+ return at
251
+ }
252
+
253
+ /** Stores the `index`-th ACK range pair; the caller keeps `index` under `QUIC_FRAME_MAX_ACK_RANGES`. */
254
+ const quicFrameStoreRange = (frame: QuicFrame, index: i32, smallest: i64, largest: i64): void => {
255
+ const at: i32 = index * 2
256
+ const ranges: i64[] = frame.ackRanges
257
+ if (at >= 0 && at + 1 < toI32(ranges.length)) {
258
+ ranges[at] = smallest
259
+ ranges[at + 1] = largest
260
+ }
261
+ }
262
+
263
+ /** Resets every field a previous frame may have set, so a reused `QuicFrame` holds only this one. */
264
+ const quicFrameReset = (frame: QuicFrame, at: i32): void => {
265
+ frame.type = 0
266
+ frame.end = at
267
+ frame.streamId = 0
268
+ frame.offset = 0
269
+ frame.value = 0
270
+ frame.errorCode = 0
271
+ frame.frameType = 0
272
+ frame.retirePriorTo = 0
273
+ frame.fin = false
274
+ frame.dataStart = at
275
+ frame.dataLength = 0
276
+ frame.largest = 0
277
+ frame.ackDelay = 0
278
+ frame.ackRangeCount = 0
279
+ frame.ackRangeTotal = 0
280
+ frame.ect0 = 0
281
+ frame.ect1 = 0
282
+ frame.ce = 0
283
+ frame.connectionIdStart = at
284
+ frame.connectionIdLength = 0
285
+ frame.resetTokenStart = at
286
+ }
287
+
288
+ /**
289
+ * The ranges of an ACK frame (§19.3.1): the first range down from `largest`,
290
+ * then each Gap and ACK Range Length pair below it. A range that reaches
291
+ * below packet number zero is `QUIC_ERROR_FRAME_ENCODING`. Answers 0 or that.
292
+ */
293
+ const quicFrameReadAck = (r: QuicFrameReader, d: u8[], frame: QuicFrame, ecn: boolean): i64 => {
294
+ frame.largest = quicFrameVarint(r, d)
295
+ frame.ackDelay = quicFrameVarint(r, d)
296
+ const count: i64 = quicFrameVarint(r, d)
297
+ const first: i64 = quicFrameVarint(r, d)
298
+ if (r.failed) {
299
+ return QUIC_ERROR_FRAME_ENCODING
300
+ }
301
+ let smallest: i64 = frame.largest - first
302
+ if (smallest < 0) {
303
+ return QUIC_ERROR_FRAME_ENCODING
304
+ }
305
+ quicFrameStoreRange(frame, 0, smallest, frame.largest)
306
+ let stored: i32 = 1
307
+ // Each further range takes at least two bytes, so the loop is bounded by the
308
+ // payload whatever count the peer claims: a short read ends it.
309
+ for (let k: i64 = 0; k < count; k += 1) {
310
+ const gap: i64 = quicFrameVarint(r, d)
311
+ const length: i64 = quicFrameVarint(r, d)
312
+ if (r.failed) {
313
+ return QUIC_ERROR_FRAME_ENCODING
314
+ }
315
+ // The next range's largest is two below this one's smallest, less the gap.
316
+ const top: i64 = smallest - gap - 2
317
+ const bottom: i64 = top - length
318
+ if (top < 0 || bottom < 0) {
319
+ return QUIC_ERROR_FRAME_ENCODING
320
+ }
321
+ if (stored < QUIC_FRAME_MAX_ACK_RANGES) {
322
+ quicFrameStoreRange(frame, stored, bottom, top)
323
+ stored = stored + 1
324
+ }
325
+ smallest = bottom
326
+ }
327
+ frame.ackRangeCount = stored
328
+ frame.ackRangeTotal = count + 1
329
+ if (ecn) {
330
+ frame.ect0 = quicFrameVarint(r, d)
331
+ frame.ect1 = quicFrameVarint(r, d)
332
+ frame.ce = quicFrameVarint(r, d)
333
+ }
334
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
335
+ }
336
+
337
+ /**
338
+ * The data of a CRYPTO or STREAM frame, `length` bytes from the reader's
339
+ * position; past the window, or reaching past offset 2^62 − 1 (§19.6,
340
+ * §19.8), it is `QUIC_ERROR_FRAME_ENCODING`.
341
+ */
342
+ const quicFrameReadData = (r: QuicFrameReader, frame: QuicFrame, length: i64): i64 => {
343
+ frame.dataStart = quicFrameSkip(r, length)
344
+ if (r.failed) {
345
+ return QUIC_ERROR_FRAME_ENCODING
346
+ }
347
+ frame.dataLength = toI32(length)
348
+ return frame.offset > QUIC_MAX_VARINT - length ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
349
+ }
350
+
351
+ /** STREAM (§19.8): the type's three low bits say whether Offset and Length are present and whether FIN is set. */
352
+ const quicFrameReadStream = (r: QuicFrameReader, d: u8[], frame: QuicFrame, bits: i32): i64 => {
353
+ frame.streamId = quicFrameVarint(r, d)
354
+ if ((bits & 0x04) !== 0) {
355
+ frame.offset = quicFrameVarint(r, d)
356
+ }
357
+ // Without a Length the data runs to the end of the packet.
358
+ const length: i64 = (bits & 0x02) !== 0 ? quicFrameVarint(r, d) : toI64(r.end - r.at)
359
+ frame.fin = (bits & 0x01) !== 0
360
+ if (r.failed) {
361
+ return QUIC_ERROR_FRAME_ENCODING
362
+ }
363
+ return quicFrameReadData(r, frame, length)
364
+ }
365
+
366
+ /** DATAGRAM (RFC 9221 §4): with a Length, that many bytes; without, the rest of the packet. */
367
+ const quicFrameReadDatagram = (r: QuicFrameReader, d: u8[], frame: QuicFrame, hasLength: boolean): i64 => {
368
+ const length: i64 = hasLength ? quicFrameVarint(r, d) : toI64(r.end - r.at)
369
+ frame.fin = hasLength
370
+ frame.dataStart = quicFrameSkip(r, length)
371
+ if (r.failed) {
372
+ return QUIC_ERROR_FRAME_ENCODING
373
+ }
374
+ frame.dataLength = toI32(length)
375
+ return QUIC_ERROR_NO_ERROR
376
+ }
377
+
378
+ /** NEW_CONNECTION_ID (§19.15): a sequence number, Retire Prior To, a 1 to 20 byte ID and a 16-byte reset token. */
379
+ const quicFrameReadNewConnectionId = (r: QuicFrameReader, d: u8[], frame: QuicFrame): i64 => {
380
+ frame.value = quicFrameVarint(r, d)
381
+ frame.retirePriorTo = quicFrameVarint(r, d)
382
+ const lengthAt: i32 = quicFrameSkip(r, 1)
383
+ if (r.failed || lengthAt < 0 || lengthAt >= toI32(d.length)) {
384
+ return QUIC_ERROR_FRAME_ENCODING
385
+ }
386
+ const length: i32 = toI32(d[lengthAt])
387
+ const cidAt: i32 = quicFrameSkip(r, toI64(length))
388
+ const tokenAt: i32 = quicFrameSkip(r, toI64(QUIC_RESET_TOKEN_SIZE))
389
+ if (r.failed || length < 1 || length > QUIC_MAX_CID_LENGTH || frame.retirePriorTo > frame.value) {
390
+ return QUIC_ERROR_FRAME_ENCODING
391
+ }
392
+ frame.connectionIdStart = cidAt
393
+ frame.connectionIdLength = length
394
+ frame.resetTokenStart = tokenAt
395
+ return QUIC_ERROR_NO_ERROR
396
+ }
397
+
398
+ /** A frame of one varint-length field followed by that many bytes, left in place: NEW_TOKEN's token, a close's reason. */
399
+ const quicFrameReadBytes = (r: QuicFrameReader, d: u8[], frame: QuicFrame): void => {
400
+ const length: i64 = quicFrameVarint(r, d)
401
+ frame.dataStart = quicFrameSkip(r, length)
402
+ frame.dataLength = r.failed ? 0 : toI32(length)
403
+ }
404
+
405
+ /**
406
+ * The body of a frame of type `type` (already read and known), from the
407
+ * reader's position. Answers 0, or the transport error the RFC names.
408
+ */
409
+ const quicFrameReadBody = (r: QuicFrameReader, d: u8[], frame: QuicFrame, type: i32): i64 => {
410
+ switch (type) {
411
+ case QUIC_FRAME_PADDING: {
412
+ // Padding is a run of zero bytes; read it as one frame, so a packet of
413
+ // a thousand padding bytes is one call rather than a thousand.
414
+ while (r.at < r.end && r.at >= 0 && r.at < toI32(d.length) && toI32(d[r.at]) === 0) {
415
+ r.at = r.at + 1
416
+ }
417
+ return QUIC_ERROR_NO_ERROR
418
+ }
419
+ case QUIC_FRAME_PING:
420
+ case QUIC_FRAME_HANDSHAKE_DONE:
421
+ return QUIC_ERROR_NO_ERROR
422
+ case QUIC_FRAME_ACK:
423
+ return quicFrameReadAck(r, d, frame, false)
424
+ case QUIC_FRAME_ACK_ECN:
425
+ return quicFrameReadAck(r, d, frame, true)
426
+ case QUIC_FRAME_RESET_STREAM: {
427
+ frame.streamId = quicFrameVarint(r, d)
428
+ frame.errorCode = quicFrameVarint(r, d)
429
+ frame.value = quicFrameVarint(r, d)
430
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
431
+ }
432
+ case QUIC_FRAME_STOP_SENDING: {
433
+ frame.streamId = quicFrameVarint(r, d)
434
+ frame.errorCode = quicFrameVarint(r, d)
435
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
436
+ }
437
+ case QUIC_FRAME_CRYPTO: {
438
+ frame.offset = quicFrameVarint(r, d)
439
+ const length: i64 = quicFrameVarint(r, d)
440
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : quicFrameReadData(r, frame, length)
441
+ }
442
+ case QUIC_FRAME_NEW_TOKEN: {
443
+ quicFrameReadBytes(r, d, frame)
444
+ // §19.7: an empty token is FRAME_ENCODING_ERROR.
445
+ return r.failed || frame.dataLength === 0 ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
446
+ }
447
+ case QUIC_FRAME_MAX_STREAM_DATA:
448
+ case QUIC_FRAME_STREAM_DATA_BLOCKED: {
449
+ frame.streamId = quicFrameVarint(r, d)
450
+ frame.value = quicFrameVarint(r, d)
451
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
452
+ }
453
+ case QUIC_FRAME_MAX_DATA:
454
+ case QUIC_FRAME_DATA_BLOCKED:
455
+ case QUIC_FRAME_RETIRE_CONNECTION_ID: {
456
+ frame.value = quicFrameVarint(r, d)
457
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
458
+ }
459
+ case QUIC_FRAME_NEW_CONNECTION_ID:
460
+ return quicFrameReadNewConnectionId(r, d, frame)
461
+ case QUIC_FRAME_PATH_CHALLENGE:
462
+ case QUIC_FRAME_PATH_RESPONSE: {
463
+ frame.dataStart = quicFrameSkip(r, toI64(QUIC_PATH_DATA_SIZE))
464
+ frame.dataLength = QUIC_PATH_DATA_SIZE
465
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
466
+ }
467
+ case QUIC_FRAME_CONNECTION_CLOSE: {
468
+ frame.errorCode = quicFrameVarint(r, d)
469
+ frame.frameType = quicFrameVarint(r, d)
470
+ quicFrameReadBytes(r, d, frame)
471
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
472
+ }
473
+ case QUIC_FRAME_CONNECTION_CLOSE_APP: {
474
+ frame.errorCode = quicFrameVarint(r, d)
475
+ quicFrameReadBytes(r, d, frame)
476
+ return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
477
+ }
478
+ default: {
479
+ // The four stream-count frames share one rule: a count past 2^60 is a
480
+ // FRAME_ENCODING_ERROR (§19.11, §19.14). Every other type was handled
481
+ // above or is STREAM's, which `quicParseFrame` routes separately.
482
+ frame.value = quicFrameVarint(r, d)
483
+ if (r.failed || frame.value > QUIC_FRAME_MAX_STREAMS) {
484
+ return QUIC_ERROR_FRAME_ENCODING
485
+ }
486
+ return QUIC_ERROR_NO_ERROR
487
+ }
488
+ }
489
+ }
490
+
491
+ /**
492
+ * Reads the frame starting at `payload[at]`, inside `payload[at .. end)`,
493
+ * into `frame`, which is reset first, and answers 0 or the transport error
494
+ * the RFC names for the break (see the module header). On 0, `frame.end` is
495
+ * where the next frame starts; after an error the packet is not to be read
496
+ * further, and `frame.type` still holds the type when it was read, which is
497
+ * what CONNECTION_CLOSE reports. A window outside `payload` is
498
+ * `QUIC_ERROR_INTERNAL`, the caller's fault rather than the peer's.
499
+ */
500
+ export const quicParseFrame = (frame: QuicFrame, payload: u8[], at: i32, end: i32): i64 => {
501
+ quicFrameReset(frame, at)
502
+ if (at < 0 || end > toI32(payload.length) || at >= end) {
503
+ return QUIC_ERROR_INTERNAL
504
+ }
505
+ const r: QuicFrameReader = new QuicFrameReader(at, end)
506
+ const typeLength: i32 = quicVarintLength(payload, at)
507
+ const type: i64 = quicFrameVarint(r, payload)
508
+ if (r.failed) {
509
+ return QUIC_ERROR_FRAME_ENCODING
510
+ }
511
+ const datagram: boolean = type === toI64(QUIC_FRAME_DATAGRAM) || type === toI64(QUIC_FRAME_DATAGRAM_LENGTH)
512
+ if (type > toI64(QUIC_FRAME_HANDSHAKE_DONE) && !datagram) {
513
+ return QUIC_ERROR_FRAME_ENCODING
514
+ }
515
+ const small: i32 = toI32(type)
516
+ frame.type = small >= QUIC_FRAME_STREAM && small <= 0x0f ? QUIC_FRAME_STREAM : small
517
+ // §12.4: a frame type is written in the fewest bytes. Every type here fits
518
+ // in one, so a longer spelling is the peer's mistake.
519
+ if (typeLength !== 1) {
520
+ return QUIC_ERROR_PROTOCOL_VIOLATION
521
+ }
522
+ let error: i64 = QUIC_ERROR_NO_ERROR
523
+ if (datagram) {
524
+ frame.type = QUIC_FRAME_DATAGRAM
525
+ error = quicFrameReadDatagram(r, payload, frame, small === QUIC_FRAME_DATAGRAM_LENGTH)
526
+ } else if (frame.type === QUIC_FRAME_STREAM) {
527
+ error = quicFrameReadStream(r, payload, frame, small & 7)
528
+ } else {
529
+ error = quicFrameReadBody(r, payload, frame, small)
530
+ }
531
+ frame.end = r.at
532
+ return error
533
+ }
534
+
535
+ /**
536
+ * Whether a frame of `type` may appear in a packet of `packetType` (RFC 9000
537
+ * §12.4, Table 3). Initial and Handshake packets carry only PADDING, PING,
538
+ * ACK, CRYPTO and the transport CONNECTION_CLOSE; a 0-RTT packet anything
539
+ * but ACK, CRYPTO, NEW_TOKEN, HANDSHAKE_DONE and the retire and response
540
+ * frames a client cannot have a reason to send yet; a 1-RTT packet anything.
541
+ */
542
+ export const quicFrameAllowed = (type: i32, packetType: i32): boolean => {
543
+ if (packetType === QUIC_PACKET_SHORT) {
544
+ return true
545
+ }
546
+ if (packetType === QUIC_PACKET_INITIAL || packetType === QUIC_PACKET_HANDSHAKE) {
547
+ return (
548
+ type === QUIC_FRAME_PADDING ||
549
+ type === QUIC_FRAME_PING ||
550
+ type === QUIC_FRAME_ACK ||
551
+ type === QUIC_FRAME_ACK_ECN ||
552
+ type === QUIC_FRAME_CRYPTO ||
553
+ type === QUIC_FRAME_CONNECTION_CLOSE
554
+ )
555
+ }
556
+ if (packetType === QUIC_PACKET_ZERO_RTT) {
557
+ return !(
558
+ type === QUIC_FRAME_ACK ||
559
+ type === QUIC_FRAME_ACK_ECN ||
560
+ type === QUIC_FRAME_CRYPTO ||
561
+ type === QUIC_FRAME_NEW_TOKEN ||
562
+ type === QUIC_FRAME_HANDSHAKE_DONE ||
563
+ type === QUIC_FRAME_RETIRE_CONNECTION_ID ||
564
+ type === QUIC_FRAME_PATH_RESPONSE
565
+ )
566
+ }
567
+ return false
568
+ }
569
+
570
+ /**
571
+ * Whether a frame of `type` asks for an acknowledgement (RFC 9000 §13.2):
572
+ * every frame but ACK, PADDING and CONNECTION_CLOSE.
573
+ */
574
+ export const quicFrameAckEliciting = (type: i32): boolean =>
575
+ !(
576
+ type === QUIC_FRAME_ACK ||
577
+ type === QUIC_FRAME_ACK_ECN ||
578
+ type === QUIC_FRAME_PADDING ||
579
+ type === QUIC_FRAME_CONNECTION_CLOSE ||
580
+ type === QUIC_FRAME_CONNECTION_CLOSE_APP
581
+ )
582
+
583
+ // ---- Writers --------------------------------------------------------------------
584
+ //
585
+ // Each frame has a `put` writer, which writes it into a caller's buffer at an
586
+ // offset and answers the offset past it (-1, writing nothing, for an argument
587
+ // out of range or a frame that does not fit before `end`), and a `push`
588
+ // writer, which appends it to an array: the `push` form grows the array by
589
+ // the frame's size and calls the `put` form, so each frame has one encoder.
590
+ // A connection builds its packets with the `put` forms, in place, with no
591
+ // allocation.
592
+
593
+ /** Whether `from .. from + length` is a window of `bytes`. */
594
+ const quicFrameWindowFits = (bytes: u8[], from: i32, length: i32): boolean =>
595
+ from >= 0 && length >= 0 && from <= toI32(bytes.length) - length
596
+
597
+ /** Whether `size` bytes from `at` fit before `end` and inside `buf`. */
598
+ const quicFrameRoom = (buf: u8[], at: i32, end: i32, size: i32): boolean =>
599
+ size > 0 && at >= 0 && end <= toI32(buf.length) && at <= end - size
600
+
601
+ /** Writes one byte and answers the offset past it; the caller has checked the room. */
602
+ const quicFramePutByte = (buf: u8[], at: i32, value: i32): i32 => {
603
+ if (at >= 0 && at < toI32(buf.length)) {
604
+ buf[at] = toU8(value)
605
+ }
606
+ return at + 1
607
+ }
608
+
609
+ /** Writes a varint in the fewest bytes; the caller has checked the room and the value. */
610
+ const quicFramePutVarint = (buf: u8[], at: i32, value: i64): i32 =>
611
+ quicVarintPut(buf, at, value, quicVarintSize(value))
612
+
613
+ /** Grows `out` by `size` bytes and answers where they start, for a `push` writer to `put` into. */
614
+ const quicFrameGrow = (out: u8[], size: i32): i32 => {
615
+ const at: i32 = toI32(out.length)
616
+ quicPacketGrow(out, size)
617
+ return at
618
+ }
619
+
620
+ /** Whether every value is a varint, which is what a frame of them needs. */
621
+ const quicFrameVarints3 = (a: i64, b: i64, c: i64): boolean =>
622
+ quicVarintSize(a) !== 0 && quicVarintSize(b) !== 0 && quicVarintSize(c) !== 0
623
+
624
+ /** Appends `count` PADDING frames, which are `count` zero bytes. A count below one appends nothing. */
625
+ export const quicPushPadding = (out: u8[], count: i32): void => {
626
+ quicPacketGrow(out, count)
627
+ }
628
+
629
+ /**
630
+ * Writes `count` PADDING frames, zero bytes, into `buf[at .. at + count)`,
631
+ * clamped to `buf`, and answers the offset past them.
632
+ */
633
+ export const quicPutPadding = (buf: u8[], at: i32, count: i32): i32 => {
634
+ let k: i32 = 0
635
+ while (k < count && at + k >= 0 && at + k < toI32(buf.length)) {
636
+ buf[at + k] = toU8(0)
637
+ k += 1
638
+ }
639
+ return at + k
640
+ }
641
+
642
+ /** Writes a frame that is its type alone, PING or HANDSHAKE_DONE; -1 for another type or no room. */
643
+ export const quicPutTypeOnly = (buf: u8[], at: i32, end: i32, type: i32): i32 => {
644
+ if ((type !== QUIC_FRAME_PING && type !== QUIC_FRAME_HANDSHAKE_DONE) || !quicFrameRoom(buf, at, end, 1)) {
645
+ return -1
646
+ }
647
+ return quicFramePutByte(buf, at, type)
648
+ }
649
+
650
+ /** Appends a frame that is its type alone: PING or HANDSHAKE_DONE. Answers `false` for another type. */
651
+ export const quicPushTypeOnly = (out: u8[], type: i32): boolean => {
652
+ if (type !== QUIC_FRAME_PING && type !== QUIC_FRAME_HANDSHAKE_DONE) {
653
+ return false
654
+ }
655
+ const at: i32 = quicFrameGrow(out, 1)
656
+ return quicPutTypeOnly(out, at, at + 1, type) >= 0
657
+ }
658
+
659
+ /**
660
+ * The size of the ACK frame `quicPutAck` writes for these ranges, or 0 when
661
+ * it would refuse them: no ranges, ranges out of order or touching, a pair
662
+ * the array does not hold, or a value no varint holds.
663
+ */
664
+ export const quicAckSize = (ranges: i64[], rangeCount: i32, ackDelay: i64): i32 => {
665
+ if (rangeCount < 1 || rangeCount * 2 > toI32(ranges.length) || quicVarintSize(ackDelay) === 0) {
666
+ return 0
667
+ }
668
+ let size: i32 = 1
669
+ let previousSmallest: i64 = 0
670
+ for (let k: i32 = 0; k < rangeCount; k += 1) {
671
+ const at: i32 = k * 2
672
+ const next: i32 = at + 1
673
+ if (at < 0 || at >= toI32(ranges.length) || next < 0 || next >= toI32(ranges.length)) {
674
+ return 0
675
+ }
676
+ const smallest: i64 = ranges[at]
677
+ const largest: i64 = ranges[next]
678
+ if (smallest < 0 || largest < smallest || largest > QUIC_MAX_VARINT) {
679
+ return 0
680
+ }
681
+ if (k === 0) {
682
+ size = size + quicVarintSize(largest) + quicVarintSize(ackDelay) + quicVarintSize(toI64(rangeCount - 1))
683
+ } else {
684
+ // Gap: the packets missing between this range and the one above, less one (§19.3.1).
685
+ const gap: i64 = previousSmallest - largest - 2
686
+ if (gap < 0) {
687
+ return 0
688
+ }
689
+ size = size + quicVarintSize(gap)
690
+ }
691
+ size = size + quicVarintSize(largest - smallest)
692
+ previousSmallest = smallest
693
+ }
694
+ return size
695
+ }
696
+
697
+ /**
698
+ * Writes an ACK frame (§19.3) for the `rangeCount` pairs of `ranges`, each
699
+ * `[smallest, largest]`, from the highest down and not touching (each pair's
700
+ * largest at least two below the smallest of the pair before it), with
701
+ * `ackDelay` already scaled by this endpoint's exponent. Answers -1, writing
702
+ * nothing, where `quicAckSize` answers 0 or the frame does not fit.
703
+ */
704
+ export const quicPutAck = (
705
+ buf: u8[],
706
+ at: i32,
707
+ end: i32,
708
+ ranges: i64[],
709
+ rangeCount: i32,
710
+ ackDelay: i64
711
+ ): i32 => {
712
+ const size: i32 = quicAckSize(ranges, rangeCount, ackDelay)
713
+ if (!quicFrameRoom(buf, at, end, size)) {
714
+ return -1
715
+ }
716
+ let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_ACK)
717
+ let previousSmallest: i64 = 0
718
+ for (let k: i32 = 0; k < rangeCount; k += 1) {
719
+ const pair: i32 = k * 2
720
+ if (pair >= 0 && pair < toI32(ranges.length) && pair + 1 < toI32(ranges.length)) {
721
+ const smallest: i64 = ranges[pair]
722
+ const largest: i64 = ranges[pair + 1]
723
+ if (k === 0) {
724
+ cursor = quicFramePutVarint(buf, cursor, largest)
725
+ cursor = quicFramePutVarint(buf, cursor, ackDelay)
726
+ cursor = quicFramePutVarint(buf, cursor, toI64(rangeCount - 1))
727
+ } else {
728
+ cursor = quicFramePutVarint(buf, cursor, previousSmallest - largest - 2)
729
+ }
730
+ cursor = quicFramePutVarint(buf, cursor, largest - smallest)
731
+ previousSmallest = smallest
732
+ }
733
+ }
734
+ return cursor
735
+ }
736
+
737
+ /** Appends the ACK frame `quicPutAck` writes. Answers `false`, appending nothing, where it refuses. */
738
+ export const quicPushAck = (out: u8[], ranges: i64[], rangeCount: i32, ackDelay: i64): boolean => {
739
+ const size: i32 = quicAckSize(ranges, rangeCount, ackDelay)
740
+ if (size === 0) {
741
+ return false
742
+ }
743
+ const at: i32 = quicFrameGrow(out, size)
744
+ return quicPutAck(out, at, at + size, ranges, rangeCount, ackDelay) >= 0
745
+ }
746
+
747
+ /**
748
+ * The bytes a CRYPTO frame's header takes for `length` bytes of data at
749
+ * `offset`: the type, the offset and the length.
750
+ */
751
+ export const quicCryptoOverhead = (offset: i64, length: i32): i32 =>
752
+ 1 + quicVarintSize(offset) + quicVarintSize(toI64(length))
753
+
754
+ /**
755
+ * Writes a CRYPTO frame (§19.6) carrying `data[from .. from + length)` at
756
+ * stream offset `offset`. Answers -1, writing nothing, for a window outside
757
+ * `data`, an offset whose end passes 2^62 − 1, or no room.
758
+ */
759
+ export const quicPutCrypto = (
760
+ buf: u8[],
761
+ at: i32,
762
+ end: i32,
763
+ offset: i64,
764
+ data: u8[],
765
+ from: i32,
766
+ length: i32
767
+ ): i32 => {
768
+ if (
769
+ !quicFrameWindowFits(data, from, length) ||
770
+ offset < 0 ||
771
+ offset > QUIC_MAX_VARINT - toI64(length) ||
772
+ !quicFrameRoom(buf, at, end, quicCryptoOverhead(offset, length) + length)
773
+ ) {
774
+ return -1
775
+ }
776
+ let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_CRYPTO)
777
+ cursor = quicFramePutVarint(buf, cursor, offset)
778
+ cursor = quicFramePutVarint(buf, cursor, toI64(length))
779
+ quicPacketCopy(buf, cursor, data, from, length)
780
+ return cursor + length
781
+ }
782
+
783
+ /** Appends the CRYPTO frame `quicPutCrypto` writes. Answers `false`, appending nothing, where it refuses. */
784
+ export const quicPushCrypto = (out: u8[], offset: i64, data: u8[], from: i32, length: i32): boolean => {
785
+ if (!quicFrameWindowFits(data, from, length) || offset < 0 || offset > QUIC_MAX_VARINT - toI64(length)) {
786
+ return false
787
+ }
788
+ const size: i32 = quicCryptoOverhead(offset, length) + length
789
+ const at: i32 = quicFrameGrow(out, size)
790
+ return quicPutCrypto(out, at, at + size, offset, data, from, length) >= 0
791
+ }
792
+
793
+ /**
794
+ * The bytes a STREAM frame's header takes for `length` bytes of data on
795
+ * `streamId` at `offset`: the type, the stream ID, the offset when it is not
796
+ * zero, and the length, which `quicPutStream` always writes.
797
+ */
798
+ export const quicStreamOverhead = (streamId: i64, offset: i64, length: i32): i32 =>
799
+ 1 + quicVarintSize(streamId) + (offset === 0 ? 0 : quicVarintSize(offset)) + quicVarintSize(toI64(length))
800
+
801
+ /**
802
+ * Writes a STREAM frame (§19.8) carrying `length` bytes of `data` on
803
+ * `streamId` at `offset`, with FIN when `fin`. The data is read from `data`
804
+ * as a ring: byte `k` of it is `data[(from + k) % data.length]`, so a
805
+ * stream's send buffer is framed where it wraps without a copy; a `from`
806
+ * plus `length` inside the array is the plain window. The Length field is
807
+ * always written, so another frame may follow; the Offset field only when it
808
+ * is not zero. Answers -1, writing nothing, for a `length` past the array, a
809
+ * stream ID that is not a varint, an end past 2^62 − 1, or no room.
810
+ */
811
+ export const quicPutStream = (
812
+ buf: u8[],
813
+ at: i32,
814
+ end: i32,
815
+ streamId: i64,
816
+ offset: i64,
817
+ data: u8[],
818
+ from: i32,
819
+ length: i32,
820
+ fin: boolean
821
+ ): i32 => {
822
+ const size: i32 = toI32(data.length)
823
+ if (
824
+ from < 0 ||
825
+ length < 0 ||
826
+ length > size ||
827
+ (length > 0 && from >= size) ||
828
+ quicVarintSize(streamId) === 0 ||
829
+ offset < 0 ||
830
+ offset > QUIC_MAX_VARINT - toI64(length) ||
831
+ !quicFrameRoom(buf, at, end, quicStreamOverhead(streamId, offset, length) + length)
832
+ ) {
833
+ return -1
834
+ }
835
+ const offsetBit: i32 = offset === 0 ? 0 : 0x04
836
+ const finBit: i32 = fin ? 0x01 : 0
837
+ let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_STREAM | offsetBit | 0x02 | finBit)
838
+ cursor = quicFramePutVarint(buf, cursor, streamId)
839
+ if (offset !== 0) {
840
+ cursor = quicFramePutVarint(buf, cursor, offset)
841
+ }
842
+ cursor = quicFramePutVarint(buf, cursor, toI64(length))
843
+ // Two runs: up to the end of the array, then from its start.
844
+ const first: i32 = length < size - from ? length : size - from
845
+ quicPacketCopy(buf, cursor, data, from, first)
846
+ quicPacketCopy(buf, cursor + first, data, 0, length - first)
847
+ return cursor + length
848
+ }
849
+
850
+ /**
851
+ * Appends a STREAM frame (§19.8) carrying `data[from .. from + length)` on
852
+ * `streamId` at `offset`, with FIN when `fin`, as `quicPutStream` writes it.
853
+ * Answers `false`, appending nothing, for a window outside `data`, a stream
854
+ * ID that is not a varint or an end past 2^62 − 1.
855
+ */
856
+ export const quicPushStream = (
857
+ out: u8[],
858
+ streamId: i64,
859
+ offset: i64,
860
+ data: u8[],
861
+ from: i32,
862
+ length: i32,
863
+ fin: boolean
864
+ ): boolean => {
865
+ if (
866
+ !quicFrameWindowFits(data, from, length) ||
867
+ quicVarintSize(streamId) === 0 ||
868
+ offset < 0 ||
869
+ offset > QUIC_MAX_VARINT - toI64(length)
870
+ ) {
871
+ return false
872
+ }
873
+ const size: i32 = quicStreamOverhead(streamId, offset, length) + length
874
+ const at: i32 = quicFrameGrow(out, size)
875
+ // A window inside the array is a ring that does not wrap.
876
+ return quicPutStream(out, at, at + size, streamId, offset, data, from, length, fin) >= 0
877
+ }
878
+
879
+ /** Whether `type` is one of the frames `quicPutValue` writes, and `value` one it may carry. */
880
+ const quicFrameValueFits = (type: i32, value: i64): boolean => {
881
+ const counts: boolean =
882
+ type === QUIC_FRAME_MAX_STREAMS_BIDI ||
883
+ type === QUIC_FRAME_MAX_STREAMS_UNI ||
884
+ type === QUIC_FRAME_STREAMS_BLOCKED_BIDI ||
885
+ type === QUIC_FRAME_STREAMS_BLOCKED_UNI
886
+ const known: boolean =
887
+ counts ||
888
+ type === QUIC_FRAME_MAX_DATA ||
889
+ type === QUIC_FRAME_DATA_BLOCKED ||
890
+ type === QUIC_FRAME_RETIRE_CONNECTION_ID
891
+ return known && quicVarintSize(value) !== 0 && !(counts && value > QUIC_FRAME_MAX_STREAMS)
892
+ }
893
+
894
+ /**
895
+ * Writes a frame of one varint after its type: MAX_DATA, MAX_STREAMS_BIDI,
896
+ * MAX_STREAMS_UNI, DATA_BLOCKED, STREAMS_BLOCKED_BIDI, STREAMS_BLOCKED_UNI or
897
+ * RETIRE_CONNECTION_ID. Answers -1 for another type, a value no varint holds,
898
+ * a stream count past 2^60, or no room.
899
+ */
900
+ export const quicPutValue = (buf: u8[], at: i32, end: i32, type: i32, value: i64): i32 => {
901
+ if (!quicFrameValueFits(type, value) || !quicFrameRoom(buf, at, end, 1 + quicVarintSize(value))) {
902
+ return -1
903
+ }
904
+ return quicFramePutVarint(buf, quicFramePutByte(buf, at, type), value)
905
+ }
906
+
907
+ /** Appends the frame `quicPutValue` writes. Answers `false`, appending nothing, where it refuses. */
908
+ export const quicPushValue = (out: u8[], type: i32, value: i64): boolean => {
909
+ if (!quicFrameValueFits(type, value)) {
910
+ return false
911
+ }
912
+ const size: i32 = 1 + quicVarintSize(value)
913
+ const at: i32 = quicFrameGrow(out, size)
914
+ return quicPutValue(out, at, at + size, type, value) >= 0
915
+ }
916
+
917
+ /**
918
+ * Writes a MAX_STREAM_DATA or STREAM_DATA_BLOCKED frame: the stream ID and
919
+ * the limit. Answers -1 for another type, a value no varint holds, or no room.
920
+ */
921
+ export const quicPutStreamValue = (
922
+ buf: u8[],
923
+ at: i32,
924
+ end: i32,
925
+ type: i32,
926
+ streamId: i64,
927
+ value: i64
928
+ ): i32 => {
929
+ if (
930
+ (type !== QUIC_FRAME_MAX_STREAM_DATA && type !== QUIC_FRAME_STREAM_DATA_BLOCKED) ||
931
+ quicVarintSize(streamId) === 0 ||
932
+ quicVarintSize(value) === 0 ||
933
+ !quicFrameRoom(buf, at, end, 1 + quicVarintSize(streamId) + quicVarintSize(value))
934
+ ) {
935
+ return -1
936
+ }
937
+ const cursor: i32 = quicFramePutVarint(buf, quicFramePutByte(buf, at, type), streamId)
938
+ return quicFramePutVarint(buf, cursor, value)
939
+ }
940
+
941
+ /** Appends the frame `quicPutStreamValue` writes. Answers `false`, appending nothing, where it refuses. */
942
+ export const quicPushStreamValue = (out: u8[], type: i32, streamId: i64, value: i64): boolean => {
943
+ if (
944
+ (type !== QUIC_FRAME_MAX_STREAM_DATA && type !== QUIC_FRAME_STREAM_DATA_BLOCKED) ||
945
+ quicVarintSize(streamId) === 0 ||
946
+ quicVarintSize(value) === 0
947
+ ) {
948
+ return false
949
+ }
950
+ const size: i32 = 1 + quicVarintSize(streamId) + quicVarintSize(value)
951
+ const at: i32 = quicFrameGrow(out, size)
952
+ return quicPutStreamValue(out, at, at + size, type, streamId, value) >= 0
953
+ }
954
+
955
+ /** The size of a NEW_CONNECTION_ID frame, or 0 for arguments `quicPutNewConnectionId` refuses. */
956
+ const quicFrameNewCidSize = (sequence: i64, retirePriorTo: i64, cidLength: i32, tokenLength: i32): i32 => {
957
+ if (
958
+ cidLength < 1 ||
959
+ cidLength > QUIC_MAX_CID_LENGTH ||
960
+ tokenLength !== QUIC_RESET_TOKEN_SIZE ||
961
+ retirePriorTo < 0 ||
962
+ retirePriorTo > sequence ||
963
+ quicVarintSize(sequence) === 0
964
+ ) {
965
+ return 0
966
+ }
967
+ return 1 + quicVarintSize(sequence) + quicVarintSize(retirePriorTo) + 1 + cidLength + QUIC_RESET_TOKEN_SIZE
968
+ }
969
+
970
+ /**
971
+ * Writes a NEW_CONNECTION_ID frame (§19.15) for the first `cidLength` bytes
972
+ * of `connectionId`. Answers -1 for a connection ID outside 1 to 20 bytes or
973
+ * past the array, a reset token that is not 16 bytes, a Retire Prior To
974
+ * above the sequence number, a value no varint holds, or no room.
975
+ */
976
+ export const quicPutNewConnectionId = (
977
+ buf: u8[],
978
+ at: i32,
979
+ end: i32,
980
+ sequence: i64,
981
+ retirePriorTo: i64,
982
+ connectionId: u8[],
983
+ cidLength: i32,
984
+ resetToken: u8[]
985
+ ): i32 => {
986
+ const size: i32 = quicFrameNewCidSize(sequence, retirePriorTo, cidLength, toI32(resetToken.length))
987
+ if (size === 0 || cidLength > toI32(connectionId.length) || !quicFrameRoom(buf, at, end, size)) {
988
+ return -1
989
+ }
990
+ let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_NEW_CONNECTION_ID)
991
+ cursor = quicFramePutVarint(buf, cursor, sequence)
992
+ cursor = quicFramePutVarint(buf, cursor, retirePriorTo)
993
+ cursor = quicFramePutByte(buf, cursor, cidLength)
994
+ quicPacketCopy(buf, cursor, connectionId, 0, cidLength)
995
+ quicPacketCopy(buf, cursor + cidLength, resetToken, 0, QUIC_RESET_TOKEN_SIZE)
996
+ return cursor + cidLength + QUIC_RESET_TOKEN_SIZE
997
+ }
998
+
999
+ /**
1000
+ * Appends a NEW_CONNECTION_ID frame (§19.15). Answers `false` for a
1001
+ * connection ID outside 1 to 20 bytes, a reset token that is not 16 bytes, a
1002
+ * Retire Prior To above the sequence number, or a value no varint holds.
1003
+ */
1004
+ export const quicPushNewConnectionId = (
1005
+ out: u8[],
1006
+ sequence: i64,
1007
+ retirePriorTo: i64,
1008
+ connectionId: u8[],
1009
+ resetToken: u8[]
1010
+ ): boolean => {
1011
+ const cidLength: i32 = toI32(connectionId.length)
1012
+ const size: i32 = quicFrameNewCidSize(sequence, retirePriorTo, cidLength, toI32(resetToken.length))
1013
+ if (size === 0) {
1014
+ return false
1015
+ }
1016
+ const at: i32 = quicFrameGrow(out, size)
1017
+ return (
1018
+ quicPutNewConnectionId(
1019
+ out,
1020
+ at,
1021
+ at + size,
1022
+ sequence,
1023
+ retirePriorTo,
1024
+ connectionId,
1025
+ cidLength,
1026
+ resetToken
1027
+ ) >= 0
1028
+ )
1029
+ }
1030
+
1031
+ /**
1032
+ * Writes a PATH_CHALLENGE or PATH_RESPONSE frame carrying the eight bytes
1033
+ * `data[from .. from + 8)`. Answers -1 for another type, a window outside
1034
+ * `data`, or no room.
1035
+ */
1036
+ export const quicPutPathData = (buf: u8[], at: i32, end: i32, type: i32, data: u8[], from: i32): i32 => {
1037
+ if (
1038
+ (type !== QUIC_FRAME_PATH_CHALLENGE && type !== QUIC_FRAME_PATH_RESPONSE) ||
1039
+ !quicFrameWindowFits(data, from, QUIC_PATH_DATA_SIZE) ||
1040
+ !quicFrameRoom(buf, at, end, 1 + QUIC_PATH_DATA_SIZE)
1041
+ ) {
1042
+ return -1
1043
+ }
1044
+ const cursor: i32 = quicFramePutByte(buf, at, type)
1045
+ quicPacketCopy(buf, cursor, data, from, QUIC_PATH_DATA_SIZE)
1046
+ return cursor + QUIC_PATH_DATA_SIZE
1047
+ }
1048
+
1049
+ /** Appends the frame `quicPutPathData` writes. Answers `false`, appending nothing, where it refuses. */
1050
+ export const quicPushPathData = (out: u8[], type: i32, data: u8[], from: i32): boolean => {
1051
+ if (
1052
+ (type !== QUIC_FRAME_PATH_CHALLENGE && type !== QUIC_FRAME_PATH_RESPONSE) ||
1053
+ !quicFrameWindowFits(data, from, QUIC_PATH_DATA_SIZE)
1054
+ ) {
1055
+ return false
1056
+ }
1057
+ const at: i32 = quicFrameGrow(out, 1 + QUIC_PATH_DATA_SIZE)
1058
+ return quicPutPathData(out, at, at + 1 + QUIC_PATH_DATA_SIZE, type, data, from) >= 0
1059
+ }
1060
+
1061
+ /**
1062
+ * Writes a RESET_STREAM frame (§19.4) or, with `finalSize` at -1, a
1063
+ * STOP_SENDING frame (§19.5). Answers -1 for a value no varint holds, or no room.
1064
+ */
1065
+ export const quicPutStreamError = (
1066
+ buf: u8[],
1067
+ at: i32,
1068
+ end: i32,
1069
+ streamId: i64,
1070
+ errorCode: i64,
1071
+ finalSize: i64
1072
+ ): i32 => {
1073
+ const reset: boolean = finalSize !== -1
1074
+ if (!quicFrameVarints3(streamId, errorCode, reset ? finalSize : 0)) {
1075
+ return -1
1076
+ }
1077
+ const size: i32 =
1078
+ 1 + quicVarintSize(streamId) + quicVarintSize(errorCode) + (reset ? quicVarintSize(finalSize) : 0)
1079
+ if (!quicFrameRoom(buf, at, end, size)) {
1080
+ return -1
1081
+ }
1082
+ let cursor: i32 = quicFramePutByte(buf, at, reset ? QUIC_FRAME_RESET_STREAM : QUIC_FRAME_STOP_SENDING)
1083
+ cursor = quicFramePutVarint(buf, cursor, streamId)
1084
+ cursor = quicFramePutVarint(buf, cursor, errorCode)
1085
+ if (reset) {
1086
+ cursor = quicFramePutVarint(buf, cursor, finalSize)
1087
+ }
1088
+ return cursor
1089
+ }
1090
+
1091
+ /** Appends the frame `quicPutStreamError` writes. Answers `false`, appending nothing, where it refuses. */
1092
+ export const quicPushStreamError = (out: u8[], streamId: i64, errorCode: i64, finalSize: i64): boolean => {
1093
+ const reset: boolean = finalSize !== -1
1094
+ if (!quicFrameVarints3(streamId, errorCode, reset ? finalSize : 0)) {
1095
+ return false
1096
+ }
1097
+ const size: i32 =
1098
+ 1 + quicVarintSize(streamId) + quicVarintSize(errorCode) + (reset ? quicVarintSize(finalSize) : 0)
1099
+ const at: i32 = quicFrameGrow(out, size)
1100
+ return quicPutStreamError(out, at, at + size, streamId, errorCode, finalSize) >= 0
1101
+ }
1102
+
1103
+ /** The size of a CONNECTION_CLOSE frame, or 0 for a value no varint holds. */
1104
+ const quicFrameCloseSize = (application: boolean, errorCode: i64, frameType: i64, reasonLength: i32): i32 => {
1105
+ if (!quicFrameVarints3(errorCode, application ? 0 : frameType, toI64(reasonLength))) {
1106
+ return 0
1107
+ }
1108
+ return (
1109
+ 1 +
1110
+ quicVarintSize(errorCode) +
1111
+ (application ? 0 : quicVarintSize(frameType)) +
1112
+ quicVarintSize(toI64(reasonLength)) +
1113
+ reasonLength
1114
+ )
1115
+ }
1116
+
1117
+ /**
1118
+ * Writes a CONNECTION_CLOSE frame (§19.19): with `application` false the
1119
+ * transport form 0x1c, carrying `frameType`, the type of the frame that
1120
+ * caused the error (0 when none did); with `application` true the 0x1d form,
1121
+ * which has no frame type. `reason` is the phrase, UTF-8, possibly empty.
1122
+ * Answers -1 for a value no varint holds, or no room.
1123
+ */
1124
+ export const quicPutConnectionClose = (
1125
+ buf: u8[],
1126
+ at: i32,
1127
+ end: i32,
1128
+ application: boolean,
1129
+ errorCode: i64,
1130
+ frameType: i64,
1131
+ reason: u8[]
1132
+ ): i32 => {
1133
+ const reasonLength: i32 = toI32(reason.length)
1134
+ const size: i32 = quicFrameCloseSize(application, errorCode, frameType, reasonLength)
1135
+ if (!quicFrameRoom(buf, at, end, size)) {
1136
+ return -1
1137
+ }
1138
+ let cursor: i32 = quicFramePutByte(
1139
+ buf,
1140
+ at,
1141
+ application ? QUIC_FRAME_CONNECTION_CLOSE_APP : QUIC_FRAME_CONNECTION_CLOSE
1142
+ )
1143
+ cursor = quicFramePutVarint(buf, cursor, errorCode)
1144
+ if (!application) {
1145
+ cursor = quicFramePutVarint(buf, cursor, frameType)
1146
+ }
1147
+ cursor = quicFramePutVarint(buf, cursor, toI64(reasonLength))
1148
+ quicPacketCopy(buf, cursor, reason, 0, reasonLength)
1149
+ return cursor + reasonLength
1150
+ }
1151
+
1152
+ /** Appends the frame `quicPutConnectionClose` writes. Answers `false`, appending nothing, where it refuses. */
1153
+ export const quicPushConnectionClose = (
1154
+ out: u8[],
1155
+ application: boolean,
1156
+ errorCode: i64,
1157
+ frameType: i64,
1158
+ reason: u8[]
1159
+ ): boolean => {
1160
+ const size: i32 = quicFrameCloseSize(application, errorCode, frameType, toI32(reason.length))
1161
+ if (size === 0) {
1162
+ return false
1163
+ }
1164
+ const at: i32 = quicFrameGrow(out, size)
1165
+ return quicPutConnectionClose(out, at, at + size, application, errorCode, frameType, reason) >= 0
1166
+ }
1167
+
1168
+ /**
1169
+ * Appends a NEW_TOKEN frame (§19.7) carrying `token`. Answers `false` for an
1170
+ * empty token, which §19.7 forbids.
1171
+ */
1172
+ export const quicPushNewToken = (out: u8[], token: u8[]): boolean => {
1173
+ const length: i32 = toI32(token.length)
1174
+ if (length === 0) {
1175
+ return false
1176
+ }
1177
+ const size: i32 = 1 + quicVarintSize(toI64(length)) + length
1178
+ let cursor: i32 = quicFramePutByte(out, quicFrameGrow(out, size), QUIC_FRAME_NEW_TOKEN)
1179
+ cursor = quicFramePutVarint(out, cursor, toI64(length))
1180
+ quicPacketCopy(out, cursor, token, 0, length)
1181
+ return true
1182
+ }
1183
+
1184
+ /**
1185
+ * The size of the DATAGRAM frame `quicPutDatagram` writes for a payload of
1186
+ * `length` bytes: the type, the Length and the payload. This is the size RFC
1187
+ * 9221 §3 holds to `max_datagram_frame_size`.
1188
+ */
1189
+ export const quicDatagramSize = (length: i32): i32 => 1 + quicVarintSize(toI64(length)) + length
1190
+
1191
+ /**
1192
+ * Writes a DATAGRAM frame with a Length (type 0x31, RFC 9221 §4) carrying
1193
+ * `data[from .. from + length)`. Answers -1 for a window outside `data` or no room.
1194
+ */
1195
+ export const quicPutDatagram = (buf: u8[], at: i32, end: i32, data: u8[], from: i32, length: i32): i32 => {
1196
+ if (!quicFrameWindowFits(data, from, length) || !quicFrameRoom(buf, at, end, quicDatagramSize(length))) {
1197
+ return -1
1198
+ }
1199
+ const cursor: i32 = quicFramePutVarint(
1200
+ buf,
1201
+ quicFramePutByte(buf, at, QUIC_FRAME_DATAGRAM_LENGTH),
1202
+ toI64(length)
1203
+ )
1204
+ quicPacketCopy(buf, cursor, data, from, length)
1205
+ return cursor + length
1206
+ }