@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,604 @@
1
+ /**
2
+ * `nish/net/http-fields` — the header model HTTP/2 and HTTP/3 share: what a
3
+ * field name and a field value may hold, the pseudo-header fields and the
4
+ * order and count they come in, the connection-specific fields neither
5
+ * version carries, and the shapes of a request, a response and a trailer
6
+ * section.
7
+ *
8
+ * import { HttpFields, HTTP_FIELDS_OK } from "nish/net/http-fields";
9
+ *
10
+ * const request = new HttpFields();
11
+ * if (request.readRequest(decoder.names, decoder.values, extendedConnect) === HTTP_FIELDS_OK) {
12
+ * // request.method, request.path, request.get("content-type"), ...
13
+ * }
14
+ *
15
+ * It is version-neutral on purpose: RFC 9113 §8 and RFC 9114 §4 state the
16
+ * same rules for a field section, and each version answers a section that
17
+ * breaks one in its own words — HTTP/2 with a stream error of type
18
+ * PROTOCOL_ERROR (RFC 9113 §8.1.1), HTTP/3 with H3_MESSAGE_ERROR (RFC 9114
19
+ * §4.1.2). So every check here answers one of the `HTTP_FIELDS_*` codes and
20
+ * nothing more, and nothing here knows about frames, streams or a codec.
21
+ *
22
+ * **What it refuses** (RFC 9110 §5, RFC 9113 §8.2 and §8.3, RFC 9114 §4.2
23
+ * and §4.3, RFC 8441 §4):
24
+ *
25
+ * - a field name that is empty, holds a character outside RFC 9110's `tchar`,
26
+ * or holds an uppercase letter — both versions send names in lowercase and
27
+ * call an uppercase one malformed (`HTTP_FIELDS_BAD_NAME`);
28
+ * - a field value with a NUL, CR or LF anywhere, or a space or tab at either
29
+ * end (`HTTP_FIELDS_BAD_VALUE`) — the first three are what lets a value end
30
+ * a line once the message is turned back into HTTP/1.1;
31
+ * - `connection`, `proxy-connection`, `keep-alive`, `transfer-encoding`,
32
+ * `upgrade`, and `te` with any value but `trailers`
33
+ * (`HTTP_FIELDS_CONNECTION_SPECIFIC`);
34
+ * - a pseudo-header that is not one of the five a request has or the one a
35
+ * response has (`HTTP_FIELDS_PSEUDO_UNKNOWN`), one that appears twice
36
+ * (`_PSEUDO_REPEATED`), one after a regular field (`_PSEUDO_AFTER_FIELD`),
37
+ * one the shape does not allow — `:status` in a request, `:path` in a
38
+ * plain CONNECT, any in a trailer section, `:protocol` where extended
39
+ * CONNECT was not enabled or with a method but CONNECT
40
+ * (`_PSEUDO_FORBIDDEN`) — and a required one that is absent
41
+ * (`_PSEUDO_MISSING`);
42
+ * - a pseudo-header value of the wrong shape: a `:method` or `:protocol`
43
+ * that is not a token, a `:scheme` that is not RFC 3986's, an empty
44
+ * `:path` or one that starts with neither `/` nor is OPTIONS's `*`, an
45
+ * `:authority` with userinfo, a `:status` that is not three digits from
46
+ * 100 to 599 (`HTTP_FIELDS_BAD_PSEUDO_VALUE`);
47
+ * - a `host` field that names another authority than `:authority`, letters
48
+ * compared without case (`HTTP_FIELDS_AUTHORITY_MISMATCH`);
49
+ * - a `content-length` that is not one run of at most eighteen digits, or two
50
+ * that disagree (`HTTP_FIELDS_BAD_CONTENT_LENGTH`). Matching it against
51
+ * the body is the version's job, since only it sees the body.
52
+ *
53
+ * Names and values are octets (`u8[]`), as HPACK and QPACK answer them. A
54
+ * section that passes keeps references to the caller's arrays, not copies, so
55
+ * it is valid for as long as they are.
56
+ *
57
+ * Private helpers share the importing program's flat symbol namespace
58
+ * (`docs/wp26-stdlib.md` §3e), which is why each one carries the module's name.
59
+ *
60
+ * Written from RFC 9110, RFC 9113, RFC 9114 and RFC 8441, not ported from
61
+ * another implementation.
62
+ */
63
+
64
+ /** A field section that has the shape asked for. */
65
+ export const HTTP_FIELDS_OK: i32 = 0
66
+
67
+ /** A field name that is empty, not a token, or not in lowercase. */
68
+ export const HTTP_FIELDS_BAD_NAME: i32 = -1
69
+
70
+ /** A field value with a NUL, CR or LF, or whitespace at either end. */
71
+ export const HTTP_FIELDS_BAD_VALUE: i32 = -2
72
+
73
+ /** A connection-specific field (RFC 9113 §8.2.2, RFC 9114 §4.2). */
74
+ export const HTTP_FIELDS_CONNECTION_SPECIFIC: i32 = -3
75
+
76
+ /** A pseudo-header that neither a request nor a response defines. */
77
+ export const HTTP_FIELDS_PSEUDO_UNKNOWN: i32 = -4
78
+
79
+ /** A pseudo-header that appears more than once. */
80
+ export const HTTP_FIELDS_PSEUDO_REPEATED: i32 = -5
81
+
82
+ /** A pseudo-header after a regular field. */
83
+ export const HTTP_FIELDS_PSEUDO_AFTER_FIELD: i32 = -6
84
+
85
+ /** A pseudo-header the shape requires and the section does not have. */
86
+ export const HTTP_FIELDS_PSEUDO_MISSING: i32 = -7
87
+
88
+ /** A pseudo-header the shape does not allow. */
89
+ export const HTTP_FIELDS_PSEUDO_FORBIDDEN: i32 = -8
90
+
91
+ /** A pseudo-header whose value does not have its field's syntax. */
92
+ export const HTTP_FIELDS_BAD_PSEUDO_VALUE: i32 = -9
93
+
94
+ /** A `host` field that names another authority than `:authority`. */
95
+ export const HTTP_FIELDS_AUTHORITY_MISMATCH: i32 = -10
96
+
97
+ /** A `content-length` that is not a run of digits, or two that disagree. */
98
+ export const HTTP_FIELDS_BAD_CONTENT_LENGTH: i32 = -11
99
+
100
+ /** `contentLength` when the section has no `content-length`. */
101
+ export const HTTP_FIELDS_NO_LENGTH: i64 = -1
102
+
103
+ /** The pseudo-headers, as bits of a mask while a section is read. */
104
+ const HTTP_FIELDS_METHOD: i32 = 1
105
+ const HTTP_FIELDS_SCHEME: i32 = 2
106
+ const HTTP_FIELDS_AUTHORITY: i32 = 4
107
+ const HTTP_FIELDS_PATH: i32 = 8
108
+ const HTTP_FIELDS_PROTOCOL: i32 = 16
109
+ const HTTP_FIELDS_STATUS: i32 = 32
110
+
111
+ /** The longest `content-length` read: eighteen digits stay inside an `i64`. */
112
+ const HTTP_FIELDS_MAX_LENGTH_DIGITS: i32 = 18
113
+
114
+ /** A typed zero, so that a bare literal does not become an `f64` under `--number-mode f64`. */
115
+ const HTTP_FIELDS_ZERO: i32 = 0
116
+
117
+ /** Whether `c` is a `tchar` of RFC 9110 §5.6.2, uppercase letters left out. */
118
+ const httpFieldsLowerTchar = (c: i32): boolean => {
119
+ if ((c >= 97 && c <= 122) || (c >= 48 && c <= 57)) {
120
+ return true
121
+ }
122
+ switch (c) {
123
+ case 33: // !
124
+ case 35: // #
125
+ case 36: // $
126
+ case 37: // %
127
+ case 38: // &
128
+ case 39: // '
129
+ case 42: // *
130
+ case 43: // +
131
+ case 45: // -
132
+ case 46: // .
133
+ case 94: // ^
134
+ case 95: // _
135
+ case 96: // `
136
+ case 124: // |
137
+ case 126: // ~
138
+ return true
139
+ default:
140
+ return false
141
+ }
142
+ }
143
+
144
+ /** Whether `bytes` is a non-empty token, with uppercase letters in it only when `upper`. */
145
+ const httpFieldsAllTchar = (bytes: u8[], upper: boolean): boolean => {
146
+ if (toI32(bytes.length) === 0) {
147
+ return false
148
+ }
149
+ for (const b of bytes) {
150
+ const c: i32 = toI32(b)
151
+ if (!httpFieldsLowerTchar(c) && !(upper && c >= 65 && c <= 90)) {
152
+ return false
153
+ }
154
+ }
155
+ return true
156
+ }
157
+
158
+ /**
159
+ * Whether `name` may be sent as a regular field name in HTTP/2 or HTTP/3: a
160
+ * non-empty token in lowercase (RFC 9110 §5.1, RFC 9113 §8.2.1, RFC 9114
161
+ * §4.2). A pseudo-header's name starts with a colon and is not one.
162
+ */
163
+ export const httpFieldNameValid = (name: u8[]): boolean => httpFieldsAllTchar(name, false)
164
+
165
+ /**
166
+ * Whether `value` may be sent as a field value: no NUL, CR or LF, and no
167
+ * space or tab as its first or last octet (RFC 9113 §8.2.1, RFC 9114 §4.2).
168
+ * Other octets, obs-text included, pass as they are.
169
+ */
170
+ export const httpFieldValueValid = (value: u8[]): boolean => {
171
+ const n: i32 = toI32(value.length)
172
+ for (const b of value) {
173
+ const c: i32 = toI32(b)
174
+ if (c === 0 || c === 10 || c === 13) {
175
+ return false
176
+ }
177
+ }
178
+ if (n === 0) {
179
+ return true
180
+ }
181
+ const first: i32 = toI32(value[0])
182
+ const last: i32 = toI32(value[n - 1])
183
+ return first !== 32 && first !== 9 && last !== 32 && last !== 9
184
+ }
185
+
186
+ /** Whether the octets of `bytes` spell `text` exactly. */
187
+ export const httpFieldIs = (bytes: u8[], text: string): boolean => {
188
+ const n: i32 = toI32(bytes.length)
189
+ if (n !== toI32(text.length)) {
190
+ return false
191
+ }
192
+ for (let k: i32 = 0; k < n && k < toI32(bytes.length) && k < toI32(text.length); k++) {
193
+ if (toI32(bytes[k]) !== toI32(text.charCodeAt(k))) {
194
+ return false
195
+ }
196
+ }
197
+ return true
198
+ }
199
+
200
+ /** The octets of `text`, one a character: what a field name or value is sent as. */
201
+ export const httpFieldBytes = (text: string): u8[] => {
202
+ const out: u8[] = []
203
+ const n: i32 = toI32(text.length)
204
+ for (let k: i32 = 0; k < n; k++) {
205
+ out.push(toU8(text.charCodeAt(k)))
206
+ }
207
+ return out
208
+ }
209
+
210
+ /**
211
+ * Whether a field named `name` with `value` is connection-specific, which
212
+ * HTTP/2 and HTTP/3 refuse in a message (RFC 9113 §8.2.2, RFC 9114 §4.2):
213
+ * the fields RFC 9110 §7.6.1 names, `keep-alive`, `proxy-connection`,
214
+ * `transfer-encoding` and `upgrade`, and `te` with any value but
215
+ * `trailers`.
216
+ */
217
+ export const httpFieldConnectionSpecific = (name: u8[], value: u8[]): boolean => {
218
+ if (httpFieldIs(name, "te")) {
219
+ return !httpFieldIs(value, "trailers")
220
+ }
221
+ return (
222
+ httpFieldIs(name, "connection") ||
223
+ httpFieldIs(name, "proxy-connection") ||
224
+ httpFieldIs(name, "keep-alive") ||
225
+ httpFieldIs(name, "transfer-encoding") ||
226
+ httpFieldIs(name, "upgrade")
227
+ )
228
+ }
229
+
230
+ /**
231
+ * Whether a field named `name` carries a credential an encoder should never
232
+ * put in a compression table, here or at any intermediary: `authorization`,
233
+ * `proxy-authorization`, `cookie` and `set-cookie` (RFC 7541 §7.1.3, RFC 9204
234
+ * §7.1.3). A table entry can be probed by a peer that controls part of what
235
+ * is compressed beside it, so a secret sent indexed can leak through the
236
+ * compressed length (CRIME's shape).
237
+ */
238
+ export const httpFieldSensitive = (name: u8[]): boolean =>
239
+ httpFieldIs(name, "authorization") ||
240
+ httpFieldIs(name, "proxy-authorization") ||
241
+ httpFieldIs(name, "cookie") ||
242
+ httpFieldIs(name, "set-cookie")
243
+
244
+ /** An ASCII letter in lowercase, any other octet as it is. */
245
+ const httpFieldsLower = (c: i32): i32 => (c >= 65 && c <= 90 ? c + 32 : c)
246
+
247
+ /**
248
+ * Whether `bytes` and `other` hold the same octets, ASCII letters compared
249
+ * without case, as a host name is (RFC 3986 §3.2.2).
250
+ */
251
+ const httpFieldsSameHost = (bytes: u8[], other: u8[]): boolean => {
252
+ const n: i32 = toI32(bytes.length)
253
+ if (n !== toI32(other.length)) {
254
+ return false
255
+ }
256
+ for (let k: i32 = 0; k < n && k < toI32(bytes.length) && k < toI32(other.length); k++) {
257
+ if (httpFieldsLower(toI32(bytes[k])) !== httpFieldsLower(toI32(other[k]))) {
258
+ return false
259
+ }
260
+ }
261
+ return true
262
+ }
263
+
264
+ /** The pseudo-header bit `name` is, 0 for a regular field, -1 for a name that starts with a colon and is none of them. */
265
+ const httpFieldsPseudoOf = (name: u8[]): i32 => {
266
+ if (toI32(name.length) === 0 || toI32(name[0]) !== 58) {
267
+ return 0
268
+ }
269
+ if (httpFieldIs(name, ":method")) {
270
+ return HTTP_FIELDS_METHOD
271
+ }
272
+ if (httpFieldIs(name, ":scheme")) {
273
+ return HTTP_FIELDS_SCHEME
274
+ }
275
+ if (httpFieldIs(name, ":authority")) {
276
+ return HTTP_FIELDS_AUTHORITY
277
+ }
278
+ if (httpFieldIs(name, ":path")) {
279
+ return HTTP_FIELDS_PATH
280
+ }
281
+ if (httpFieldIs(name, ":protocol")) {
282
+ return HTTP_FIELDS_PROTOCOL
283
+ }
284
+ if (httpFieldIs(name, ":status")) {
285
+ return HTTP_FIELDS_STATUS
286
+ }
287
+ return -1
288
+ }
289
+
290
+ /** Whether `scheme` has RFC 3986 §3.1's syntax: a letter, then letters, digits, `+`, `-` and `.`. */
291
+ const httpFieldsSchemeValid = (scheme: u8[]): boolean => {
292
+ const n: i32 = toI32(scheme.length)
293
+ for (let k: i32 = 0; k < n && k < toI32(scheme.length); k++) {
294
+ const c: i32 = toI32(scheme[k])
295
+ const letter: boolean = (c >= 97 && c <= 122) || (c >= 65 && c <= 90)
296
+ const rest: boolean = (c >= 48 && c <= 57) || c === 43 || c === 45 || c === 46
297
+ if (!letter && (k === 0 || !rest)) {
298
+ return false
299
+ }
300
+ }
301
+ return n > 0
302
+ }
303
+
304
+ /** The status code three ASCII digits from 100 to 599 spell, or -1. */
305
+ const httpFieldsStatusOf = (value: u8[]): i32 => {
306
+ if (toI32(value.length) !== 3) {
307
+ return -1
308
+ }
309
+ let code: i32 = 0
310
+ for (const b of value) {
311
+ const digit: i32 = toI32(b) - 48
312
+ if (digit < 0 || digit > 9) {
313
+ return -1
314
+ }
315
+ code = code * 10 + digit
316
+ }
317
+ return code >= 100 && code <= 599 ? code : -1
318
+ }
319
+
320
+ /** The length one run of at most eighteen ASCII digits spells, or -1. */
321
+ const httpFieldsLengthOf = (value: u8[]): i64 => {
322
+ const n: i32 = toI32(value.length)
323
+ if (n === 0 || n > HTTP_FIELDS_MAX_LENGTH_DIGITS) {
324
+ return toI64(-1)
325
+ }
326
+ let length: i64 = 0
327
+ for (const b of value) {
328
+ const digit: i32 = toI32(b) - 48
329
+ if (digit < 0 || digit > 9) {
330
+ return toI64(-1)
331
+ }
332
+ length = length * toI64(10) + toI64(digit)
333
+ }
334
+ return length
335
+ }
336
+
337
+ /** Whether `authority` holds an `@`, which only userinfo would put there (RFC 9113 §8.3.1). */
338
+ const httpFieldsHasUserinfo = (authority: u8[]): boolean => {
339
+ for (const b of authority) {
340
+ if (toI32(b) === 64) {
341
+ return true
342
+ }
343
+ }
344
+ return false
345
+ }
346
+
347
+ /**
348
+ * Checks a field section a program is about to send, pseudo-headers left
349
+ * out: every name valid and in lowercase, every value valid, nothing
350
+ * connection-specific, and as many values as names. Answers `HTTP_FIELDS_OK`
351
+ * or the first refusal. A version's writer refuses what this refuses, so
352
+ * that a program's string can never reach the wire as a field it was not.
353
+ */
354
+ export const httpFieldsCheckOutgoing = (names: u8[][], values: u8[][]): i32 => {
355
+ const n: i32 = toI32(names.length)
356
+ if (n !== toI32(values.length)) {
357
+ return HTTP_FIELDS_BAD_VALUE
358
+ }
359
+ for (let k: i32 = 0; k < n && k < toI32(names.length) && k < toI32(values.length); k++) {
360
+ const name: u8[] = names[k]
361
+ const value: u8[] = values[k]
362
+ if (!httpFieldNameValid(name)) {
363
+ return HTTP_FIELDS_BAD_NAME
364
+ }
365
+ if (!httpFieldValueValid(value)) {
366
+ return HTTP_FIELDS_BAD_VALUE
367
+ }
368
+ if (httpFieldConnectionSpecific(name, value)) {
369
+ return HTTP_FIELDS_CONNECTION_SPECIFIC
370
+ }
371
+ }
372
+ return HTTP_FIELDS_OK
373
+ }
374
+
375
+ /**
376
+ * One field section read into its shape: the pseudo-headers by name, and the
377
+ * regular fields in order. `readRequest`, `readResponse` and `readTrailers`
378
+ * each clear it and fill it from a decoded section, and answer
379
+ * `HTTP_FIELDS_OK` or the first refusal; after a refusal what it holds is
380
+ * partial and not to be used. Every read empties and refills the same two
381
+ * lists rather than making new ones, so a caller that keeps a section past
382
+ * the next read copies it.
383
+ */
384
+ export class HttpFields {
385
+ /** `:method`, empty in a response or a trailer section. */
386
+ method: u8[]
387
+ /** `:scheme`, empty when absent. */
388
+ scheme: u8[]
389
+ /** `:authority`, empty when absent. */
390
+ authority: u8[]
391
+ /** `:path`, empty when absent. */
392
+ path: u8[]
393
+ /** `:protocol` of an extended CONNECT (RFC 8441), empty when absent. */
394
+ protocol: u8[]
395
+ /** `:status` of a response, or 0. */
396
+ status: i32 = 0
397
+ /** The regular fields' names, in order. */
398
+ names: u8[][]
399
+ /** The regular fields' values, beside their names. */
400
+ values: u8[][]
401
+ /** The `content-length`, or `HTTP_FIELDS_NO_LENGTH`. */
402
+ contentLength: i64 = -1
403
+ /** The empty value an absent pseudo-header reads as, made once. */
404
+ none: u8[]
405
+
406
+ constructor() {
407
+ this.method = []
408
+ this.scheme = []
409
+ this.authority = []
410
+ this.path = []
411
+ this.protocol = []
412
+ this.none = []
413
+ this.names = []
414
+ this.values = []
415
+ }
416
+
417
+ /** Empties every field, so the next read starts from nothing. */
418
+ clear(): void {
419
+ this.method = this.none
420
+ this.scheme = this.none
421
+ this.authority = this.none
422
+ this.path = this.none
423
+ this.protocol = this.none
424
+ this.status = 0
425
+ while (toI32(this.names.length) > 0) {
426
+ this.names.pop()
427
+ }
428
+ while (toI32(this.values.length) > 0) {
429
+ this.values.pop()
430
+ }
431
+ this.contentLength = HTTP_FIELDS_NO_LENGTH
432
+ }
433
+
434
+ /** Whether the method is CONNECT, plain (RFC 9113 §8.5) or extended (RFC 8441). */
435
+ isConnect(): boolean {
436
+ return httpFieldIs(this.method, "CONNECT")
437
+ }
438
+
439
+ /** The value of the first regular field named `name` (lowercase), or `null`. */
440
+ get(name: string): u8[] | null {
441
+ const n: i32 = toI32(this.names.length)
442
+ for (let k: i32 = 0; k < n && k < toI32(this.names.length) && k < toI32(this.values.length); k++) {
443
+ if (httpFieldIs(this.names[k], name)) {
444
+ return this.values[k]
445
+ }
446
+ }
447
+ return null
448
+ }
449
+
450
+ /**
451
+ * Reads `names` and `values` as a section whose pseudo-headers are those
452
+ * `allowed` names, recording them and the regular fields. The shape's own
453
+ * rules — which pseudo-headers are required — are the caller's. Answers
454
+ * the mask of pseudo-headers seen, or a negative refusal.
455
+ */
456
+ readSection(names: u8[][], values: u8[][], allowed: i32): i32 {
457
+ this.clear()
458
+ const n: i32 = toI32(names.length)
459
+ if (n !== toI32(values.length)) {
460
+ return HTTP_FIELDS_BAD_VALUE
461
+ }
462
+ let seen: i32 = 0
463
+ let regular: boolean = false
464
+ for (let k: i32 = 0; k < n && k < toI32(names.length) && k < toI32(values.length); k++) {
465
+ const name: u8[] = names[k]
466
+ const value: u8[] = values[k]
467
+ if (!httpFieldValueValid(value)) {
468
+ return HTTP_FIELDS_BAD_VALUE
469
+ }
470
+ const pseudo: i32 = httpFieldsPseudoOf(name)
471
+ if (pseudo < 0) {
472
+ return HTTP_FIELDS_PSEUDO_UNKNOWN
473
+ }
474
+ if (pseudo > 0) {
475
+ if (regular) {
476
+ return HTTP_FIELDS_PSEUDO_AFTER_FIELD
477
+ }
478
+ if ((seen & pseudo) !== 0) {
479
+ return HTTP_FIELDS_PSEUDO_REPEATED
480
+ }
481
+ if ((allowed & pseudo) === 0) {
482
+ return HTTP_FIELDS_PSEUDO_FORBIDDEN
483
+ }
484
+ seen = seen | pseudo
485
+ const result: i32 = this.takePseudo(pseudo, value)
486
+ if (result !== HTTP_FIELDS_OK) {
487
+ return result
488
+ }
489
+ continue
490
+ }
491
+ regular = true
492
+ if (!httpFieldNameValid(name)) {
493
+ return HTTP_FIELDS_BAD_NAME
494
+ }
495
+ if (httpFieldConnectionSpecific(name, value)) {
496
+ return HTTP_FIELDS_CONNECTION_SPECIFIC
497
+ }
498
+ if (httpFieldIs(name, "content-length")) {
499
+ const length: i64 = httpFieldsLengthOf(value)
500
+ if (length < toI64(0) || (this.contentLength >= toI64(0) && this.contentLength !== length)) {
501
+ return HTTP_FIELDS_BAD_CONTENT_LENGTH
502
+ }
503
+ this.contentLength = length
504
+ }
505
+ this.names.push(name)
506
+ this.values.push(value)
507
+ }
508
+ return seen
509
+ }
510
+
511
+ /** Records the pseudo-header `pseudo` as `value`, checking its syntax. */
512
+ takePseudo(pseudo: i32, value: u8[]): i32 {
513
+ switch (pseudo) {
514
+ case HTTP_FIELDS_METHOD:
515
+ this.method = value
516
+ return httpFieldsAllTchar(value, true) ? HTTP_FIELDS_OK : HTTP_FIELDS_BAD_PSEUDO_VALUE
517
+ case HTTP_FIELDS_SCHEME:
518
+ this.scheme = value
519
+ return httpFieldsSchemeValid(value) ? HTTP_FIELDS_OK : HTTP_FIELDS_BAD_PSEUDO_VALUE
520
+ case HTTP_FIELDS_AUTHORITY:
521
+ this.authority = value
522
+ return httpFieldsHasUserinfo(value) ? HTTP_FIELDS_BAD_PSEUDO_VALUE : HTTP_FIELDS_OK
523
+ case HTTP_FIELDS_PATH:
524
+ this.path = value
525
+ return toI32(value.length) > 0 ? HTTP_FIELDS_OK : HTTP_FIELDS_BAD_PSEUDO_VALUE
526
+ case HTTP_FIELDS_PROTOCOL:
527
+ this.protocol = value
528
+ return httpFieldsAllTchar(value, true) ? HTTP_FIELDS_OK : HTTP_FIELDS_BAD_PSEUDO_VALUE
529
+ default:
530
+ this.status = httpFieldsStatusOf(value)
531
+ return this.status > 0 ? HTTP_FIELDS_OK : HTTP_FIELDS_BAD_PSEUDO_VALUE
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Reads a request's field section (RFC 9113 §8.3.1, RFC 9114 §4.3.1).
537
+ * Every request has `:method`. A CONNECT without `:protocol` has
538
+ * `:authority` and neither `:scheme` nor `:path` (RFC 9113 §8.5). An
539
+ * extended CONNECT — `:protocol` present, which only `extendedConnect`
540
+ * allows, the version having sent SETTINGS_ENABLE_CONNECT_PROTOCOL — has
541
+ * all four of the others (RFC 8441 §4). Any other request has `:scheme`
542
+ * and `:path`. A `:path` that does not start with `/` is OPTIONS's `*` or
543
+ * refused, and a `host` field must name `:authority` when both are there.
544
+ */
545
+ readRequest(names: u8[][], values: u8[][], extendedConnect: boolean): i32 {
546
+ const all: i32 =
547
+ HTTP_FIELDS_METHOD |
548
+ HTTP_FIELDS_SCHEME |
549
+ HTTP_FIELDS_AUTHORITY |
550
+ HTTP_FIELDS_PATH |
551
+ HTTP_FIELDS_PROTOCOL
552
+ const seen: i32 = this.readSection(names, values, all)
553
+ if (seen < 0) {
554
+ return seen
555
+ }
556
+ if ((seen & HTTP_FIELDS_METHOD) === 0) {
557
+ return HTTP_FIELDS_PSEUDO_MISSING
558
+ }
559
+ const connect: boolean = this.isConnect()
560
+ if ((seen & HTTP_FIELDS_PROTOCOL) !== 0) {
561
+ if (!extendedConnect || !connect) {
562
+ return HTTP_FIELDS_PSEUDO_FORBIDDEN
563
+ }
564
+ const tunnel: i32 = HTTP_FIELDS_SCHEME | HTTP_FIELDS_AUTHORITY | HTTP_FIELDS_PATH
565
+ if ((seen & tunnel) !== tunnel) {
566
+ return HTTP_FIELDS_PSEUDO_MISSING
567
+ }
568
+ } else if (connect) {
569
+ if ((seen & (HTTP_FIELDS_SCHEME | HTTP_FIELDS_PATH)) !== 0) {
570
+ return HTTP_FIELDS_PSEUDO_FORBIDDEN
571
+ }
572
+ if ((seen & HTTP_FIELDS_AUTHORITY) === 0) {
573
+ return HTTP_FIELDS_PSEUDO_MISSING
574
+ }
575
+ } else if ((seen & (HTTP_FIELDS_SCHEME | HTTP_FIELDS_PATH)) !== (HTTP_FIELDS_SCHEME | HTTP_FIELDS_PATH)) {
576
+ return HTTP_FIELDS_PSEUDO_MISSING
577
+ }
578
+ if (toI32(this.path.length) > 0 && toI32(this.path[0]) !== 47) {
579
+ if (!httpFieldIs(this.path, "*") || !httpFieldIs(this.method, "OPTIONS")) {
580
+ return HTTP_FIELDS_BAD_PSEUDO_VALUE
581
+ }
582
+ }
583
+ const host: u8[] | null = this.get("host")
584
+ if (host !== null && (seen & HTTP_FIELDS_AUTHORITY) !== 0 && !httpFieldsSameHost(host, this.authority)) {
585
+ return HTTP_FIELDS_AUTHORITY_MISMATCH
586
+ }
587
+ return HTTP_FIELDS_OK
588
+ }
589
+
590
+ /** Reads a response's field section: exactly one `:status`, and no other pseudo-header (RFC 9113 §8.3.2). */
591
+ readResponse(names: u8[][], values: u8[][]): i32 {
592
+ const seen: i32 = this.readSection(names, values, HTTP_FIELDS_STATUS)
593
+ if (seen < 0) {
594
+ return seen
595
+ }
596
+ return (seen & HTTP_FIELDS_STATUS) !== 0 ? HTTP_FIELDS_OK : HTTP_FIELDS_PSEUDO_MISSING
597
+ }
598
+
599
+ /** Reads a trailer section: regular fields only (RFC 9113 §8.1). */
600
+ readTrailers(names: u8[][], values: u8[][]): i32 {
601
+ const seen: i32 = this.readSection(names, values, HTTP_FIELDS_ZERO)
602
+ return seen < 0 ? seen : HTTP_FIELDS_OK
603
+ }
604
+ }