@amritk/nish-x86_64-linux 0.16.0 → 0.17.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 (55) 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 +30 -7
  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-wasm.c +1 -0
  11. package/runtime/runtime.c +46 -8
  12. package/runtime/shim.mjs +114 -5
  13. package/scripts/build.sh +1 -3
  14. package/std/README.md +166 -35
  15. package/std/crypto/aes.ts +51 -8
  16. package/std/crypto/base64url.ts +25 -12
  17. package/std/crypto/chacha20poly1305.ts +43 -29
  18. package/std/crypto/ct.ts +6 -1
  19. package/std/crypto/hkdf.ts +364 -3
  20. package/std/crypto/hmac.ts +307 -7
  21. package/std/crypto/p256.ts +148 -27
  22. package/std/crypto/sha1.ts +147 -0
  23. package/std/crypto/sha256.ts +3 -2
  24. package/std/crypto/x25519.ts +88 -36
  25. package/std/crypto/x509.ts +130 -47
  26. package/std/net/hpack.ts +1094 -0
  27. package/std/net/http-fields.ts +604 -0
  28. package/std/net/http1-server.ts +1389 -0
  29. package/std/net/http1.ts +1492 -0
  30. package/std/net/http2-frame.ts +745 -0
  31. package/std/net/http2-tls.ts +216 -0
  32. package/std/net/http2.ts +1433 -0
  33. package/std/net/http3-frame.ts +457 -0
  34. package/std/net/http3-server.ts +905 -0
  35. package/std/net/http3.ts +2145 -0
  36. package/std/net/qpack.ts +1163 -0
  37. package/std/net/quic-conn-ack.ts +206 -0
  38. package/std/net/quic-conn-cid.ts +425 -0
  39. package/std/net/quic-conn-params.ts +392 -0
  40. package/std/net/quic-datagram.ts +130 -0
  41. package/std/net/quic-frame.ts +1206 -0
  42. package/std/net/quic-listener.ts +653 -0
  43. package/std/net/quic-packet.ts +1474 -0
  44. package/std/net/quic-recovery.ts +849 -0
  45. package/std/net/quic-stream.ts +1603 -0
  46. package/std/net/quic.ts +3214 -0
  47. package/std/net/tls/codec.ts +1089 -0
  48. package/std/net/tls/record-server.ts +1173 -0
  49. package/std/net/tls/record.ts +621 -0
  50. package/std/net/tls/schedule.ts +429 -0
  51. package/std/net/tls-tcp.ts +410 -0
  52. package/std/net/tls.ts +1368 -0
  53. package/std/net/websocket.ts +823 -0
  54. package/std/net/webtransport.ts +1334 -0
  55. package/std/secret.ts +62 -0
@@ -0,0 +1,745 @@
1
+ /**
2
+ * `nish/net/http2-frame` — the frames of HTTP/2 (RFC 9113 §4 and §6): the
3
+ * nine-byte header, every one of the ten frame types read and written, and
4
+ * the size, flag, padding and stream-identifier rules each one carries.
5
+ *
6
+ * It is sans-IO, like every lane of WP34 §5 below the sockets, and it never
7
+ * allocates: a frame is read out of the caller's buffer into an `Http2Frame`
8
+ * the caller reuses, its content left where it lies as a window, and a frame
9
+ * is written into the caller's buffer at an offset.
10
+ *
11
+ * import { Http2Frame, http2ReadHeader, http2ParseFrame, http2WriteData } from "nish/net/http2-frame";
12
+ *
13
+ * const frame = new Http2Frame();
14
+ * if (http2ReadHeader(frame, buf, off, len) && len >= 9 + frame.length) {
15
+ * const error: i32 = http2ParseFrame(frame, buf, off, maxFrameSize);
16
+ * // 0, or the connection error to close with; frame.streamError, the
17
+ * // stream error to reset the stream with; then buf[frame.contentStart ..
18
+ * // frame.contentStart + frame.contentLength) is the DATA, the header
19
+ * // block fragment or the GOAWAY debug data.
20
+ * }
21
+ * const end: i32 = http2WriteData(out, at, streamId, body, 0, n, H2_FLAG_END_STREAM, -1);
22
+ *
23
+ * **What a parse refuses**, as the error code RFC 9113 names, which closes
24
+ * the connection: a frame longer than the receiver's SETTINGS_MAX_FRAME_SIZE
25
+ * (FRAME_SIZE_ERROR, §4.2); DATA, HEADERS, PRIORITY, RST_STREAM,
26
+ * PUSH_PROMISE or CONTINUATION on stream 0, and SETTINGS, PING or GOAWAY on
27
+ * any other (PROTOCOL_ERROR); a frame too short for its fixed fields, a
28
+ * RST_STREAM or WINDOW_UPDATE that is not four bytes, a PING that is not
29
+ * eight, a SETTINGS whose length is not a multiple of six or an ACK with a
30
+ * payload, and a GOAWAY under eight (FRAME_SIZE_ERROR); padding as long as
31
+ * what it pads or longer (PROTOCOL_ERROR, §6.1); and a WINDOW_UPDATE of zero
32
+ * on the connection (PROTOCOL_ERROR, §6.9). Two refusals only reset the
33
+ * stream, and come back in `streamError` with a parse that answers 0: a
34
+ * PRIORITY of the wrong length (FRAME_SIZE_ERROR, §6.3), and a WINDOW_UPDATE
35
+ * of zero on a stream (PROTOCOL_ERROR). A HEADERS or PRIORITY that makes a
36
+ * stream depend on itself is the same (PROTOCOL_ERROR, §5.3.1). A frame of a
37
+ * type this module does not know is read and answers 0: §4.1 says it is
38
+ * ignored, and that is the connection's to do.
39
+ *
40
+ * SETTINGS values are checked one at a time by `http2SettingError`, because
41
+ * which value a refusal names is the reader's business (§6.5.2): an
42
+ * ENABLE_PUSH or ENABLE_CONNECT_PROTOCOL other than 0 or 1, and a
43
+ * MAX_FRAME_SIZE outside 2^14 to 2^24 − 1, are PROTOCOL_ERROR; an
44
+ * INITIAL_WINDOW_SIZE past 2^31 − 1 is FLOW_CONTROL_ERROR.
45
+ *
46
+ * **The writers** answer the offset after the frame, or -1 when it would not
47
+ * fit in the buffer, which is then unchanged; a window outside its source
48
+ * buffer, a stream identifier outside 0 to 2^31 − 1 and padding past 255 are
49
+ * the program's mistakes and panic. They write any frame the reader accepts
50
+ * and none it refuses, so a program cannot send what its peer must close for.
51
+ *
52
+ * Private helpers share the importing program's flat symbol namespace
53
+ * (`docs/wp26-stdlib.md` §3e), which is why each one carries the module's name.
54
+ *
55
+ * Written from RFC 9113, not ported from another implementation.
56
+ */
57
+
58
+ /** The size of a frame header: a 24-bit length, a type, flags and a 31-bit stream identifier (§4.1). */
59
+ export const H2_FRAME_HEADER_SIZE: i32 = 9
60
+
61
+ /** The frame types of §6. */
62
+ export const H2_FRAME_DATA: i32 = 0
63
+ export const H2_FRAME_HEADERS: i32 = 1
64
+ export const H2_FRAME_PRIORITY: i32 = 2
65
+ export const H2_FRAME_RST_STREAM: i32 = 3
66
+ export const H2_FRAME_SETTINGS: i32 = 4
67
+ export const H2_FRAME_PUSH_PROMISE: i32 = 5
68
+ export const H2_FRAME_PING: i32 = 6
69
+ export const H2_FRAME_GOAWAY: i32 = 7
70
+ export const H2_FRAME_WINDOW_UPDATE: i32 = 8
71
+ export const H2_FRAME_CONTINUATION: i32 = 9
72
+
73
+ /** END_STREAM on DATA and HEADERS; ACK on SETTINGS and PING, the same bit. */
74
+ export const H2_FLAG_END_STREAM: i32 = 1
75
+ export const H2_FLAG_ACK: i32 = 1
76
+ /** END_HEADERS on HEADERS, PUSH_PROMISE and CONTINUATION. */
77
+ export const H2_FLAG_END_HEADERS: i32 = 4
78
+ /** PADDED on DATA, HEADERS and PUSH_PROMISE. */
79
+ export const H2_FLAG_PADDED: i32 = 8
80
+ /** PRIORITY on HEADERS. */
81
+ export const H2_FLAG_PRIORITY: i32 = 32
82
+
83
+ /** The error codes of §7. */
84
+ export const H2_NO_ERROR: i32 = 0
85
+ export const H2_PROTOCOL_ERROR: i32 = 1
86
+ export const H2_INTERNAL_ERROR: i32 = 2
87
+ export const H2_FLOW_CONTROL_ERROR: i32 = 3
88
+ export const H2_SETTINGS_TIMEOUT: i32 = 4
89
+ export const H2_STREAM_CLOSED: i32 = 5
90
+ export const H2_FRAME_SIZE_ERROR: i32 = 6
91
+ export const H2_REFUSED_STREAM: i32 = 7
92
+ export const H2_CANCEL: i32 = 8
93
+ export const H2_COMPRESSION_ERROR: i32 = 9
94
+ export const H2_CONNECT_ERROR: i32 = 10
95
+ export const H2_ENHANCE_YOUR_CALM: i32 = 11
96
+ export const H2_INADEQUATE_SECURITY: i32 = 12
97
+ export const H2_HTTP_1_1_REQUIRED: i32 = 13
98
+
99
+ /** The settings of §6.5.2, and RFC 8441's. */
100
+ export const H2_SETTINGS_HEADER_TABLE_SIZE: i32 = 1
101
+ export const H2_SETTINGS_ENABLE_PUSH: i32 = 2
102
+ export const H2_SETTINGS_MAX_CONCURRENT_STREAMS: i32 = 3
103
+ export const H2_SETTINGS_INITIAL_WINDOW_SIZE: i32 = 4
104
+ export const H2_SETTINGS_MAX_FRAME_SIZE: i32 = 5
105
+ export const H2_SETTINGS_MAX_HEADER_LIST_SIZE: i32 = 6
106
+ export const H2_SETTINGS_ENABLE_CONNECT_PROTOCOL: i32 = 8
107
+
108
+ /** A window's initial size, and the largest a window may reach (§6.9.1, §6.9.2). */
109
+ export const H2_DEFAULT_WINDOW: i32 = 65535
110
+ export const H2_MAX_WINDOW: i32 = 2147483647
111
+
112
+ /** SETTINGS_MAX_FRAME_SIZE's initial value, which is also its least, and its greatest (§6.5.2). */
113
+ export const H2_DEFAULT_MAX_FRAME: i32 = 16384
114
+ export const H2_MAX_FRAME_LIMIT: i32 = 16777215
115
+
116
+ /** The size of one setting: a 16-bit identifier and a 32-bit value. */
117
+ export const H2_SETTING_SIZE: i32 = 6
118
+
119
+ /** A typed zero: a bare literal is an `f64` under `--number-mode f64`. */
120
+ const H2_FRAME_ZERO: i32 = 0
121
+
122
+ /** A typed one: the Pad Length field's size. */
123
+ const H2_FRAME_ONE: i32 = 1
124
+
125
+ /** A typed -1, for a field that is absent. */
126
+ const H2_FRAME_NONE: i32 = -1
127
+
128
+ /** The exclusive bit of a stream dependency (§5.3.1), above the 31-bit identifier. */
129
+ const H2_FRAME_EXCLUSIVE: i64 = 2147483648
130
+
131
+ /** The largest 32-bit value, which bounds a setting's value. */
132
+ const H2_FRAME_U32_MAX: i64 = 4294967295
133
+
134
+ /**
135
+ * One frame, as `http2ReadHeader` and `http2ParseFrame` read it. Every field
136
+ * is overwritten by the next read, so one frame serves a whole connection.
137
+ */
138
+ export class Http2Frame {
139
+ /** The payload's length, from the header. */
140
+ length: i32 = 0
141
+ type: i32 = 0
142
+ flags: i32 = 0
143
+ /** The stream identifier, the reserved bit dropped. */
144
+ streamId: i32 = 0
145
+ /** Where the payload starts in the buffer read. */
146
+ payload: i32 = 0
147
+ /**
148
+ * The frame's content, `[contentStart, contentStart + contentLength)` of the
149
+ * buffer: DATA's data, a header block fragment, PING's eight bytes, GOAWAY's
150
+ * debug data, SETTINGS' settings; padding and fixed fields left out.
151
+ */
152
+ contentStart: i32 = 0
153
+ contentLength: i32 = 0
154
+ /** The Pad Length field, or -1 without PADDED. */
155
+ padLength: i32 = -1
156
+ /** Whether a HEADERS frame carried the PRIORITY fields, as a PRIORITY frame always does. */
157
+ hasPriority: boolean = false
158
+ exclusive: boolean = false
159
+ dependency: i32 = 0
160
+ /** The weight, 1 to 256. */
161
+ weight: i32 = 0
162
+ /** RST_STREAM's and GOAWAY's error code, a 32-bit value. */
163
+ errorCode: i64 = 0
164
+ /** GOAWAY's last stream identifier. */
165
+ lastStreamId: i32 = 0
166
+ /** WINDOW_UPDATE's increment. */
167
+ increment: i32 = 0
168
+ /** PUSH_PROMISE's promised stream identifier. */
169
+ promisedId: i32 = 0
170
+ /** A stream error the frame is, or 0: the stream is reset and the connection lives. */
171
+ streamError: i32 = 0
172
+
173
+ /** Whether the flag `bit` is set. */
174
+ has(bit: i32): boolean {
175
+ return (this.flags & bit) !== 0
176
+ }
177
+ }
178
+
179
+ /** Panics unless `[off, off + len)` lies inside `buf`: a window outside it is the program's mistake. */
180
+ const http2FrameCheckWindow = (what: string, buf: u8[], off: i32, len: i32): void => {
181
+ if (off < 0 || len < 0 || toI64(off) + toI64(len) > toI64(buf.length)) {
182
+ panic(`${what}: the window [${off}, ${off} + ${len}) is outside a buffer of ${buf.length} bytes`)
183
+ }
184
+ }
185
+
186
+ /** Panics unless `id` is a stream identifier, 0 to 2^31 − 1. */
187
+ const http2FrameCheckStream = (what: string, id: i32): void => {
188
+ if (id < 0) {
189
+ panic(`${what}: a stream identifier of ${id}`)
190
+ }
191
+ }
192
+
193
+ /** The big-endian 32-bit value at `buf[at]`. */
194
+ const http2FrameU32 = (buf: u8[], at: i32): i64 =>
195
+ (toI64(buf[at]) << toI64(24)) |
196
+ (toI64(buf[at + 1]) << toI64(16)) |
197
+ (toI64(buf[at + 2]) << toI64(8)) |
198
+ toI64(buf[at + 3])
199
+
200
+ /** The big-endian 31-bit value at `buf[at]`, its reserved top bit dropped. */
201
+ const http2FrameU31 = (buf: u8[], at: i32): i32 => toI32(http2FrameU32(buf, at) & toI64(2147483647))
202
+
203
+ /** Writes `value`'s low 32 bits big-endian at `out[at]`. */
204
+ const http2FramePut32 = (out: u8[], at: i32, value: i64): void => {
205
+ out[at] = toU8((value >> toI64(24)) & toI64(255))
206
+ out[at + 1] = toU8((value >> toI64(16)) & toI64(255))
207
+ out[at + 2] = toU8((value >> toI64(8)) & toI64(255))
208
+ out[at + 3] = toU8(value & toI64(255))
209
+ }
210
+
211
+ /**
212
+ * Reads the nine-byte header at `buf[off]` into `frame`, when `len` bytes from
213
+ * `off` hold one, and answers whether they did. The payload need not be there
214
+ * yet: a reader checks `frame.length` against its limit before it waits for
215
+ * the rest, which is what keeps a peer from making it buffer more.
216
+ */
217
+ export const http2ReadHeader = (frame: Http2Frame, buf: u8[], off: i32, len: i32): boolean => {
218
+ http2FrameCheckWindow("http2ReadHeader", buf, off, len)
219
+ if (len < H2_FRAME_HEADER_SIZE) {
220
+ return false
221
+ }
222
+ frame.length = (toI32(buf[off]) << 16) | (toI32(buf[off + 1]) << 8) | toI32(buf[off + 2])
223
+ frame.type = toI32(buf[off + 3])
224
+ frame.flags = toI32(buf[off + 4])
225
+ frame.streamId = http2FrameU31(buf, off + 5)
226
+ frame.payload = off + H2_FRAME_HEADER_SIZE
227
+ return true
228
+ }
229
+
230
+ /**
231
+ * Takes the Pad Length field off the front of a padded frame's payload, when
232
+ * PADDED is set, leaving `fixed` bytes of fixed fields after it; sets the
233
+ * content window to what lies between them and the padding. Answers 0 or the
234
+ * connection error.
235
+ */
236
+ const http2FrameUnpad = (frame: Http2Frame, buf: u8[], fixed: i32): i32 => {
237
+ const padded: boolean = frame.has(H2_FLAG_PADDED)
238
+ const head: i32 = (padded ? H2_FRAME_ONE : H2_FRAME_ZERO) + fixed
239
+ if (frame.length < head) {
240
+ return H2_FRAME_SIZE_ERROR
241
+ }
242
+ frame.padLength = padded ? toI32(buf[frame.payload]) : H2_FRAME_NONE
243
+ const pad: i32 = padded ? frame.padLength : H2_FRAME_ZERO
244
+ if (pad > frame.length - head) {
245
+ return H2_PROTOCOL_ERROR
246
+ }
247
+ frame.contentStart = frame.payload + head
248
+ frame.contentLength = frame.length - head - pad
249
+ return H2_NO_ERROR
250
+ }
251
+
252
+ /** Reads the five bytes of priority fields at `buf[at]` into `frame` (§6.3). A stream may not depend on itself (§5.3.1). */
253
+ const http2FramePriority = (frame: Http2Frame, buf: u8[], at: i32): void => {
254
+ frame.hasPriority = true
255
+ frame.exclusive = (toI32(buf[at]) & 128) !== 0
256
+ frame.dependency = http2FrameU31(buf, at)
257
+ frame.weight = toI32(buf[at + 4]) + 1
258
+ if (frame.dependency === frame.streamId) {
259
+ frame.streamError = H2_PROTOCOL_ERROR
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Parses the frame whose header `http2ReadHeader` read from `buf[off]`; its
265
+ * whole payload must be in `buf`. `maxFrameSize` is the reader's
266
+ * SETTINGS_MAX_FRAME_SIZE. Answers 0, with the type's fields set and any
267
+ * stream error in `streamError`, or the connection error the module comment
268
+ * lists.
269
+ */
270
+ export const http2ParseFrame = (frame: Http2Frame, buf: u8[], off: i32, maxFrameSize: i32): i32 => {
271
+ http2FrameCheckWindow("http2ParseFrame", buf, off, H2_FRAME_HEADER_SIZE + frame.length)
272
+ frame.payload = off + H2_FRAME_HEADER_SIZE
273
+ frame.contentStart = frame.payload
274
+ frame.contentLength = frame.length
275
+ frame.padLength = H2_FRAME_NONE
276
+ frame.hasPriority = false
277
+ frame.exclusive = false
278
+ frame.dependency = 0
279
+ frame.weight = 0
280
+ frame.streamError = 0
281
+ if (frame.length > maxFrameSize) {
282
+ return H2_FRAME_SIZE_ERROR
283
+ }
284
+ const p: i32 = frame.payload
285
+ const onStream: boolean = frame.streamId !== 0
286
+ switch (frame.type) {
287
+ case H2_FRAME_DATA:
288
+ return onStream ? http2FrameUnpad(frame, buf, H2_FRAME_ZERO) : H2_PROTOCOL_ERROR
289
+ case H2_FRAME_HEADERS: {
290
+ if (!onStream) {
291
+ return H2_PROTOCOL_ERROR
292
+ }
293
+ const priority: boolean = frame.has(H2_FLAG_PRIORITY)
294
+ const error: i32 = http2FrameUnpad(frame, buf, priority ? 5 : 0)
295
+ if (error === H2_NO_ERROR && priority) {
296
+ http2FramePriority(frame, buf, frame.contentStart - 5)
297
+ }
298
+ return error
299
+ }
300
+ case H2_FRAME_PRIORITY:
301
+ if (!onStream) {
302
+ return H2_PROTOCOL_ERROR
303
+ }
304
+ if (frame.length !== 5) {
305
+ frame.streamError = H2_FRAME_SIZE_ERROR
306
+ return H2_NO_ERROR
307
+ }
308
+ http2FramePriority(frame, buf, p)
309
+ return H2_NO_ERROR
310
+ case H2_FRAME_RST_STREAM:
311
+ if (!onStream) {
312
+ return H2_PROTOCOL_ERROR
313
+ }
314
+ if (frame.length !== 4) {
315
+ return H2_FRAME_SIZE_ERROR
316
+ }
317
+ frame.errorCode = http2FrameU32(buf, p)
318
+ return H2_NO_ERROR
319
+ case H2_FRAME_SETTINGS:
320
+ if (onStream) {
321
+ return H2_PROTOCOL_ERROR
322
+ }
323
+ if (frame.has(H2_FLAG_ACK) ? frame.length > 0 : frame.length % H2_SETTING_SIZE !== 0) {
324
+ return H2_FRAME_SIZE_ERROR
325
+ }
326
+ return H2_NO_ERROR
327
+ case H2_FRAME_PUSH_PROMISE: {
328
+ if (!onStream) {
329
+ return H2_PROTOCOL_ERROR
330
+ }
331
+ const error: i32 = http2FrameUnpad(frame, buf, 4)
332
+ if (error === H2_NO_ERROR) {
333
+ frame.promisedId = http2FrameU31(buf, frame.contentStart - 4)
334
+ }
335
+ return error
336
+ }
337
+ case H2_FRAME_PING:
338
+ if (onStream) {
339
+ return H2_PROTOCOL_ERROR
340
+ }
341
+ return frame.length === 8 ? H2_NO_ERROR : H2_FRAME_SIZE_ERROR
342
+ case H2_FRAME_GOAWAY:
343
+ if (onStream) {
344
+ return H2_PROTOCOL_ERROR
345
+ }
346
+ if (frame.length < 8) {
347
+ return H2_FRAME_SIZE_ERROR
348
+ }
349
+ frame.lastStreamId = http2FrameU31(buf, p)
350
+ frame.errorCode = http2FrameU32(buf, p + 4)
351
+ frame.contentStart = p + 8
352
+ frame.contentLength = frame.length - 8
353
+ return H2_NO_ERROR
354
+ case H2_FRAME_WINDOW_UPDATE:
355
+ if (frame.length !== 4) {
356
+ return H2_FRAME_SIZE_ERROR
357
+ }
358
+ frame.increment = http2FrameU31(buf, p)
359
+ if (frame.increment === 0) {
360
+ if (!onStream) {
361
+ return H2_PROTOCOL_ERROR
362
+ }
363
+ frame.streamError = H2_PROTOCOL_ERROR
364
+ }
365
+ return H2_NO_ERROR
366
+ case H2_FRAME_CONTINUATION:
367
+ return onStream ? H2_NO_ERROR : H2_PROTOCOL_ERROR
368
+ default:
369
+ return H2_NO_ERROR
370
+ }
371
+ }
372
+
373
+ /** How many settings a SETTINGS frame `http2ParseFrame` accepted carries. */
374
+ export const http2SettingCount = (frame: Http2Frame): i32 => frame.length / H2_SETTING_SIZE
375
+
376
+ /** The identifier of setting `k` of the SETTINGS frame in `buf`. */
377
+ export const http2SettingId = (frame: Http2Frame, buf: u8[], k: i32): i32 => {
378
+ const at: i32 = frame.payload + k * H2_SETTING_SIZE
379
+ return (toI32(buf[at]) << 8) | toI32(buf[at + 1])
380
+ }
381
+
382
+ /** The value of setting `k` of the SETTINGS frame in `buf`, a 32-bit value. */
383
+ export const http2SettingValue = (frame: Http2Frame, buf: u8[], k: i32): i64 =>
384
+ http2FrameU32(buf, frame.payload + k * H2_SETTING_SIZE + 2)
385
+
386
+ /**
387
+ * The connection error a setting's value is, or 0 (§6.5.2, RFC 8441 §3). A
388
+ * setting this module does not know is not an error: §6.5.2 says it is
389
+ * ignored.
390
+ */
391
+ export const http2SettingError = (id: i32, value: i64): i32 => {
392
+ switch (id) {
393
+ case H2_SETTINGS_ENABLE_PUSH:
394
+ case H2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
395
+ return value > toI64(1) ? H2_PROTOCOL_ERROR : H2_NO_ERROR
396
+ case H2_SETTINGS_INITIAL_WINDOW_SIZE:
397
+ return value > toI64(H2_MAX_WINDOW) ? H2_FLOW_CONTROL_ERROR : H2_NO_ERROR
398
+ case H2_SETTINGS_MAX_FRAME_SIZE:
399
+ return value < toI64(H2_DEFAULT_MAX_FRAME) || value > toI64(H2_MAX_FRAME_LIMIT)
400
+ ? H2_PROTOCOL_ERROR
401
+ : H2_NO_ERROR
402
+ default:
403
+ return H2_NO_ERROR
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Writes a frame header at `out[at]` for a payload of `length` bytes, and
409
+ * answers where the payload goes, or -1 when the header and the payload would
410
+ * not both fit.
411
+ */
412
+ export const http2WriteHeader = (
413
+ out: u8[],
414
+ at: i32,
415
+ length: i32,
416
+ type: i32,
417
+ flags: i32,
418
+ streamId: i32
419
+ ): i32 => {
420
+ http2FrameCheckStream("http2WriteHeader", streamId)
421
+ if (at < 0 || length < 0 || length > H2_MAX_FRAME_LIMIT) {
422
+ panic(`http2WriteHeader: a frame of ${length} bytes at ${at}`)
423
+ }
424
+ if (toI64(at) + toI64(H2_FRAME_HEADER_SIZE) + toI64(length) > toI64(out.length)) {
425
+ return -1
426
+ }
427
+ out[at] = toU8((length >> 16) & 255)
428
+ out[at + 1] = toU8((length >> 8) & 255)
429
+ out[at + 2] = toU8(length & 255)
430
+ out[at + 3] = toU8(type)
431
+ out[at + 4] = toU8(flags)
432
+ http2FramePut32(out, at + 5, toI64(streamId))
433
+ return at + H2_FRAME_HEADER_SIZE
434
+ }
435
+
436
+ /** Copies `src[off, off + len)` to `out[at]`; the callers have checked both windows. */
437
+ const http2FrameCopy = (out: u8[], at: i32, src: u8[], off: i32, len: i32): void => {
438
+ for (let k: i32 = 0; k < len; k++) {
439
+ out[at + k] = src[off + k]
440
+ }
441
+ }
442
+
443
+ /**
444
+ * The shared shape of DATA, HEADERS, PUSH_PROMISE and CONTINUATION: the
445
+ * header, the Pad Length when `pad` is 0 to 255 (-1 for none), `fixed` bytes
446
+ * the caller fills at the answer's offset minus `fixed`, the content and
447
+ * the padding. Answers the offset after the frame, or -1.
448
+ */
449
+ const http2FrameWritePadded = (
450
+ out: u8[],
451
+ at: i32,
452
+ type: i32,
453
+ flags: i32,
454
+ streamId: i32,
455
+ fixed: i32,
456
+ src: u8[],
457
+ off: i32,
458
+ len: i32,
459
+ pad: i32
460
+ ): i32 => {
461
+ http2FrameCheckWindow("http2Write", src, off, len)
462
+ if (pad > 255) {
463
+ panic(`http2Write: ${pad} bytes of padding, past 255`)
464
+ }
465
+ const padded: boolean = pad >= 0
466
+ const head: i32 = (padded ? H2_FRAME_ONE : H2_FRAME_ZERO) + fixed
467
+ const tail: i32 = padded ? pad : H2_FRAME_ZERO
468
+ const at2: i32 = http2WriteHeader(
469
+ out,
470
+ at,
471
+ head + len + tail,
472
+ type,
473
+ padded ? flags | H2_FLAG_PADDED : flags & ~H2_FLAG_PADDED,
474
+ streamId
475
+ )
476
+ if (at2 < 0) {
477
+ return -1
478
+ }
479
+ if (padded) {
480
+ out[at2] = toU8(pad)
481
+ }
482
+ const body: i32 = at2 + head
483
+ http2FrameCopy(out, body, src, off, len)
484
+ out.fill(0, body + len, body + len + tail)
485
+ return body + len + tail
486
+ }
487
+
488
+ /** Writes a DATA frame of `src[off, off + len)` on `streamId`, with `flags` (END_STREAM) and `pad` bytes of padding, -1 for none (§6.1). */
489
+ export const http2WriteData = (
490
+ out: u8[],
491
+ at: i32,
492
+ streamId: i32,
493
+ src: u8[],
494
+ off: i32,
495
+ len: i32,
496
+ flags: i32,
497
+ pad: i32
498
+ ): i32 => {
499
+ if (streamId === 0) {
500
+ panic("http2WriteData: DATA on stream 0")
501
+ }
502
+ return http2FrameWritePadded(
503
+ out,
504
+ at,
505
+ H2_FRAME_DATA,
506
+ flags & H2_FLAG_END_STREAM,
507
+ streamId,
508
+ H2_FRAME_ZERO,
509
+ src,
510
+ off,
511
+ len,
512
+ pad
513
+ )
514
+ }
515
+
516
+ /**
517
+ * Writes a HEADERS frame carrying the header block fragment `src[off, off +
518
+ * len)` on `streamId`, with `flags` (END_STREAM, END_HEADERS) and `pad`
519
+ * bytes of padding, -1 for none (§6.2). It never sets PRIORITY, which RFC
520
+ * 9113 deprecates.
521
+ */
522
+ export const http2WriteHeaders = (
523
+ out: u8[],
524
+ at: i32,
525
+ streamId: i32,
526
+ src: u8[],
527
+ off: i32,
528
+ len: i32,
529
+ flags: i32,
530
+ pad: i32
531
+ ): i32 => {
532
+ if (streamId === 0) {
533
+ panic("http2WriteHeaders: HEADERS on stream 0")
534
+ }
535
+ const allowed: i32 = H2_FLAG_END_STREAM | H2_FLAG_END_HEADERS
536
+ return http2FrameWritePadded(
537
+ out,
538
+ at,
539
+ H2_FRAME_HEADERS,
540
+ flags & allowed,
541
+ streamId,
542
+ H2_FRAME_ZERO,
543
+ src,
544
+ off,
545
+ len,
546
+ pad
547
+ )
548
+ }
549
+
550
+ /** Writes a PRIORITY frame: `streamId` depends on `dependency`, exclusively or not, with `weight` 1 to 256 (§6.3). */
551
+ export const http2WritePriority = (
552
+ out: u8[],
553
+ at: i32,
554
+ streamId: i32,
555
+ dependency: i32,
556
+ exclusive: boolean,
557
+ weight: i32
558
+ ): i32 => {
559
+ http2FrameCheckStream("http2WritePriority", dependency)
560
+ if (streamId === 0 || streamId === dependency || weight < 1 || weight > 256) {
561
+ panic(`http2WritePriority: stream ${streamId} on ${dependency} with weight ${weight}`)
562
+ }
563
+ const body: i32 = http2WriteHeader(out, at, 5, H2_FRAME_PRIORITY, H2_FRAME_ZERO, streamId)
564
+ if (body < 0) {
565
+ return -1
566
+ }
567
+ http2FramePut32(out, body, toI64(dependency) | (exclusive ? H2_FRAME_EXCLUSIVE : toI64(0)))
568
+ out[body + 4] = toU8(weight - 1)
569
+ return body + 5
570
+ }
571
+
572
+ /** Writes a RST_STREAM frame closing `streamId` with `code` (§6.4). */
573
+ export const http2WriteRstStream = (out: u8[], at: i32, streamId: i32, code: i64): i32 => {
574
+ if (streamId === 0) {
575
+ panic("http2WriteRstStream: RST_STREAM on stream 0")
576
+ }
577
+ const body: i32 = http2WriteHeader(out, at, 4, H2_FRAME_RST_STREAM, H2_FRAME_ZERO, streamId)
578
+ if (body < 0) {
579
+ return -1
580
+ }
581
+ http2FramePut32(out, body, code)
582
+ return body + 4
583
+ }
584
+
585
+ /**
586
+ * Writes a SETTINGS frame of the settings `ids[k]` = `values[k]` (§6.5).
587
+ * A value `http2SettingError` refuses panics, as two lists of different
588
+ * lengths do.
589
+ */
590
+ export const http2WriteSettings = (out: u8[], at: i32, ids: i32[], values: i64[]): i32 => {
591
+ const n: i32 = toI32(ids.length)
592
+ if (n !== toI32(values.length)) {
593
+ panic(`http2WriteSettings: ${n} identifiers for ${values.length} values`)
594
+ }
595
+ for (let k: i32 = 0; k < n && k < toI32(ids.length) && k < toI32(values.length); k++) {
596
+ if (
597
+ ids[k] < 0 ||
598
+ ids[k] > 65535 ||
599
+ values[k] < toI64(0) ||
600
+ values[k] > H2_FRAME_U32_MAX ||
601
+ http2SettingError(ids[k], values[k]) !== H2_NO_ERROR
602
+ ) {
603
+ panic(`http2WriteSettings: setting ${ids[k]} = ${values[k]}`)
604
+ }
605
+ }
606
+ const body: i32 = http2WriteHeader(
607
+ out,
608
+ at,
609
+ n * H2_SETTING_SIZE,
610
+ H2_FRAME_SETTINGS,
611
+ H2_FRAME_ZERO,
612
+ H2_FRAME_ZERO
613
+ )
614
+ if (body < 0) {
615
+ return -1
616
+ }
617
+ for (let k: i32 = 0; k < n && k < toI32(ids.length) && k < toI32(values.length); k++) {
618
+ const at2: i32 = body + k * H2_SETTING_SIZE
619
+ if (at2 >= 0 && at2 < toI32(out.length) && at2 + 1 < toI32(out.length)) {
620
+ out[at2] = toU8((ids[k] >> 8) & 255)
621
+ out[at2 + 1] = toU8(ids[k] & 255)
622
+ }
623
+ http2FramePut32(out, at2 + 2, values[k])
624
+ }
625
+ return body + n * H2_SETTING_SIZE
626
+ }
627
+
628
+ /** Writes the empty SETTINGS frame with ACK that acknowledges the peer's (§6.5.3). */
629
+ export const http2WriteSettingsAck = (out: u8[], at: i32): i32 =>
630
+ http2WriteHeader(out, at, H2_FRAME_ZERO, H2_FRAME_SETTINGS, H2_FLAG_ACK, H2_FRAME_ZERO)
631
+
632
+ /**
633
+ * Writes a PUSH_PROMISE frame on `streamId` reserving `promisedId`, carrying
634
+ * the header block fragment `src[off, off + len)`, with `flags`
635
+ * (END_HEADERS) and `pad` bytes of padding, -1 for none (§6.6).
636
+ */
637
+ export const http2WritePushPromise = (
638
+ out: u8[],
639
+ at: i32,
640
+ streamId: i32,
641
+ promisedId: i32,
642
+ src: u8[],
643
+ off: i32,
644
+ len: i32,
645
+ flags: i32,
646
+ pad: i32
647
+ ): i32 => {
648
+ http2FrameCheckStream("http2WritePushPromise", promisedId)
649
+ if (streamId === 0 || promisedId === 0) {
650
+ panic(`http2WritePushPromise: stream ${streamId} promising ${promisedId}`)
651
+ }
652
+ const end: i32 = http2FrameWritePadded(
653
+ out,
654
+ at,
655
+ H2_FRAME_PUSH_PROMISE,
656
+ flags & H2_FLAG_END_HEADERS,
657
+ streamId,
658
+ 4,
659
+ src,
660
+ off,
661
+ len,
662
+ pad
663
+ )
664
+ if (end >= 0) {
665
+ http2FramePut32(
666
+ out,
667
+ at + H2_FRAME_HEADER_SIZE + (pad >= 0 ? H2_FRAME_ONE : H2_FRAME_ZERO),
668
+ toI64(promisedId)
669
+ )
670
+ }
671
+ return end
672
+ }
673
+
674
+ /** Writes a PING frame of the eight bytes `opaque[off, off + 8)`, an ACK when `ack` (§6.7). */
675
+ export const http2WritePing = (out: u8[], at: i32, opaque: u8[], off: i32, ack: boolean): i32 => {
676
+ http2FrameCheckWindow("http2WritePing", opaque, off, 8)
677
+ const body: i32 = http2WriteHeader(out, at, 8, H2_FRAME_PING, ack ? H2_FLAG_ACK : 0, H2_FRAME_ZERO)
678
+ if (body < 0) {
679
+ return -1
680
+ }
681
+ http2FrameCopy(out, body, opaque, off, 8)
682
+ return body + 8
683
+ }
684
+
685
+ /** Writes a GOAWAY frame naming `lastStreamId` and `code`, with the debug data `debug[off, off + len)` (§6.8). */
686
+ export const http2WriteGoaway = (
687
+ out: u8[],
688
+ at: i32,
689
+ lastStreamId: i32,
690
+ code: i64,
691
+ debug: u8[],
692
+ off: i32,
693
+ len: i32
694
+ ): i32 => {
695
+ http2FrameCheckStream("http2WriteGoaway", lastStreamId)
696
+ http2FrameCheckWindow("http2WriteGoaway", debug, off, len)
697
+ const body: i32 = http2WriteHeader(out, at, 8 + len, H2_FRAME_GOAWAY, H2_FRAME_ZERO, H2_FRAME_ZERO)
698
+ if (body < 0) {
699
+ return -1
700
+ }
701
+ http2FramePut32(out, body, toI64(lastStreamId))
702
+ http2FramePut32(out, body + 4, code)
703
+ http2FrameCopy(out, body + 8, debug, off, len)
704
+ return body + 8 + len
705
+ }
706
+
707
+ /** Writes a WINDOW_UPDATE frame adding `increment`, 1 to 2^31 − 1, to `streamId`'s window, or the connection's for 0 (§6.9). */
708
+ export const http2WriteWindowUpdate = (out: u8[], at: i32, streamId: i32, increment: i32): i32 => {
709
+ if (increment < 1) {
710
+ panic(`http2WriteWindowUpdate: an increment of ${increment}`)
711
+ }
712
+ const body: i32 = http2WriteHeader(out, at, 4, H2_FRAME_WINDOW_UPDATE, H2_FRAME_ZERO, streamId)
713
+ if (body < 0) {
714
+ return -1
715
+ }
716
+ http2FramePut32(out, body, toI64(increment))
717
+ return body + 4
718
+ }
719
+
720
+ /** Writes a CONTINUATION frame of the header block fragment `src[off, off + len)`, with `flags` (END_HEADERS) (§6.10). */
721
+ export const http2WriteContinuation = (
722
+ out: u8[],
723
+ at: i32,
724
+ streamId: i32,
725
+ src: u8[],
726
+ off: i32,
727
+ len: i32,
728
+ flags: i32
729
+ ): i32 => {
730
+ if (streamId === 0) {
731
+ panic("http2WriteContinuation: CONTINUATION on stream 0")
732
+ }
733
+ return http2FrameWritePadded(
734
+ out,
735
+ at,
736
+ H2_FRAME_CONTINUATION,
737
+ flags & H2_FLAG_END_HEADERS,
738
+ streamId,
739
+ H2_FRAME_ZERO,
740
+ src,
741
+ off,
742
+ len,
743
+ H2_FRAME_NONE
744
+ )
745
+ }