@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,457 @@
1
+ /**
2
+ * `nish/net/http3-frame` — the frames and stream types of HTTP/3 (RFC 9114
3
+ * §6.2, §7): frame headers, SETTINGS, the one-identifier frames (GOAWAY,
4
+ * MAX_PUSH_ID, CANCEL_PUSH), and which types are HTTP/2's reserved ones or
5
+ * unknown. Sans-IO: every reader takes a byte window `(buf, off, len)` and
6
+ * reads it in place, and every writer writes into the caller's buffer at an
7
+ * offset, before an end, and answers the offset past what it wrote, or -1
8
+ * with nothing written. Nothing here allocates.
9
+ *
10
+ * import { Http3FrameHeader, h3ReadFrameHeader, h3PutFrameHeader, H3_FRAME_DATA } from "nish/net/http3-frame";
11
+ *
12
+ * const h = new Http3FrameHeader();
13
+ * const size: i32 = h3ReadFrameHeader(h, buf, off, len); // 0: the window ends inside the header
14
+ * // h.type, h.length; the payload is the next h.length bytes of the stream
15
+ * const at: i32 = h3PutFrameHeader(out, 0, toI32(out.length), H3_FRAME_DATA, toI64(n));
16
+ *
17
+ * **Frames** (§7.1) are a type and a length, each a QUIC variable-length
18
+ * integer, and then that many bytes of payload. On a QUIC stream a frame may
19
+ * arrive in any number of pieces, so a header is read only once both of its
20
+ * integers are in the window (`h3ReadFrameHeader` answers 0 until then) and a
21
+ * payload is the caller's to count down. The varints are
22
+ * `nish/net/quic-packet`'s, which QUIC and HTTP/3 share.
23
+ *
24
+ * **Types.** DATA, HEADERS, CANCEL_PUSH, SETTINGS, PUSH_PROMISE, GOAWAY and
25
+ * MAX_PUSH_ID are the seven RFC 9114 defines. The types HTTP/2 used with no
26
+ * HTTP/3 counterpart (0x02 PRIORITY, 0x06 PING, 0x08 WINDOW_UPDATE, 0x09
27
+ * CONTINUATION) are reserved, and receiving one is H3_FRAME_UNEXPECTED
28
+ * (§7.2.8): `h3ReservedHttp2Frame` names them. Every other type is unknown,
29
+ * the reserved `0x1f * N + 0x21` types that exercise that rule included
30
+ * (§7.2.8, §9), and its frame is skipped whole wherever it arrives.
31
+ *
32
+ * **SETTINGS** (§7.2.4). `h3ReadSettings` reads a whole payload into an
33
+ * `Http3Settings`: the three identifiers this endpoint understands, and the
34
+ * first `H3_SETTINGS_KEEP` it does not, kept rather than dropped so that an
35
+ * extension can read them. The four that extended CONNECT and WebTransport
36
+ * read (RFC 9220's, RFC 9297's and the two of the WebTransport drafts) are
37
+ * also recorded in fields of their own, refused when repeated, so a peer
38
+ * cannot push them out of reach behind eight others. A payload that ends
39
+ * inside a pair is H3_FRAME_ERROR (§7.1); an identifier HTTP/2 defined with
40
+ * no HTTP/3 counterpart (0x00, 0x02 to 0x05), or one that appears twice, is
41
+ * H3_SETTINGS_ERROR (§7.2.4, §7.2.4.1). A repeat among the unknown ones is
42
+ * caught while they are kept, and past `H3_SETTINGS_KEEP` they are counted,
43
+ * not compared, so a peer cannot make the read cost more than a fixed number
44
+ * of compares per setting.
45
+ *
46
+ * **Stream types** (§6.2): control 0x00, push 0x01, RFC 9204's QPACK
47
+ * encoder 0x02 and decoder 0x03, and WebTransport's 0x54, whose bidirectional
48
+ * counterpart is the signal 0x41 where a request's first frame would be. Each is one varint at the start of a
49
+ * unidirectional stream, written with `h3PutVarint`.
50
+ *
51
+ * **Error codes** (§8.1) are the `H3_*` values from 0x0100; QPACK's three
52
+ * (0x0200 to 0x0202) are `nish/net/qpack`'s.
53
+ *
54
+ * Written from RFC 9114 §6.2, §7, §8.1 and §11.2, RFC 9220 §5, RFC 9297
55
+ * §2.1.1 and §5.2 and draft-ietf-webtrans-http3-02, not ported from another
56
+ * implementation. Private names carry the `h3Frame` prefix, since a `std/`
57
+ * module's private functions share the importing program's flat symbol
58
+ * namespace (`docs/wp26-stdlib.md` §3e).
59
+ */
60
+ import {
61
+ QPACK_DECODER_STREAM,
62
+ QPACK_ENCODER_STREAM,
63
+ QPACK_SETTINGS_BLOCKED_STREAMS,
64
+ QPACK_SETTINGS_MAX_TABLE_CAPACITY,
65
+ } from "nish/net/qpack"
66
+ import { quicVarintLength, quicVarintPut, quicVarintRead, quicVarintSize } from "nish/net/quic-packet"
67
+
68
+ // ---- Frame types (§7.2, §11.2.1) ------------------------------------------------
69
+
70
+ /** DATA (§7.2.1): a piece of a message's content. */
71
+ export const H3_FRAME_DATA: i64 = 0x00
72
+ /** HEADERS (§7.2.2): a QPACK-encoded field section. */
73
+ export const H3_FRAME_HEADERS: i64 = 0x01
74
+ /** CANCEL_PUSH (§7.2.3): one push ID. */
75
+ export const H3_FRAME_CANCEL_PUSH: i64 = 0x03
76
+ /** SETTINGS (§7.2.4): identifier and value pairs, first on a control stream. */
77
+ export const H3_FRAME_SETTINGS: i64 = 0x04
78
+ /** PUSH_PROMISE (§7.2.5): a push ID and a request's field section; only a server sends it. */
79
+ export const H3_FRAME_PUSH_PROMISE: i64 = 0x05
80
+ /** GOAWAY (§7.2.6): a stream ID from a server, a push ID from a client. */
81
+ export const H3_FRAME_GOAWAY: i64 = 0x07
82
+ /** MAX_PUSH_ID (§7.2.7): the largest push ID a client lets a server use. */
83
+ export const H3_FRAME_MAX_PUSH_ID: i64 = 0x0d
84
+
85
+ // ---- Unidirectional stream types (§6.2, RFC 9204 §4.2) --------------------------
86
+
87
+ /** The control stream: SETTINGS first, then the connection's control frames. */
88
+ export const H3_STREAM_CONTROL: i64 = 0x00
89
+ /** A push stream, which only a server opens. */
90
+ export const H3_STREAM_PUSH: i64 = 0x01
91
+ /** QPACK's encoder stream. */
92
+ export const H3_STREAM_QPACK_ENCODER: i64 = QPACK_ENCODER_STREAM
93
+ /** QPACK's decoder stream. */
94
+ export const H3_STREAM_QPACK_DECODER: i64 = QPACK_DECODER_STREAM
95
+ /**
96
+ * A WebTransport unidirectional stream (draft-ietf-webtrans-http3-02 §4.1):
97
+ * the type, then the session ID, then the session's bytes.
98
+ */
99
+ export const H3_STREAM_WEBTRANSPORT: i64 = 0x54
100
+ /**
101
+ * The signal that opens a WebTransport bidirectional stream (draft-02 §4.2):
102
+ * where a request's first frame type would be, then the session ID, then the
103
+ * session's bytes. It is not a frame: no length follows it.
104
+ */
105
+ export const H3_FRAME_WEBTRANSPORT_STREAM: i64 = 0x41
106
+
107
+ // ---- Settings (§7.2.4.1, RFC 9204 §5) -------------------------------------------
108
+
109
+ /** SETTINGS_QPACK_MAX_TABLE_CAPACITY. */
110
+ export const H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY: i64 = QPACK_SETTINGS_MAX_TABLE_CAPACITY
111
+ /** SETTINGS_MAX_FIELD_SECTION_SIZE: the largest field section the sender takes, as §4.2.2 counts it. */
112
+ export const H3_SETTINGS_MAX_FIELD_SECTION_SIZE: i64 = 0x06
113
+ /** SETTINGS_QPACK_BLOCKED_STREAMS. */
114
+ export const H3_SETTINGS_QPACK_BLOCKED_STREAMS: i64 = QPACK_SETTINGS_BLOCKED_STREAMS
115
+ /** SETTINGS_ENABLE_CONNECT_PROTOCOL (RFC 9220 §5, RFC 8441 §3): 1 lets the peer send an extended CONNECT. */
116
+ export const H3_SETTINGS_ENABLE_CONNECT_PROTOCOL: i64 = 0x08
117
+ /** SETTINGS_H3_DATAGRAM (RFC 9297 §2.1.1): 1 lets the peer send HTTP datagrams. */
118
+ export const H3_SETTINGS_H3_DATAGRAM: i64 = 0x33
119
+ /** SETTINGS_ENABLE_WEBTRANSPORT of draft-ietf-webtrans-http3-02 §3.1, which Chrome and `wtransport` 0.7 send. */
120
+ export const H3_SETTINGS_ENABLE_WEBTRANSPORT: i64 = 0x2b603742
121
+ /** SETTINGS_WEBTRANSPORT_MAX_SESSIONS of draft-ietf-webtrans-http3-07 §3.1, which `wtransport` 0.7 sends beside the draft-02 one. */
122
+ export const H3_SETTINGS_WEBTRANSPORT_MAX_SESSIONS: i64 = 0xc671706a
123
+
124
+ /** How many identifiers `Http3Settings` keeps that it does not understand. */
125
+ export const H3_SETTINGS_KEEP: i32 = 8
126
+
127
+ // ---- Error codes (§8.1) ---------------------------------------------------------
128
+
129
+ /** What `h3ReadSettings` answers for a payload it read whole. */
130
+ export const H3_SETTINGS_OK: i64 = 0
131
+ export const H3_NO_ERROR: i64 = 0x0100
132
+ export const H3_GENERAL_PROTOCOL_ERROR: i64 = 0x0101
133
+ export const H3_INTERNAL_ERROR: i64 = 0x0102
134
+ export const H3_STREAM_CREATION_ERROR: i64 = 0x0103
135
+ export const H3_CLOSED_CRITICAL_STREAM: i64 = 0x0104
136
+ export const H3_FRAME_UNEXPECTED: i64 = 0x0105
137
+ export const H3_FRAME_ERROR: i64 = 0x0106
138
+ export const H3_EXCESSIVE_LOAD: i64 = 0x0107
139
+ export const H3_ID_ERROR: i64 = 0x0108
140
+ export const H3_SETTINGS_ERROR: i64 = 0x0109
141
+ export const H3_MISSING_SETTINGS: i64 = 0x010a
142
+ export const H3_REQUEST_REJECTED: i64 = 0x010b
143
+ export const H3_REQUEST_CANCELLED: i64 = 0x010c
144
+ export const H3_REQUEST_INCOMPLETE: i64 = 0x010d
145
+ export const H3_MESSAGE_ERROR: i64 = 0x010e
146
+ export const H3_CONNECT_ERROR: i64 = 0x010f
147
+ export const H3_VERSION_FALLBACK: i64 = 0x0110
148
+ /** WT_SESSION_GONE (draft-ietf-webtrans-http3-02 §4.5): a WebTransport stream of a session that is gone, or can never come. */
149
+ export const H3_WT_SESSION_GONE: i64 = 0x170d7b68
150
+ /** H3_DATAGRAM_ERROR (RFC 9297 §5.2): an HTTP datagram or a capsule that does not parse. */
151
+ export const H3_DATAGRAM_ERROR: i64 = 0x33
152
+
153
+ /** The largest frame header: two eight-byte varints. */
154
+ export const H3_FRAME_HEADER_MAX: i32 = 16
155
+
156
+ /** The setting bits `Http3Settings.seen` records, one per identifier it understands. */
157
+ const H3_FRAME_SEEN_CAPACITY: i32 = 1
158
+ const H3_FRAME_SEEN_FIELD_SECTION: i32 = 2
159
+ const H3_FRAME_SEEN_BLOCKED: i32 = 4
160
+
161
+ /** A typed -1 and 0, since a bare literal is an `f64` under `--number-mode f64`. */
162
+ const H3_FRAME_NONE: i64 = -1
163
+ const H3_FRAME_ZERO: i32 = 0
164
+
165
+ /** One frame header as `h3ReadFrameHeader` read it: the type and the payload's length. */
166
+ export class Http3FrameHeader {
167
+ type: i64 = 0
168
+ length: i64 = 0
169
+ }
170
+
171
+ /**
172
+ * The peer's SETTINGS as `h3ReadSettings` read them. The three it understands
173
+ * hold RFC 9114's and RFC 9204's defaults until a frame sets them:
174
+ * `maxFieldSectionSize` is -1, unlimited (§7.2.4.1). The first
175
+ * `H3_SETTINGS_KEEP` identifiers it does not understand are kept with their
176
+ * values, in order, and the rest counted in `dropped`.
177
+ */
178
+ export class Http3Settings {
179
+ unknownIds: i64[]
180
+ unknownValues: i64[]
181
+ maxTableCapacity: i64 = 0
182
+ maxFieldSectionSize: i64 = -1
183
+ blockedStreams: i64 = 0
184
+ /**
185
+ * The extension settings WebTransport reads, -1 when the frame did not
186
+ * carry them: SETTINGS_ENABLE_CONNECT_PROTOCOL, SETTINGS_H3_DATAGRAM,
187
+ * SETTINGS_ENABLE_WEBTRANSPORT and SETTINGS_WEBTRANSPORT_MAX_SESSIONS. Each
188
+ * is recorded here, where a peer cannot push it out of reach, and also kept
189
+ * as an unknown identifier while there is room, so `unknown` still finds it.
190
+ */
191
+ enableConnectProtocol: i64 = -1
192
+ h3Datagram: i64 = -1
193
+ enableWebtransport: i64 = -1
194
+ webtransportMaxSessions: i64 = -1
195
+ /** The bits of the identifiers understood that the frame set. */
196
+ seen: i32 = 0
197
+ /** How many unknown identifiers are kept. */
198
+ unknownCount: i32 = 0
199
+ /** How many more there were, saturating at 2^31 - 1. */
200
+ dropped: i32 = 0
201
+
202
+ constructor() {
203
+ this.unknownIds = new Array<i64>(H3_SETTINGS_KEEP)
204
+ this.unknownValues = new Array<i64>(H3_SETTINGS_KEEP)
205
+ }
206
+
207
+ /** Back to the defaults, for the next connection. */
208
+ reset(): void {
209
+ this.maxTableCapacity = 0
210
+ this.maxFieldSectionSize = -1
211
+ this.blockedStreams = 0
212
+ this.enableConnectProtocol = -1
213
+ this.h3Datagram = -1
214
+ this.enableWebtransport = -1
215
+ this.webtransportMaxSessions = -1
216
+ this.seen = 0
217
+ this.unknownCount = 0
218
+ this.dropped = 0
219
+ }
220
+
221
+ /** The value of an identifier this module does not understand, or -1 when the frame did not carry it (or carried it past the kept ones). */
222
+ unknown(id: i64): i64 {
223
+ for (let k: i32 = 0; k < this.unknownCount && k < H3_SETTINGS_KEEP; k++) {
224
+ if (this.unknownIds[k] === id) {
225
+ return this.unknownValues[k]
226
+ }
227
+ }
228
+ return H3_FRAME_NONE
229
+ }
230
+
231
+ /** Records one pair; answers `H3_SETTINGS_OK` or H3_SETTINGS_ERROR for a reserved or repeated identifier. */
232
+ take(id: i64, value: i64): i64 {
233
+ if (id === 0x00 || (id >= 0x02 && id <= 0x05)) {
234
+ return H3_SETTINGS_ERROR
235
+ }
236
+ let bit: i32 = 0
237
+ if (id === H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY) {
238
+ bit = H3_FRAME_SEEN_CAPACITY
239
+ this.maxTableCapacity = value
240
+ } else if (id === H3_SETTINGS_MAX_FIELD_SECTION_SIZE) {
241
+ bit = H3_FRAME_SEEN_FIELD_SECTION
242
+ this.maxFieldSectionSize = value
243
+ } else if (id === H3_SETTINGS_QPACK_BLOCKED_STREAMS) {
244
+ bit = H3_FRAME_SEEN_BLOCKED
245
+ this.blockedStreams = value
246
+ }
247
+ if (bit !== 0) {
248
+ if ((this.seen & bit) !== 0) {
249
+ return H3_SETTINGS_ERROR
250
+ }
251
+ this.seen = this.seen | bit
252
+ return H3_SETTINGS_OK
253
+ }
254
+ // The extension settings: recorded (-1 until then, so a second is a repeat), then kept below like any unknown one.
255
+ if (id === H3_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
256
+ if (this.enableConnectProtocol >= 0) {
257
+ return H3_SETTINGS_ERROR
258
+ }
259
+ this.enableConnectProtocol = value
260
+ } else if (id === H3_SETTINGS_H3_DATAGRAM) {
261
+ if (this.h3Datagram >= 0) {
262
+ return H3_SETTINGS_ERROR
263
+ }
264
+ this.h3Datagram = value
265
+ } else if (id === H3_SETTINGS_ENABLE_WEBTRANSPORT) {
266
+ if (this.enableWebtransport >= 0) {
267
+ return H3_SETTINGS_ERROR
268
+ }
269
+ this.enableWebtransport = value
270
+ } else if (id === H3_SETTINGS_WEBTRANSPORT_MAX_SESSIONS) {
271
+ if (this.webtransportMaxSessions >= 0) {
272
+ return H3_SETTINGS_ERROR
273
+ }
274
+ this.webtransportMaxSessions = value
275
+ }
276
+ if (this.unknown(id) >= 0) {
277
+ return H3_SETTINGS_ERROR
278
+ }
279
+ const at: i32 = this.unknownCount
280
+ if (at >= 0 && at < H3_SETTINGS_KEEP) {
281
+ this.unknownIds[at] = id
282
+ this.unknownValues[at] = value
283
+ this.unknownCount = at + 1
284
+ } else if (this.dropped < 2147483647) {
285
+ this.dropped = this.dropped + 1
286
+ }
287
+ return H3_SETTINGS_OK
288
+ }
289
+ }
290
+
291
+ /** Panics unless `[off, off + len)` lies inside `buf`: a window outside its buffer is the program's mistake. */
292
+ export const h3CheckWindow = (what: string, buf: u8[], off: i32, len: i32): void => {
293
+ if (off < 0 || len < 0 || toI64(off) + toI64(len) > toI64(buf.length)) {
294
+ panic(`${what}: the window [${off}, ${off} + ${len}) is outside a buffer of ${buf.length} bytes`)
295
+ }
296
+ }
297
+
298
+ /** `count` plus one, saturating at 2^31 - 1: every counter a peer drives. */
299
+ export const h3Count = (count: i32): i32 => (count < 2147483647 ? count + 1 : count)
300
+
301
+ /** Whether `[at, end)` is a usable place to write in `buf`. */
302
+ const h3FrameRoom = (buf: u8[], at: i32, end: i32): boolean =>
303
+ at >= 0 && at <= end && end <= toI32(buf.length)
304
+
305
+ /**
306
+ * The varint at `buf[at]`, read only from `buf[at .. end)`, or -1 when it
307
+ * does not fit there. Advance past it by `h3VarintLength(buf, at)`.
308
+ */
309
+ export const h3ReadVarint = (buf: u8[], at: i32, end: i32): i64 => quicVarintRead(buf, at, end)
310
+
311
+ /** The length of the varint whose first byte is `buf[at]`: 1, 2, 4 or 8, or 0 outside `buf`. */
312
+ export const h3VarintLength = (buf: u8[], at: i32): i32 => quicVarintLength(buf, at)
313
+
314
+ /**
315
+ * Writes `value` as a varint in the fewest bytes at `buf[at]`, before `end`,
316
+ * and answers the offset past it, or -1 when it does not fit or no varint
317
+ * holds the value.
318
+ */
319
+ export const h3PutVarint = (buf: u8[], at: i32, end: i32, value: i64): i32 => {
320
+ const size: i32 = quicVarintSize(value)
321
+ if (size === 0 || !h3FrameRoom(buf, at, end) || at > end - size) {
322
+ return -1
323
+ }
324
+ return quicVarintPut(buf, at, value, size)
325
+ }
326
+
327
+ /** How many bytes a frame header of `type` and `length` takes, or 0 when either is no varint. */
328
+ export const h3FrameHeaderSize = (type: i64, length: i64): i32 => {
329
+ const a: i32 = quicVarintSize(type)
330
+ const b: i32 = quicVarintSize(length)
331
+ return a === 0 || b === 0 ? H3_FRAME_ZERO : a + b
332
+ }
333
+
334
+ /**
335
+ * Reads a frame header from `buf[off, off + len)` into `h` and answers how
336
+ * many bytes it took, or 0 when the window ends inside it (and `h` is not
337
+ * touched). Any type and any length up to 2^62 - 1 is a header; whether the
338
+ * type is allowed where it arrived is the stream's question.
339
+ */
340
+ export const h3ReadFrameHeader = (h: Http3FrameHeader, buf: u8[], off: i32, len: i32): i32 => {
341
+ h3CheckWindow("h3ReadFrameHeader", buf, off, len)
342
+ const end: i32 = off + len
343
+ const type: i64 = quicVarintRead(buf, off, end)
344
+ if (type < 0) {
345
+ return 0
346
+ }
347
+ const at: i32 = off + quicVarintLength(buf, off)
348
+ const length: i64 = quicVarintRead(buf, at, end)
349
+ if (length < 0) {
350
+ return 0
351
+ }
352
+ h.type = type
353
+ h.length = length
354
+ return at + quicVarintLength(buf, at) - off
355
+ }
356
+
357
+ /** Writes a frame header of `type` and `length` at `buf[at]`, before `end`; answers the offset past it, or -1. */
358
+ export const h3PutFrameHeader = (buf: u8[], at: i32, end: i32, type: i64, length: i64): i32 => {
359
+ const size: i32 = h3FrameHeaderSize(type, length)
360
+ if (size === 0 || !h3FrameRoom(buf, at, end) || at > end - size) {
361
+ return -1
362
+ }
363
+ return h3PutVarint(buf, h3PutVarint(buf, at, end, type), end, length)
364
+ }
365
+
366
+ /** Whether `type` is one HTTP/2 used that HTTP/3 reserves, whose receipt is H3_FRAME_UNEXPECTED (§7.2.8). */
367
+ export const h3ReservedHttp2Frame = (type: i64): boolean =>
368
+ type === 0x02 || type === 0x06 || type === 0x08 || type === 0x09
369
+
370
+ /** Whether `value` is one of the `0x1f * N + 0x21` values reserved to exercise the rule that unknowns are ignored (§7.2.8, §7.2.4.1, §6.2.3). */
371
+ export const h3Greased = (value: i64): boolean => value >= 0x21 && (value - 0x21) % 0x1f === 0
372
+
373
+ /**
374
+ * Reads a whole SETTINGS payload, `buf[off, off + len)`, into `s` (which it
375
+ * resets first). Answers `H3_SETTINGS_OK`, H3_FRAME_ERROR for a payload that
376
+ * ends inside a pair (§7.1), or H3_SETTINGS_ERROR for a reserved or repeated
377
+ * identifier (§7.2.4).
378
+ */
379
+ export const h3ReadSettings = (s: Http3Settings, buf: u8[], off: i32, len: i32): i64 => {
380
+ h3CheckWindow("h3ReadSettings", buf, off, len)
381
+ s.reset()
382
+ const end: i32 = off + len
383
+ let at: i32 = off
384
+ while (at < end) {
385
+ const id: i64 = quicVarintRead(buf, at, end)
386
+ if (id < 0) {
387
+ return H3_FRAME_ERROR
388
+ }
389
+ at = at + quicVarintLength(buf, at)
390
+ const value: i64 = quicVarintRead(buf, at, end)
391
+ if (value < 0) {
392
+ return H3_FRAME_ERROR
393
+ }
394
+ at = at + quicVarintLength(buf, at)
395
+ const result: i64 = s.take(id, value)
396
+ if (result !== H3_SETTINGS_OK) {
397
+ return result
398
+ }
399
+ }
400
+ return H3_SETTINGS_OK
401
+ }
402
+
403
+ /**
404
+ * Writes a whole SETTINGS frame of the pairs `ids[k]`, `values[k]` at
405
+ * `buf[at]`, before `end`, and answers the offset past it, or -1 when it does
406
+ * not fit, the lists differ in length, or a value is no varint. It writes
407
+ * what it is given: refusing a reserved identifier is the reader's job.
408
+ */
409
+ export const h3PutSettings = (buf: u8[], at: i32, end: i32, ids: i64[], values: i64[]): i32 => {
410
+ const n: i32 = toI32(ids.length)
411
+ if (n !== toI32(values.length)) {
412
+ return -1
413
+ }
414
+ let length: i64 = 0
415
+ for (let k: i32 = 0; k < n && k < toI32(ids.length) && k < toI32(values.length); k++) {
416
+ const a: i32 = quicVarintSize(ids[k])
417
+ const b: i32 = quicVarintSize(values[k])
418
+ if (a === 0 || b === 0) {
419
+ return -1
420
+ }
421
+ length = length + toI64(a + b)
422
+ }
423
+ let p: i32 = h3PutFrameHeader(buf, at, end, H3_FRAME_SETTINGS, length)
424
+ if (p < 0 || toI64(p) + length > toI64(end)) {
425
+ return -1
426
+ }
427
+ for (let k: i32 = 0; k < n && k < toI32(ids.length) && k < toI32(values.length); k++) {
428
+ p = h3PutVarint(buf, p, end, ids[k])
429
+ p = h3PutVarint(buf, p, end, values[k])
430
+ }
431
+ return p
432
+ }
433
+
434
+ /**
435
+ * Writes a frame whose payload is one identifier — GOAWAY, MAX_PUSH_ID or
436
+ * CANCEL_PUSH (§7.2.6, §7.2.7, §7.2.3) — at `buf[at]`, before `end`; answers
437
+ * the offset past it, or -1.
438
+ */
439
+ export const h3PutIdFrame = (buf: u8[], at: i32, end: i32, type: i64, id: i64): i32 => {
440
+ const size: i32 = quicVarintSize(id)
441
+ if (size === 0) {
442
+ return -1
443
+ }
444
+ const p: i32 = h3PutFrameHeader(buf, at, end, type, toI64(size))
445
+ return p < 0 ? -1 : h3PutVarint(buf, p, end, id)
446
+ }
447
+
448
+ /**
449
+ * The identifier a GOAWAY, MAX_PUSH_ID or CANCEL_PUSH payload
450
+ * `buf[off, off + len)` carries, or -1 when the payload is not exactly one
451
+ * varint, which is H3_FRAME_ERROR (§7.1).
452
+ */
453
+ export const h3ReadIdPayload = (buf: u8[], off: i32, len: i32): i64 => {
454
+ h3CheckWindow("h3ReadIdPayload", buf, off, len)
455
+ const id: i64 = quicVarintRead(buf, off, off + len)
456
+ return id >= 0 && quicVarintLength(buf, off) === len ? id : H3_FRAME_NONE
457
+ }