@amritk/nish-x86_64-linux 0.27.2 → 0.29.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 (54) hide show
  1. package/INSTALL.md +16 -8
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +118 -26
  4. package/package.json +1 -1
  5. package/runtime/core/host.d.ts +2 -1
  6. package/runtime/core/host.mjs +4 -0
  7. package/runtime/core.d.ts +47 -0
  8. package/runtime/core.mjs +67 -0
  9. package/runtime/nish.d.ts +176 -0
  10. package/runtime/nish.h +120 -6
  11. package/runtime/nish.mjs +24 -4
  12. package/runtime/runtime-checksum.c +270 -0
  13. package/runtime/runtime-net.c +386 -5
  14. package/runtime/runtime-os.c +59 -8
  15. package/runtime/runtime-parallel.c +81 -5
  16. package/runtime/runtime-sandbox.c +479 -0
  17. package/runtime/runtime-sha256.c +239 -0
  18. package/runtime/runtime-shm.c +541 -0
  19. package/runtime/runtime.c +827 -791
  20. package/scripts/build.sh +79 -28
  21. package/std/README.md +25 -13
  22. package/std/collections.ts +5 -5
  23. package/std/compress/checksum.ts +88 -75
  24. package/std/compress/deflate.ts +224 -0
  25. package/std/compress/gzip.ts +52 -0
  26. package/std/crypto/bcrypt.ts +816 -280
  27. package/std/crypto/hmac.ts +305 -2
  28. package/std/crypto/jwt.ts +1661 -0
  29. package/std/json-equal.ts +335 -0
  30. package/std/json-format.ts +1070 -0
  31. package/std/json-number.ts +428 -0
  32. package/std/json-pointer.ts +245 -0
  33. package/std/json-typed.ts +34 -0
  34. package/std/json-value.ts +510 -17
  35. package/std/json.ts +35 -6
  36. package/std/net/handler.ts +1348 -0
  37. package/std/net/http-client.ts +97 -16
  38. package/std/net/http-compress.ts +301 -16
  39. package/std/net/http1-client.ts +104 -19
  40. package/std/net/http1-server.ts +127 -24
  41. package/std/net/http1.ts +24 -4
  42. package/std/net/outbound.ts +409 -0
  43. package/std/net/postgres.ts +405 -0
  44. package/std/net/prefork.ts +344 -20
  45. package/std/net/websocket.ts +295 -14
  46. package/std/regex.ts +613 -52
  47. package/std/secret.ts +1 -1
  48. package/std/sync/ring.ts +587 -0
  49. package/std/sync/shm.ts +284 -0
  50. package/std/term-io.ts +35 -8
  51. package/std/term-rate.ts +13 -1
  52. package/std/threads.ts +35 -3
  53. package/std/yaml-json.ts +841 -0
  54. /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
@@ -0,0 +1,1348 @@
1
+ /**
2
+ * `nish/net/handler` — complete requests and their responses over the
3
+ * HTTP/1.1 carrier, so that a server answers requests instead of driving
4
+ * `Http1Server`'s events by hand.
5
+ *
6
+ * const HOME: i32 = 0 // module constants, in the order the routes are added
7
+ * const USER: i32 = 1
8
+ * const router = new Router(new RouterLimits())
9
+ * router.add("GET", "/") // answers HOME
10
+ * router.add("GET", "/users/:id") // answers USER
11
+ * const h = new HttpHandler(new HandlerLimits(), tcpListen("0.0.0.0", 8080, 1024), router)
12
+ * while (true) {
13
+ * h.wait(1000)
14
+ * while (h.ready()) {
15
+ * const req = h.take()
16
+ * switch (req.route) {
17
+ * case HOME: h.send(req, 200, TEXT, hello, 0, hello.length); break
18
+ * case USER: { const w = h.json(); ...; h.sendJson(req, 200); break }
19
+ * default: h.refuse(req) // 404, or 405 with its Allow
20
+ * }
21
+ * }
22
+ * }
23
+ *
24
+ * The handler owns the readiness loop, the carrier and everything between a
25
+ * request's first byte and its last response byte: it accepts, routes each
26
+ * request at its head, gathers its body up to the cap, and hands the program
27
+ * the request once it is whole. Keep-alive and pipelining are the carrier's:
28
+ * a connection's next request is read only once the one before it is
29
+ * answered and handed over, so answers go out in request order.
30
+ *
31
+ * **There are no function values**, so the program pulls: `wait` runs one
32
+ * turn of the loop, `ready` and `take` give out the whole requests it made,
33
+ * and the program dispatches on `req.route` — the index `Router.add`
34
+ * answered, or a negative `ROUTER_*` code — with a `switch` on module
35
+ * constants, as `nish/net/router` describes.
36
+ *
37
+ * **A request may be answered later.** A request the program has taken stays
38
+ * its own until it answers it, in the same turn or in a later one (after a
39
+ * database answers, say). Every request taken is answered exactly once, by
40
+ * `send`, `sendFields`, `sendJson`, `problem`, `refuse` or `start` ... `end`,
41
+ * and once answered the request object is the handler's again: it goes back
42
+ * to a free list and serves a later request, so the program keeps no
43
+ * reference to it.
44
+ *
45
+ * **An answer that comes too late is dropped.** Each slot carries a
46
+ * generation, bumped whenever its connection closes, and a request remembers
47
+ * the generation it was read under. An answer for a request whose
48
+ * connection has closed in the meantime — the peer left, the idle timeout
49
+ * passed — answers `HANDLER_DROPPED` and sends nothing, even when a new
50
+ * connection has taken the slot since. The answer still hands the request
51
+ * back, so nothing leaks.
52
+ *
53
+ * **Nothing is allocated per request once warm.** A request's method, target
54
+ * and header section are copied out of the carrier's head into the request's
55
+ * own buffer at `H1_REQUEST` (the parser reuses its own for the next
56
+ * request), and everything the program reads is a span into that copy or
57
+ * into the body buffer. Those buffers, the free list and the queue grow to
58
+ * the largest request seen and are kept; JSON is written into one scratch
59
+ * buffer the handler owns. `tests/link/net_handler` pins `Arena.used()` flat
60
+ * over thousands of requests.
61
+ *
62
+ * **The body cap** is the carrier's `maxBody`, which `HandlerLimits` sets to
63
+ * one MiB: a declared length over it, or a chunked body that grows past it, is
64
+ * answered 413 by the carrier and the connection ends. A request's body
65
+ * buffer grows to the body it holds, so a handler holds at most its slots
66
+ * times the cap in bodies. `docs/security/handler.md` is the record.
67
+ *
68
+ * Misuse — a status outside 200 to 599, a reserved field (`Content-Length`,
69
+ * `Transfer-Encoding`, `Connection`, `Upgrade`, `Keep-Alive`), more body than
70
+ * the length said, a second `start` — panics: it is a bug in the program,
71
+ * never something a peer can cause. What a peer can cause answers a code the
72
+ * program may ignore, so no answer needs its result bound.
73
+ *
74
+ * Private helpers share the importing program's flat symbol namespace
75
+ * (`docs/wp26-stdlib.md` §3e), which is why each one carries the module's name.
76
+ *
77
+ * Written from RFC 9110, RFC 9112 and RFC 9457, not ported from another
78
+ * implementation.
79
+ */
80
+ import { copyBytes } from "nish/bytes"
81
+ import { JsonWriter } from "nish/json"
82
+ import { httpFieldBytes } from "nish/net/http-fields"
83
+ import { HTTP1_CHUNKED, HTTP1_NO_BODY, http1HexValue } from "nish/net/http1"
84
+ import {
85
+ H1_AGAIN,
86
+ H1_BODY,
87
+ H1_DONE,
88
+ H1_END,
89
+ H1_ERROR,
90
+ H1_NEED_MORE,
91
+ H1_POOL_FULL,
92
+ H1_REQUEST,
93
+ H1_WANT_READ,
94
+ H1_WANT_WRITE,
95
+ H1_WRITE,
96
+ Http1Config,
97
+ Http1Connection,
98
+ Http1Server,
99
+ } from "nish/net/http1-server"
100
+ import {
101
+ ROUTER_BAD_ESCAPE,
102
+ ROUTER_METHOD_NOT_ALLOWED,
103
+ ROUTER_NO_ROOM,
104
+ Router,
105
+ routerDecode,
106
+ } from "nish/net/router"
107
+ import { URL_DECODE_ALLOW_NUL, URL_DECODE_ERR_SPACE, URL_DECODE_FORM, percentDecode } from "nish/net/url"
108
+ import { pollAdd, pollCreate, pollModify, pollRemove, pollWait } from "nish:net"
109
+
110
+ /** An answer sent, or queued to be sent. */
111
+ export const HANDLER_SENT: i32 = 0
112
+ /** An answer for a request whose connection closed first: nothing was sent. */
113
+ export const HANDLER_DROPPED: i32 = -21
114
+ /** `HttpRequest.query` and `header`: no such name. */
115
+ export const HANDLER_ABSENT: i32 = -20
116
+
117
+ /** The most descriptors `watch` takes beside the handler's own. */
118
+ export const HANDLER_MAX_WATCHED: i32 = 16
119
+
120
+ // A request's state, from the free list to the free list.
121
+ /** On the free list. */
122
+ const HANDLER_FREE: i32 = 0
123
+ /** Its head is routed and its body is arriving. */
124
+ const HANDLER_READING: i32 = 1
125
+ /** Whole, but its connection still holds more than half an output of the response before it unsent. */
126
+ const HANDLER_PARKED: i32 = 2
127
+ /** Whole and queued for `take`. */
128
+ const HANDLER_QUEUED: i32 = 3
129
+ /** Taken: the program owes it an answer. */
130
+ const HANDLER_HELD: i32 = 4
131
+ /** Its response is started and the program is still writing the body. */
132
+ const HANDLER_STREAMING: i32 = 5
133
+ /** The program has ended the response; what the carrier had no room for is still being handed over. */
134
+ const HANDLER_FLUSHING: i32 = 6
135
+
136
+ /** The listener's poll token; a slot's is the slot, and watched descriptor `id`'s is `-2 - id`. */
137
+ const HANDLER_LISTENER: i32 = -1
138
+ /** Read, error and hang-up: the readiness bits that mean the socket has something for `readable`. */
139
+ const HANDLER_READ_BITS: i32 = 5
140
+ const HANDLER_ZERO: i32 = 0
141
+ const HANDLER_ONE_MIB: i32 = 1048576
142
+ /** `tcpAccept`: none is waiting (EAGAIN), or the one that was left before it was taken (ECONNABORTED). */
143
+ const HANDLER_ACCEPT_NONE: i32 = -11
144
+ const HANDLER_ACCEPT_ABORTED: i32 = -103
145
+ /** How long the listener rests after an accept fails for want of descriptors or memory: 100 ms. */
146
+ const HANDLER_ACCEPT_REST: i64 = 100000000
147
+ /** How often, at most, `wait` looks for a body past `bodyTimeout`: a second, in nanoseconds. */
148
+ const HANDLER_BODY_SWEEP: i64 = 1000000000
149
+ const HANDLER_NANOS_PER_MS: i64 = 1000000
150
+ /** `handlerFormByte`'s count of bytes taken, in its bits above the byte. */
151
+ const HANDLER_TOOK_ONE: i32 = 256
152
+ const HANDLER_TOOK_THREE: i32 = 768
153
+
154
+ /**
155
+ * What an `HttpHandler` holds and refuses past. `http` is the carrier's own
156
+ * configuration, made here with its body cap lowered to one MiB because the
157
+ * handler holds a body whole; raise `http.maxBody` for a server that takes
158
+ * larger bodies, knowing each slot may keep a buffer that large.
159
+ */
160
+ export class HandlerLimits {
161
+ /** The carrier's caps and timeouts; `http.maxBody` is the body cap, past which a request is answered 413. */
162
+ http: Http1Config
163
+ /** The most connections at once. */
164
+ slots: i32 = 256
165
+ /** The JSON scratch buffer's size: the largest body `json` and `problem` can write. */
166
+ jsonBytes: i32 = 65536
167
+ /** The most readiness events one `wait` takes. */
168
+ maxEvents: i32 = 256
169
+ /**
170
+ * Milliseconds from a request's head to the end of its body. A request
171
+ * still reading its body past it has its connection closed: the carrier's
172
+ * `idleTimeout` is refreshed by every byte, so a body trickled a byte at a
173
+ * time would otherwise hold its slot for good (HANDLER-5). It is checked
174
+ * each `wait`, at most once a second, so a connection may outlive it by up
175
+ * to a second, or until `wait` returns.
176
+ */
177
+ bodyTimeout: i32 = 30000
178
+
179
+ constructor() {
180
+ this.http = new Http1Config()
181
+ this.http.maxBody = HANDLER_ONE_MIB
182
+ }
183
+ }
184
+
185
+ /**
186
+ * One request, whole: its head copied out of the carrier and its body. Every
187
+ * position is an offset into `head` or `body`, which are the request's own
188
+ * and stay valid until it is answered.
189
+ */
190
+ export class HttpRequest {
191
+ /** The method, the target and the header section, as the parser kept them (field names lowercased). */
192
+ head: u8[]
193
+ /** Four per field, into `head`: name start and end, value start and end. */
194
+ spans: i32[]
195
+ /** The route parameters as `[start, end)` pairs into `head`, in the pattern's order. */
196
+ params: i32[]
197
+ /** The body, `body[0 .. bodyLength)`. */
198
+ body: u8[]
199
+ /** What the carrier had no room for yet: `pending[pendingStart .. pendingEnd)`. */
200
+ pending: u8[]
201
+ /** When the head was read, as the handler's turn saw it (`monotonicNanos`); `bodyTimeout` runs from here. */
202
+ since: i64 = 0
203
+ /** The index `Router.add` answered for the route, or a negative `ROUTER_*` code. */
204
+ route: i32 = 0
205
+ /** How many parameter pairs `params` holds. */
206
+ paramCount: i32 = 0
207
+ methodStart: i32 = 0
208
+ methodEnd: i32 = 0
209
+ targetStart: i32 = 0
210
+ targetEnd: i32 = 0
211
+ /** Where the path ends: the target's first `?`, or its end. */
212
+ pathEnd: i32 = 0
213
+ headerCount: i32 = 0
214
+ bodyLength: i32 = 0
215
+ /** The connection's slot, and its generation when this request was read. */
216
+ slot: i32 = 0
217
+ gen: i32 = 0
218
+ /** Its index in the handler's pool. */
219
+ index: i32 = 0
220
+ state: i32 = 0
221
+ pendingStart: i32 = 0
222
+ pendingEnd: i32 = 0
223
+
224
+ constructor(index: i32) {
225
+ this.index = index
226
+ this.head = []
227
+ this.spans = []
228
+ this.params = []
229
+ this.body = []
230
+ this.pending = []
231
+ }
232
+
233
+ /** Whether the method is exactly `method` (methods are case-sensitive, RFC 9110 §9.1). */
234
+ methodIs(method: string): boolean {
235
+ return handlerSpanIs(this.head, this.methodStart, this.methodEnd, method)
236
+ }
237
+
238
+ /** Whether the path, the target up to its `?`, is exactly `path`. */
239
+ pathIs(path: string): boolean {
240
+ return handlerSpanIs(this.head, this.targetStart, this.pathEnd, path)
241
+ }
242
+
243
+ /** Where parameter `k` starts in `head`. Panics on a parameter the route does not have. */
244
+ paramStart(k: i32): i32 {
245
+ this.checkParam(k)
246
+ return this.params[2 * k]
247
+ }
248
+
249
+ /** Where parameter `k` ends in `head`. */
250
+ paramEnd(k: i32): i32 {
251
+ this.checkParam(k)
252
+ return this.params[2 * k + 1]
253
+ }
254
+
255
+ /** Whether parameter `k`, raw, is exactly `text`. */
256
+ paramIs(k: i32, text: string): boolean {
257
+ this.checkParam(k)
258
+ return handlerSpanIs(this.head, this.params[2 * k], this.params[2 * k + 1], text)
259
+ }
260
+
261
+ /**
262
+ * Parameter `k` percent-decoded into `out` from 0 (`routerDecode`): its
263
+ * length, `ROUTER_BAD_ESCAPE` for a malformed escape, or `ROUTER_NO_ROOM`
264
+ * when `out` is too short. Nothing is written on a code.
265
+ */
266
+ param(k: i32, out: u8[]): i32 {
267
+ this.checkParam(k)
268
+ return routerDecode(this.head, this.params[2 * k], this.params[2 * k + 1], out)
269
+ }
270
+
271
+ /** Parameter `k` read as a decimal number that is not negative and fits an `i32`, or -1. */
272
+ paramDecimal(k: i32): i32 {
273
+ this.checkParam(k)
274
+ return handlerDecimal(this.head, this.params[2 * k], this.params[2 * k + 1])
275
+ }
276
+
277
+ checkParam(k: i32): void {
278
+ if (k < 0 || k >= this.paramCount) {
279
+ panic(`HttpRequest.param: the route has ${this.paramCount} parameter(s), not one at ${k}`)
280
+ }
281
+ }
282
+
283
+ /**
284
+ * The first query parameter named `name`, its value decoded into `out` from
285
+ * 0 — `+` as a space and `%XX` as its byte, the form encoding
286
+ * (application/x-www-form-urlencoded) — and its length; a name is compared
287
+ * decoded too. `HANDLER_ABSENT` when no parameter has the name,
288
+ * `ROUTER_BAD_ESCAPE` for a malformed escape in its value, `ROUTER_NO_ROOM`
289
+ * when `out` is too short. A name with no `=` has an empty value.
290
+ */
291
+ query(name: string, out: u8[]): i32 {
292
+ const at: i32 = this.queryIndex(name)
293
+ if (at < 0) {
294
+ return HANDLER_ABSENT
295
+ }
296
+ return handlerFormDecode(this.head, this.queryValueStart, this.queryValueEnd, out)
297
+ }
298
+
299
+ /** Whether the query holds a parameter named `name`. */
300
+ hasQuery(name: string): boolean {
301
+ return this.queryIndex(name) >= 0
302
+ }
303
+
304
+ /** The span `queryIndex` found, raw. */
305
+ queryValueStart: i32 = 0
306
+ queryValueEnd: i32 = 0
307
+
308
+ /** Finds the first parameter named `name`: where its pair starts, or -1; its raw value goes to `queryValue...`. */
309
+ queryIndex(name: string): i32 {
310
+ let at: i32 = this.pathEnd + 1
311
+ const end: i32 = this.targetEnd
312
+ while (at < end) {
313
+ let pairEnd: i32 = at
314
+ while (pairEnd < end && toI32(this.head[pairEnd]) !== 38) {
315
+ pairEnd += 1
316
+ }
317
+ let keyEnd: i32 = at
318
+ while (keyEnd < pairEnd && toI32(this.head[keyEnd]) !== 61) {
319
+ keyEnd += 1
320
+ }
321
+ if (handlerFormIs(this.head, at, keyEnd, name)) {
322
+ this.queryValueStart = keyEnd < pairEnd ? keyEnd + 1 : pairEnd
323
+ this.queryValueEnd = pairEnd
324
+ return at
325
+ }
326
+ at = pairEnd + 1
327
+ }
328
+ return -1
329
+ }
330
+
331
+ /** The index of the first field named `name`, which is lowercase, or `HANDLER_ABSENT`. */
332
+ header(name: string): i32 {
333
+ for (let i: i32 = 0; i < this.headerCount; i++) {
334
+ if (handlerSpanIs(this.head, this.spans[4 * i], this.spans[4 * i + 1], name)) {
335
+ return i
336
+ }
337
+ }
338
+ return HANDLER_ABSENT
339
+ }
340
+
341
+ /** Where field `i`'s value starts in `head`. */
342
+ valueStart(i: i32): i32 {
343
+ return this.spans[4 * i + 2]
344
+ }
345
+
346
+ /** Where field `i`'s value ends in `head`. */
347
+ valueEnd(i: i32): i32 {
348
+ return this.spans[4 * i + 3]
349
+ }
350
+
351
+ /** Whether the first field named `name` (lowercase) has exactly the value `value`. */
352
+ headerIs(name: string, value: string): boolean {
353
+ const i: i32 = this.header(name)
354
+ return i >= 0 && handlerSpanIs(this.head, this.spans[4 * i + 2], this.spans[4 * i + 3], value)
355
+ }
356
+ }
357
+
358
+ /**
359
+ * The HTTP/1.1 server: a carrier, its readiness loop, a router and a pool of
360
+ * requests. One per thread; nothing in it is shared.
361
+ */
362
+ export class HttpHandler {
363
+ server: Http1Server
364
+ router: Router
365
+ /** Every request the pool has made, and the indices of the free ones. */
366
+ requests: HttpRequest[]
367
+ free: i32[]
368
+ /** Whole requests waiting for `take`, a ring of `queue.length` with `queueCount` from `queueStart`. */
369
+ queue: i32[]
370
+ /** Per slot: its generation, its request (-1 for none), and whether it holds a connection. */
371
+ slotGen: i32[]
372
+ current: i32[]
373
+ open: boolean[]
374
+ /** `pollWait`'s output. */
375
+ readyFds: i32[]
376
+ /** Per watched id: whether its descriptor was ready in the last `wait`. */
377
+ watchedFired: boolean[]
378
+ /** `json` and `problem` write here. */
379
+ scratch: u8[]
380
+ writer: JsonWriter
381
+ /** `refuse`'s scratch for `Router.match`. */
382
+ probe: i32[]
383
+ /** One field for `send`; two for a 405. */
384
+ typeName: u8[][]
385
+ typeValue: u8[][]
386
+ allowNames: u8[][]
387
+ allowValues: u8[][]
388
+ /** Each `Allow` value made, by the bit mask of the methods it lists: made once a mask and kept. */
389
+ allowMasks: i64[]
390
+ allowBytes: u8[][]
391
+ jsonType: u8[]
392
+ problemType: u8[]
393
+ noFields: u8[][]
394
+ /**
395
+ * When an accept failed for want of a resource (EMFILE, ENFILE, ENOMEM):
396
+ * the listener is out of the loop until then, which it would otherwise wake
397
+ * every turn while its connection waits. 0 while it is in the loop.
398
+ */
399
+ acceptRestUntil: i64 = 0
400
+ /** `bodyTimeout` in nanoseconds. */
401
+ bodyTimeout: i64 = 0
402
+ /** When the current turn's sockets were ready (`monotonicNanos`): a head read in it is stamped with this. */
403
+ turnAt: i64 = 0
404
+ /** When `wait` next looks for a body past `bodyTimeout`. */
405
+ bodyCheckAt: i64 = 0
406
+ listener: i32 = 0
407
+ loop: i32 = 0
408
+ queueStart: i32 = 0
409
+ queueCount: i32 = 0
410
+ outputSize: i32 = 0
411
+ /** Set by `drain`: accept nothing more and close each connection once it is idle. */
412
+ draining: boolean = false
413
+ /** Whether `json` gave out the writer for a value `sendJson` has not yet sent. */
414
+ jsonBegun: boolean = false
415
+
416
+ /**
417
+ * A handler over `listener`, a listening socket the program made
418
+ * (`tcpListen`, or `tcpListenFlags` with SO_REUSEPORT for a worker of
419
+ * `nish/net/prefork`), routing through `router`, whose routes are added
420
+ * before the first `wait`.
421
+ */
422
+ constructor(limits: HandlerLimits, listener: i32, router: Router) {
423
+ if (limits.slots < 1 || limits.jsonBytes < 256 || limits.maxEvents < 1 || limits.bodyTimeout < 1) {
424
+ panic(
425
+ `HttpHandler: slots ${limits.slots}, jsonBytes ${limits.jsonBytes}, maxEvents ${limits.maxEvents} and bodyTimeout ${limits.bodyTimeout} need at least 1, 256, 1 and 1`
426
+ )
427
+ }
428
+ this.bodyTimeout = toI64(limits.bodyTimeout) * HANDLER_NANOS_PER_MS
429
+ this.server = new Http1Server(limits.http, listener, limits.slots)
430
+ this.router = router
431
+ this.listener = listener
432
+ this.outputSize = limits.http.outputSize
433
+ this.requests = []
434
+ this.free = []
435
+ this.queue = new Array<i32>(16)
436
+ this.slotGen = new Array<i32>(limits.slots)
437
+ this.current = new Array<i32>(limits.slots)
438
+ this.open = new Array<boolean>(limits.slots)
439
+ for (let s: i32 = 0; s < limits.slots; s++) {
440
+ this.current[s] = -1
441
+ }
442
+ this.readyFds = new Array<i32>(2 * limits.maxEvents)
443
+ this.watchedFired = new Array<boolean>(HANDLER_MAX_WATCHED)
444
+ this.scratch = new Array<u8>(limits.jsonBytes)
445
+ this.writer = new JsonWriter(this.scratch, HANDLER_ZERO)
446
+ this.probe = new Array<i32>(2 * router.maxParams + 2)
447
+ this.jsonType = httpFieldBytes("application/json")
448
+ this.problemType = httpFieldBytes("application/problem+json")
449
+ const contentType: u8[] = httpFieldBytes("content-type")
450
+ this.typeName = [contentType]
451
+ this.typeValue = [this.jsonType]
452
+ this.allowNames = [contentType, httpFieldBytes("allow")]
453
+ // The `Allow` value, `allowValues[1]`, is set for each 405.
454
+ this.allowValues = [this.problemType, this.problemType]
455
+ this.allowMasks = []
456
+ this.allowBytes = []
457
+ this.noFields = []
458
+ this.loop = pollCreate()
459
+ if (this.loop < 0) {
460
+ panic(`HttpHandler: pollCreate failed: ${this.loop}`)
461
+ }
462
+ pollAdd(this.loop, listener, H1_WANT_READ, HANDLER_LISTENER)
463
+ }
464
+
465
+ // ---- The loop -------------------------------------------------------------------
466
+
467
+ /**
468
+ * Watches `fd` for reading beside the handler's sockets, as `id` (0 to
469
+ * `HANDLER_MAX_WATCHED - 1`): `fired(id)` says whether it was ready in the
470
+ * last `wait`. For a program's own descriptors — `signalFd()` for a
471
+ * graceful stop, a pipe from another thread.
472
+ */
473
+ watch(fd: i32, id: i32): void {
474
+ if (id < 0 || id >= HANDLER_MAX_WATCHED) {
475
+ panic(`HttpHandler.watch: id ${id} is outside 0 to ${HANDLER_MAX_WATCHED - 1}`)
476
+ }
477
+ pollAdd(this.loop, fd, H1_WANT_READ, -2 - id)
478
+ }
479
+
480
+ /** Whether watched descriptor `id` was ready in the last `wait`. */
481
+ fired(id: i32): boolean {
482
+ return id >= 0 && id < HANDLER_MAX_WATCHED && this.watchedFired[id]
483
+ }
484
+
485
+ /**
486
+ * One turn of the loop: waits up to `timeoutMs` (-1 for no limit) for a
487
+ * socket to be ready, accepts, reads and writes what is, closes the
488
+ * connections past their timeouts, and answers how many whole requests
489
+ * are waiting for `take`.
490
+ */
491
+ wait(timeoutMs: i32): i32 {
492
+ for (let id: i32 = 0; id < HANDLER_MAX_WATCHED; id++) {
493
+ this.watchedFired[id] = false
494
+ }
495
+ if (this.acceptRestUntil > 0 && monotonicNanos() >= this.acceptRestUntil) {
496
+ this.acceptRestUntil = 0
497
+ if (!this.draining) {
498
+ pollAdd(this.loop, this.listener, H1_WANT_READ, HANDLER_LISTENER)
499
+ }
500
+ }
501
+ let timeout: i32 = timeoutMs
502
+ if (this.acceptRestUntil > 0 && !this.draining) {
503
+ // Wake for the listener's return, whatever the program asked.
504
+ const rest: i32 = toI32((this.acceptRestUntil - monotonicNanos()) / toI64(1000000)) + 1
505
+ if (timeout < 0 || rest < timeout) {
506
+ timeout = rest
507
+ }
508
+ }
509
+ const n: i32 = pollWait(this.loop, this.readyFds, timeout)
510
+ const now: i64 = monotonicNanos()
511
+ this.turnAt = now
512
+ for (let k: i32 = 0; k < n; k++) {
513
+ const token: i32 = this.readyFds[2 * k]
514
+ if (token === HANDLER_LISTENER) {
515
+ this.acceptAll()
516
+ } else if (token <= -2) {
517
+ const id: i32 = -2 - token
518
+ if (id < HANDLER_MAX_WATCHED) {
519
+ this.watchedFired[id] = true
520
+ }
521
+ } else if (token >= 0 && this.server.holds(token)) {
522
+ if ((this.readyFds[2 * k + 1] & HANDLER_READ_BITS) !== 0) {
523
+ this.server.readable(token)
524
+ } else {
525
+ this.server.writable(token)
526
+ }
527
+ this.drive(token)
528
+ }
529
+ }
530
+ if (this.server.expire(now) > 0) {
531
+ this.sweep()
532
+ }
533
+ if (now >= this.bodyCheckAt) {
534
+ this.bodyCheckAt = now + HANDLER_BODY_SWEEP
535
+ this.expireBodies(now)
536
+ }
537
+ return this.queueCount
538
+ }
539
+
540
+ /**
541
+ * Begins a graceful stop: the handler accepts no more connections, closes
542
+ * each idle one now, and closes each other one once the request on it is
543
+ * answered and sent, so `connections()` falls to 0 as the program answers
544
+ * what it holds. A request already read on a draining connection is still
545
+ * handed over and answered. The listener is the program's to close.
546
+ */
547
+ drain(): void {
548
+ if (this.draining) {
549
+ return
550
+ }
551
+ this.draining = true
552
+ if (this.acceptRestUntil === 0) {
553
+ pollRemove(this.loop, this.listener)
554
+ }
555
+ for (let s: i32 = 0; s < toI32(this.open.length) && s < toI32(this.current.length); s++) {
556
+ if (
557
+ this.open[s] &&
558
+ this.server.holds(s) &&
559
+ this.current[s] === -1 &&
560
+ !this.server.connection(s).wantsWrite()
561
+ ) {
562
+ this.closeSlot(s)
563
+ } else if (this.open[s] && this.server.holds(s)) {
564
+ // Its exchange is its last: the answer says `Connection: close` if
565
+ // it is not yet started, and no pipelined request is read behind it.
566
+ this.server.connection(s).closeAfter = true
567
+ }
568
+ }
569
+ }
570
+
571
+ /** Takes every waiting connection. */
572
+ acceptAll(): void {
573
+ if (this.draining) {
574
+ return
575
+ }
576
+ let slot: i32 = this.server.accept()
577
+ while (slot >= 0 || slot === H1_POOL_FULL || slot === HANDLER_ACCEPT_ABORTED) {
578
+ if (slot >= 0) {
579
+ this.open[slot] = true
580
+ this.current[slot] = -1
581
+ pollAdd(this.loop, this.server.fd(slot), H1_WANT_READ, slot)
582
+ }
583
+ slot = this.server.accept()
584
+ }
585
+ if (slot !== HANDLER_ACCEPT_NONE) {
586
+ // Out of descriptors or memory: the connection stays in the backlog
587
+ // and the listener stays readable, so rest it rather than spin.
588
+ pollRemove(this.loop, this.listener)
589
+ this.acceptRestUntil = monotonicNanos() + HANDLER_ACCEPT_REST
590
+ }
591
+ }
592
+
593
+ /**
594
+ * Takes `slot`'s events until it needs the socket again, then sends what it
595
+ * holds and re-arms it, or closes it once it is over.
596
+ */
597
+ drive(slot: i32): void {
598
+ const conn: Http1Connection = this.server.connection(slot)
599
+ let event: i32 = this.server.next(slot)
600
+ while (event !== H1_NEED_MORE && event !== H1_ERROR) {
601
+ if (event === H1_REQUEST) {
602
+ this.begin(slot, conn)
603
+ } else if (event === H1_BODY) {
604
+ this.gather(slot, conn)
605
+ } else if (event === H1_END) {
606
+ this.whole(slot, conn)
607
+ } else if (event === H1_WRITE) {
608
+ this.resume(slot, conn)
609
+ }
610
+ event = this.server.next(slot)
611
+ }
612
+ if (event === H1_ERROR) {
613
+ // The carrier has written its refusal and ends the connection; a
614
+ // request it was reading is nobody's.
615
+ this.releaseReading(slot)
616
+ }
617
+ const wants: i32 = this.server.flush(slot)
618
+ if ((wants & H1_DONE) !== 0) {
619
+ this.closeSlot(slot)
620
+ return
621
+ }
622
+ if (this.draining && this.current[slot] === -1 && !conn.wantsWrite()) {
623
+ this.closeSlot(slot)
624
+ return
625
+ }
626
+ if (this.server.rearm(slot, wants)) {
627
+ pollModify(this.loop, this.server.fd(slot), wants & (H1_WANT_READ | H1_WANT_WRITE), slot)
628
+ }
629
+ const at: i32 = this.current[slot]
630
+ if (at >= 0 && this.requests[at].state === HANDLER_PARKED && this.roomy(conn)) {
631
+ this.enqueue(this.requests[at])
632
+ }
633
+ }
634
+
635
+ /** Whether `conn`'s output holds at most half its size unsent, so a response head fits behind it. */
636
+ roomy(conn: Http1Connection): boolean {
637
+ return (conn.outputEnd - conn.outputStart) * 2 <= this.outputSize
638
+ }
639
+
640
+ /** A head: a request from the free list, the head copied into it, and its route. */
641
+ begin(slot: i32, conn: Http1Connection): void {
642
+ if (this.draining) {
643
+ // A head read during the drain is the connection's last.
644
+ conn.closeAfter = true
645
+ }
646
+ const r: HttpRequest = this.obtain()
647
+ const p = conn.parser
648
+ const length: i32 = p.headLength
649
+ if (toI32(r.head.length) < length) {
650
+ r.head = new Array<u8>(handlerGrown(toI32(r.head.length), length))
651
+ }
652
+ copyBytes(r.head, HANDLER_ZERO, p.head, HANDLER_ZERO, length)
653
+ const spanCount: i32 = 4 * p.headerCount
654
+ if (toI32(r.spans.length) < spanCount) {
655
+ r.spans = new Array<i32>(handlerGrown(toI32(r.spans.length), spanCount))
656
+ }
657
+ for (let k: i32 = 0; k < spanCount; k++) {
658
+ r.spans[k] = p.spans[k]
659
+ }
660
+ r.headerCount = p.headerCount
661
+ r.methodStart = p.methodStart
662
+ r.methodEnd = p.methodEnd
663
+ r.targetStart = p.targetStart
664
+ r.targetEnd = p.targetEnd
665
+ let pathEnd: i32 = r.targetStart
666
+ while (pathEnd < r.targetEnd && toI32(r.head[pathEnd]) !== 63) {
667
+ pathEnd += 1
668
+ }
669
+ r.pathEnd = pathEnd
670
+ r.bodyLength = 0
671
+ r.slot = slot
672
+ r.gen = this.slotGen[slot]
673
+ r.since = this.turnAt
674
+ r.state = HANDLER_READING
675
+ r.pendingStart = 0
676
+ r.pendingEnd = 0
677
+ this.route(r)
678
+ this.current[slot] = r.index
679
+ }
680
+
681
+ /**
682
+ * Routes `r` through the router. A HEAD that only a GET route matches takes
683
+ * that route, since a GET resource answers HEAD (RFC 9110 §9.3.2) and the
684
+ * carrier sends no body for it.
685
+ */
686
+ route(r: HttpRequest): void {
687
+ const params: i32[] = r.params
688
+ let route: i32 = this.router.match(
689
+ r.head,
690
+ r.methodStart,
691
+ r.methodEnd,
692
+ r.head,
693
+ r.targetStart,
694
+ r.targetEnd,
695
+ params
696
+ )
697
+ if (route === ROUTER_METHOD_NOT_ALLOWED && r.methodIs("HEAD")) {
698
+ const get: i32 = this.router.methodIndex("GET")
699
+ if (get >= 0) {
700
+ const viaGet: i32 = this.router.match(
701
+ this.router.methodBytes,
702
+ this.router.methodStart[get],
703
+ this.router.methodEnd[get],
704
+ r.head,
705
+ r.targetStart,
706
+ r.targetEnd,
707
+ params
708
+ )
709
+ if (viaGet >= 0) {
710
+ route = viaGet
711
+ }
712
+ }
713
+ }
714
+ r.route = route
715
+ r.paramCount = route >= 0 ? this.router.paramCount(route) : 0
716
+ }
717
+
718
+ /** Body bytes: appended to the request's body, which the carrier's cap bounds. */
719
+ gather(slot: i32, conn: Http1Connection): void {
720
+ const at: i32 = this.current[slot]
721
+ if (at < 0) {
722
+ return
723
+ }
724
+ const r: HttpRequest = this.requests[at]
725
+ const n: i32 = conn.dataLength
726
+ const need: i32 = r.bodyLength + n
727
+ if (toI32(r.body.length) < need) {
728
+ const grown: u8[] = new Array<u8>(handlerGrown(toI32(r.body.length), need))
729
+ copyBytes(grown, HANDLER_ZERO, r.body, HANDLER_ZERO, r.bodyLength)
730
+ r.body = grown
731
+ }
732
+ copyBytes(r.body, r.bodyLength, conn.data, conn.dataStart, n)
733
+ r.bodyLength = need
734
+ }
735
+
736
+ /** The request is whole: queued for `take`, or parked while the response before it is still mostly unsent. */
737
+ whole(slot: i32, conn: Http1Connection): void {
738
+ const at: i32 = this.current[slot]
739
+ if (at < 0) {
740
+ return
741
+ }
742
+ const r: HttpRequest = this.requests[at]
743
+ if (this.roomy(conn)) {
744
+ this.enqueue(r)
745
+ } else {
746
+ r.state = HANDLER_PARKED
747
+ }
748
+ }
749
+
750
+ enqueue(r: HttpRequest): void {
751
+ r.state = HANDLER_QUEUED
752
+ const size: i32 = toI32(this.queue.length)
753
+ if (this.queueCount === size) {
754
+ const grown: i32[] = new Array<i32>(2 * size)
755
+ for (let k: i32 = 0; k < size && k < toI32(grown.length); k++) {
756
+ grown[k] = this.queue[(this.queueStart + k) % size]
757
+ }
758
+ this.queue = grown
759
+ this.queueStart = 0
760
+ }
761
+ this.queue[(this.queueStart + this.queueCount) % toI32(this.queue.length)] = r.index
762
+ this.queueCount += 1
763
+ }
764
+
765
+ /** `H1_WRITE`: the carrier has room again for the response under way. */
766
+ resume(slot: i32, conn: Http1Connection): void {
767
+ const at: i32 = this.current[slot]
768
+ if (at < 0) {
769
+ return
770
+ }
771
+ const r: HttpRequest = this.requests[at]
772
+ if (r.state !== HANDLER_STREAMING && r.state !== HANDLER_FLUSHING) {
773
+ return
774
+ }
775
+ this.pushPending(r, conn)
776
+ if (r.state === HANDLER_FLUSHING) {
777
+ this.finish(r, conn)
778
+ }
779
+ }
780
+
781
+ /** Hands the carrier as much of `r`'s pending bytes as it takes. */
782
+ pushPending(r: HttpRequest, conn: Http1Connection): void {
783
+ const left: i32 = r.pendingEnd - r.pendingStart
784
+ if (left === 0) {
785
+ return
786
+ }
787
+ const taken: i32 = conn.write(r.pending, r.pendingStart, left)
788
+ if (taken > 0) {
789
+ r.pendingStart += taken
790
+ }
791
+ if (r.pendingStart === r.pendingEnd) {
792
+ r.pendingStart = 0
793
+ r.pendingEnd = 0
794
+ }
795
+ }
796
+
797
+ /** Ends `r`'s response once nothing is pending, and hands `r` back; otherwise leaves it flushing. */
798
+ finish(r: HttpRequest, conn: Http1Connection): void {
799
+ r.state = HANDLER_FLUSHING
800
+ if (r.pendingEnd > r.pendingStart) {
801
+ return
802
+ }
803
+ // `H1_AGAIN`: no room for the last chunk yet; `H1_WRITE` comes back here.
804
+ if (conn.end() === H1_AGAIN) {
805
+ return
806
+ }
807
+ this.current[r.slot] = -1
808
+ this.release(r)
809
+ }
810
+
811
+ /** A free request, or a new one. */
812
+ obtain(): HttpRequest {
813
+ const count: i32 = toI32(this.free.length)
814
+ if (count > 0) {
815
+ const at: i32 = this.free[count - 1]
816
+ this.free.pop()
817
+ return this.requests[at]
818
+ }
819
+ const r: HttpRequest = new HttpRequest(toI32(this.requests.length))
820
+ r.params = new Array<i32>(2 * this.router.maxParams + 2)
821
+ this.requests.push(r)
822
+ return r
823
+ }
824
+
825
+ release(r: HttpRequest): void {
826
+ if (r.state === HANDLER_FREE) {
827
+ return
828
+ }
829
+ r.state = HANDLER_FREE
830
+ this.free.push(r.index)
831
+ }
832
+
833
+ /** Hands back the request `slot` was reading when the carrier refused it. */
834
+ releaseReading(slot: i32): void {
835
+ const at: i32 = this.current[slot]
836
+ if (at >= 0 && this.requests[at].state === HANDLER_READING) {
837
+ this.current[slot] = -1
838
+ this.release(this.requests[at])
839
+ }
840
+ }
841
+
842
+ /**
843
+ * Closes `slot` and bumps its generation, so any answer still owed for it is
844
+ * dropped. A request the handler alone holds goes back now; one queued or
845
+ * taken goes back when `ready` passes it or the program answers it.
846
+ */
847
+ closeSlot(slot: i32): void {
848
+ this.server.close(slot)
849
+ this.forget(slot)
850
+ }
851
+
852
+ /** The bookkeeping of a slot the carrier has closed. */
853
+ forget(slot: i32): void {
854
+ this.open[slot] = false
855
+ this.slotGen[slot] = this.slotGen[slot] + 1
856
+ const at: i32 = this.current[slot]
857
+ this.current[slot] = -1
858
+ if (at >= 0) {
859
+ const r: HttpRequest = this.requests[at]
860
+ if (r.state === HANDLER_READING || r.state === HANDLER_PARKED || r.state === HANDLER_FLUSHING) {
861
+ this.release(r)
862
+ }
863
+ }
864
+ }
865
+
866
+ /** Closes every connection whose request has been reading its body for longer than `bodyTimeout` at `now`. */
867
+ expireBodies(now: i64): void {
868
+ for (let s: i32 = 0; s < toI32(this.current.length) && s < toI32(this.open.length); s++) {
869
+ const at: i32 = this.current[s]
870
+ if (this.open[s] && at >= 0 && at < toI32(this.requests.length)) {
871
+ const r: HttpRequest = this.requests[at]
872
+ if (r.state === HANDLER_READING && now - r.since > this.bodyTimeout) {
873
+ this.closeSlot(s)
874
+ }
875
+ }
876
+ }
877
+ }
878
+
879
+ /** Every slot the carrier's `expire` closed. */
880
+ sweep(): void {
881
+ for (let s: i32 = 0; s < toI32(this.open.length); s++) {
882
+ if (this.open[s] && !this.server.holds(s)) {
883
+ this.forget(s)
884
+ }
885
+ }
886
+ }
887
+
888
+ // ---- Requests -------------------------------------------------------------------
889
+
890
+ /** Whether a whole request is waiting for `take`. Requests whose connection closed while queued are handed back here. */
891
+ ready(): boolean {
892
+ while (this.queueCount > 0) {
893
+ const r: HttpRequest = this.requests[this.queue[this.queueStart]]
894
+ if (this.live(r)) {
895
+ return true
896
+ }
897
+ this.queueStart = (this.queueStart + 1) % toI32(this.queue.length)
898
+ this.queueCount -= 1
899
+ this.release(r)
900
+ }
901
+ return false
902
+ }
903
+
904
+ /** The next whole request, which the program now owes an answer. Panics when none is `ready`. */
905
+ take(): HttpRequest {
906
+ if (!this.ready()) {
907
+ panic("HttpHandler.take: no request is ready")
908
+ }
909
+ const r: HttpRequest = this.requests[this.queue[this.queueStart]]
910
+ this.queueStart = (this.queueStart + 1) % toI32(this.queue.length)
911
+ this.queueCount -= 1
912
+ r.state = HANDLER_HELD
913
+ return r
914
+ }
915
+
916
+ /** Whether `r`'s connection is the one it was read on, still open. */
917
+ live(r: HttpRequest): boolean {
918
+ return this.server.holds(r.slot) && this.slotGen[r.slot] === r.gen
919
+ }
920
+
921
+ /** How many requests the pool has made: the most ever in flight at once. */
922
+ pooled(): i32 {
923
+ return toI32(this.requests.length)
924
+ }
925
+
926
+ /** How many connections are open. */
927
+ connections(): i32 {
928
+ return this.server.busy()
929
+ }
930
+
931
+ // ---- Responses ------------------------------------------------------------------
932
+
933
+ /**
934
+ * Starts `r`'s response: `status` (200 to 599), the fields `names[k]`:
935
+ * `values[k]`, and a body of `bodyLength` bytes or `HTTP1_CHUNKED`; then
936
+ * `write` the body and `end`. Answers `HANDLER_SENT`, or `HANDLER_DROPPED`
937
+ * when `r`'s connection has closed — every later `write` and `end` for it
938
+ * then does nothing, and `end` hands it back.
939
+ */
940
+ start(r: HttpRequest, status: i32, names: u8[][], values: u8[][], bodyLength: i32): i32 {
941
+ if (r.state !== HANDLER_HELD) {
942
+ panic(`HttpHandler.start: the request is not one taken and unanswered (state ${r.state})`)
943
+ }
944
+ if (status < 200 || status > 599) {
945
+ panic(`HttpHandler.start: status ${status} is outside 200 to 599`)
946
+ }
947
+ if (bodyLength < HTTP1_CHUNKED) {
948
+ panic(`HttpHandler.start: body length ${bodyLength}`)
949
+ }
950
+ r.state = HANDLER_STREAMING
951
+ if (!this.live(r)) {
952
+ return HANDLER_DROPPED
953
+ }
954
+ const conn: Http1Connection = this.server.connection(r.slot)
955
+ const framing: i32 = status === 204 || status === 304 ? HTTP1_NO_BODY : bodyLength
956
+ const sent: i32 = conn.respond(status, names, values, framing)
957
+ if (sent === H1_AGAIN) {
958
+ // The output still holds more of the response before this one than a
959
+ // head can fit behind — only for a head near half the output, since a
960
+ // request waits for that much room before it is queued. Ending the
961
+ // connection is the one answer left.
962
+ this.closeSlot(r.slot)
963
+ return HANDLER_DROPPED
964
+ }
965
+ if (sent !== 0) {
966
+ panic(
967
+ `HttpHandler.start: the carrier refused the head (${sent}): a reserved field, or a name or value that is not a token or field text`
968
+ )
969
+ }
970
+ return HANDLER_SENT
971
+ }
972
+
973
+ /**
974
+ * Writes `buf[off .. off + len)` of `r`'s body. What the carrier has no room
975
+ * for is copied and sent as room comes, so `buf` is the caller's again on
976
+ * return. Answers `HANDLER_SENT` or `HANDLER_DROPPED`.
977
+ */
978
+ write(r: HttpRequest, buf: u8[], off: i32, len: i32): i32 {
979
+ if (r.state !== HANDLER_STREAMING) {
980
+ panic(`HttpHandler.write: the response is not started (state ${r.state})`)
981
+ }
982
+ if (off < 0 || len < 0 || off > toI32(buf.length) - len) {
983
+ panic(`HttpHandler.write: the window ${off} + ${len} is outside its ${buf.length} bytes`)
984
+ }
985
+ if (!this.live(r)) {
986
+ return HANDLER_DROPPED
987
+ }
988
+ const conn: Http1Connection = this.server.connection(r.slot)
989
+ // The carrier checks the length of what it takes; what is pending it has
990
+ // not seen, so the check counts it here.
991
+ const owed: i32 = conn.responseLeft - (r.pendingEnd - r.pendingStart)
992
+ if (conn.responseFraming >= 0 && !conn.responseEmpty && len > owed) {
993
+ panic(
994
+ `HttpHandler.write: ${len} byte(s) is more than the ${owed} the length the response declared leaves`
995
+ )
996
+ }
997
+ let taken: i32 = 0
998
+ if (r.pendingEnd === r.pendingStart) {
999
+ taken = conn.write(buf, off, len)
1000
+ if (taken === H1_AGAIN) {
1001
+ taken = 0
1002
+ } else if (taken < 0) {
1003
+ panic(
1004
+ `HttpHandler.write: the carrier refused the body (${taken}): more than the length the response declared`
1005
+ )
1006
+ }
1007
+ }
1008
+ if (taken < len) {
1009
+ this.keep(r, buf, off + taken, len - taken)
1010
+ }
1011
+ this.drive(r.slot)
1012
+ return HANDLER_SENT
1013
+ }
1014
+
1015
+ /** Appends bytes to `r`'s pending buffer, growing it once to the largest response it has held. */
1016
+ keep(r: HttpRequest, buf: u8[], off: i32, len: i32): void {
1017
+ const live: i32 = r.pendingEnd - r.pendingStart
1018
+ if (toI32(r.pending.length) - r.pendingEnd < len) {
1019
+ if (r.pendingStart > 0 || toI32(r.pending.length) < live + len) {
1020
+ const target: u8[] =
1021
+ toI32(r.pending.length) < live + len
1022
+ ? new Array<u8>(handlerGrown(toI32(r.pending.length), live + len))
1023
+ : r.pending
1024
+ copyBytes(target, HANDLER_ZERO, r.pending, r.pendingStart, live)
1025
+ r.pending = target
1026
+ r.pendingStart = 0
1027
+ r.pendingEnd = live
1028
+ }
1029
+ }
1030
+ copyBytes(r.pending, r.pendingEnd, buf, off, len)
1031
+ r.pendingEnd += len
1032
+ }
1033
+
1034
+ /**
1035
+ * Ends `r`'s response. `r` is the handler's again: it goes back once the
1036
+ * carrier has taken every byte, and the connection's next request is read.
1037
+ * Answers `HANDLER_SENT` or `HANDLER_DROPPED`.
1038
+ */
1039
+ end(r: HttpRequest): i32 {
1040
+ if (r.state !== HANDLER_STREAMING) {
1041
+ panic(`HttpHandler.end: the response is not started (state ${r.state})`)
1042
+ }
1043
+ if (!this.live(r)) {
1044
+ this.release(r)
1045
+ return HANDLER_DROPPED
1046
+ }
1047
+ const slot: i32 = r.slot
1048
+ const conn: Http1Connection = this.server.connection(slot)
1049
+ const short: i32 = conn.responseLeft - (r.pendingEnd - r.pendingStart)
1050
+ if (conn.responseFraming >= 0 && short > 0 && !conn.responseEmpty) {
1051
+ panic(`HttpHandler.end: the body is ${short} byte(s) short of the length the response declared`)
1052
+ }
1053
+ this.finish(r, conn)
1054
+ this.drive(slot)
1055
+ return HANDLER_SENT
1056
+ }
1057
+
1058
+ /** Answers `r` with `status`, a `Content-Type` of `contentType` and the body `body[off .. off + len)`, in one call. */
1059
+ send(r: HttpRequest, status: i32, contentType: u8[], body: u8[], off: i32, len: i32): i32 {
1060
+ this.typeValue[0] = contentType
1061
+ return this.sendFields(r, status, this.typeName, this.typeValue, body, off, len)
1062
+ }
1063
+
1064
+ /** Answers `r` with `status`, the fields `names[k]`: `values[k]` and the body `body[off .. off + len)`, in one call. */
1065
+ sendFields(
1066
+ r: HttpRequest,
1067
+ status: i32,
1068
+ names: u8[][],
1069
+ values: u8[][],
1070
+ body: u8[],
1071
+ off: i32,
1072
+ len: i32
1073
+ ): i32 {
1074
+ const started: i32 = this.start(r, status, names, values, len)
1075
+ if (started === HANDLER_DROPPED) {
1076
+ this.release(r)
1077
+ return HANDLER_DROPPED
1078
+ }
1079
+ if (len > 0 && status !== 204 && status !== 304) {
1080
+ this.write(r, body, off, len)
1081
+ }
1082
+ return this.end(r)
1083
+ }
1084
+
1085
+ /** Answers `r` with `status` and no body: a 204, or a status whose meaning is all there is to say. */
1086
+ empty(r: HttpRequest, status: i32): i32 {
1087
+ return this.sendFields(r, status, this.noFields, this.noFields, this.scratch, HANDLER_ZERO, HANDLER_ZERO)
1088
+ }
1089
+
1090
+ /**
1091
+ * The JSON writer, reset onto the handler's scratch buffer: write one value
1092
+ * with it, then `sendJson`. One JSON body at a time, since every request
1093
+ * shares the buffer.
1094
+ */
1095
+ json(): JsonWriter {
1096
+ this.writer.reset(this.scratch, HANDLER_ZERO)
1097
+ this.jsonBegun = true
1098
+ return this.writer
1099
+ }
1100
+
1101
+ /**
1102
+ * Answers `r` with `status` and the value `json`'s writer holds, as
1103
+ * `application/json`. A value the writer refused or did not finish — it
1104
+ * outgrew `jsonBytes`, say — is answered as a 500 problem instead.
1105
+ */
1106
+ sendJson(r: HttpRequest, status: i32): i32 {
1107
+ if (!this.jsonBegun) {
1108
+ panic("HttpHandler.sendJson: no value was begun with json()")
1109
+ }
1110
+ this.jsonBegun = false
1111
+ if (!this.writer.complete()) {
1112
+ return this.problem(r, toI32(500), "Internal Server Error", "the response did not fit its buffer")
1113
+ }
1114
+ return this.send(r, status, this.jsonType, this.scratch, HANDLER_ZERO, this.writer.end)
1115
+ }
1116
+
1117
+ /**
1118
+ * Answers `r` with an RFC 9457 problem: `application/problem+json` with
1119
+ * `type` `about:blank`, `title` (the status's reason, for that type),
1120
+ * `status` and, when not empty, `detail`, a sentence for a person about
1121
+ * this occurrence. Nothing of the request is echoed, so a detail is the
1122
+ * program's own words.
1123
+ */
1124
+ problem(r: HttpRequest, status: i32, title: string, detail: string): i32 {
1125
+ const length: i32 = this.writeProblem(status, title, detail)
1126
+ return this.send(r, status, this.problemType, this.scratch, HANDLER_ZERO, length)
1127
+ }
1128
+
1129
+ /**
1130
+ * Writes a problem object into the scratch buffer and answers its length.
1131
+ * One too large for it is written without its detail, and then without its
1132
+ * title, which leaves room enough in the least `jsonBytes`.
1133
+ */
1134
+ writeProblem(status: i32, title: string, detail: string): i32 {
1135
+ let length: i32 = this.writeProblemObject(status, title, detail)
1136
+ if (length < 0) {
1137
+ length = this.writeProblemObject(status, title, "")
1138
+ }
1139
+ if (length < 0) {
1140
+ length = this.writeProblemObject(status, "", "")
1141
+ }
1142
+ this.jsonBegun = false
1143
+ return length
1144
+ }
1145
+
1146
+ /** `writeProblem`'s one try: the object's length, or -1 when it did not fit. */
1147
+ writeProblemObject(status: i32, title: string, detail: string): i32 {
1148
+ const w: JsonWriter = this.json()
1149
+ w.beginObject()
1150
+ w.key("type")
1151
+ w.string("about:blank")
1152
+ if (toI32(title.length) > 0) {
1153
+ w.key("title")
1154
+ w.string(title)
1155
+ }
1156
+ w.key("status")
1157
+ w.i32(status)
1158
+ if (toI32(detail.length) > 0) {
1159
+ w.key("detail")
1160
+ w.string(detail)
1161
+ }
1162
+ w.endObject()
1163
+ return w.complete() ? w.end : -1
1164
+ }
1165
+
1166
+ /**
1167
+ * Answers a request the router refused, by its `route`: 405 with an `Allow`
1168
+ * field naming the methods the path has (RFC 9110 §15.5.6), and 404 for
1169
+ * anything else. A parameter's malformed escape is the program's to refuse,
1170
+ * when it decodes the parameter.
1171
+ */
1172
+ refuse(r: HttpRequest): i32 {
1173
+ if (r.route === ROUTER_METHOD_NOT_ALLOWED) {
1174
+ const length: i32 = this.writeProblem(toI32(405), "Method Not Allowed", "")
1175
+ this.allowValues[1] = this.allowFor(r)
1176
+ return this.sendFields(
1177
+ r,
1178
+ toI32(405),
1179
+ this.allowNames,
1180
+ this.allowValues,
1181
+ this.scratch,
1182
+ HANDLER_ZERO,
1183
+ length
1184
+ )
1185
+ }
1186
+ return this.problem(r, toI32(404), "Not Found", "")
1187
+ }
1188
+
1189
+ /**
1190
+ * The `Allow` value for `r`'s path: each method a route of it takes, and
1191
+ * HEAD wherever GET is, since a GET route answers HEAD too. Made once for
1192
+ * each set of methods.
1193
+ */
1194
+ allowFor(r: HttpRequest): u8[] {
1195
+ const router: Router = this.router
1196
+ const methods: i32 = toI32(router.methodStart.length)
1197
+ let mask: i64 = 0
1198
+ for (let m: i32 = 0; m < methods; m++) {
1199
+ const route: i32 = router.match(
1200
+ router.methodBytes,
1201
+ router.methodStart[m],
1202
+ router.methodEnd[m],
1203
+ r.head,
1204
+ r.targetStart,
1205
+ r.targetEnd,
1206
+ this.probe
1207
+ )
1208
+ if (route >= 0) {
1209
+ mask = mask | (toI64(1) << toI64(m))
1210
+ }
1211
+ }
1212
+ for (let k: i32 = 0; k < toI32(this.allowMasks.length); k++) {
1213
+ if (this.allowMasks[k] === mask) {
1214
+ return this.allowBytes[k]
1215
+ }
1216
+ }
1217
+ let get: boolean = false
1218
+ let head: boolean = false
1219
+ const value: u8[] = []
1220
+ for (let m: i32 = 0; m < methods; m++) {
1221
+ if ((mask & (toI64(1) << toI64(m))) !== toI64(0)) {
1222
+ const start: i32 = router.methodStart[m]
1223
+ const end: i32 = router.methodEnd[m]
1224
+ get = get || handlerSpanIs(router.methodBytes, start, end, "GET")
1225
+ head = head || handlerSpanIs(router.methodBytes, start, end, "HEAD")
1226
+ handlerAllowAppend(value, router.methodBytes, start, end)
1227
+ }
1228
+ }
1229
+ if (get && !head) {
1230
+ const name: u8[] = httpFieldBytes("HEAD")
1231
+ handlerAllowAppend(value, name, HANDLER_ZERO, toI32(name.length))
1232
+ }
1233
+ this.allowMasks.push(mask)
1234
+ this.allowBytes.push(value)
1235
+ return value
1236
+ }
1237
+ }
1238
+
1239
+ /** Appends `bytes[start .. end)` to an `Allow` value, after a `, ` if it is not the first. */
1240
+ const handlerAllowAppend = (value: u8[], bytes: u8[], start: i32, end: i32): void => {
1241
+ if (value.length > 0) {
1242
+ value.push(toU8(44))
1243
+ value.push(toU8(32))
1244
+ }
1245
+ for (let k: i32 = start; k < end; k++) {
1246
+ if (k >= 0 && k < toI32(bytes.length)) {
1247
+ value.push(bytes[k])
1248
+ }
1249
+ }
1250
+ }
1251
+
1252
+ /** A buffer size of at least `need`: `have` doubled until it is, starting from 64. */
1253
+ const handlerGrown = (have: i32, need: i32): i32 => {
1254
+ let size: i32 = have < 64 ? 64 : have
1255
+ while (size < need && size < 1073741824) {
1256
+ size = size * 2
1257
+ }
1258
+ return size < need ? need : size
1259
+ }
1260
+
1261
+ /** Whether `bytes[start .. end)` is exactly `text`. */
1262
+ const handlerSpanIs = (bytes: u8[], start: i32, end: i32, text: string): boolean => {
1263
+ const n: i32 = toI32(text.length)
1264
+ if (end - start !== n || start < 0 || end > toI32(bytes.length)) {
1265
+ return false
1266
+ }
1267
+ for (let k: i32 = 0; k < n; k++) {
1268
+ if (toI32(bytes[start + k]) !== toI32(text.charCodeAt(k))) {
1269
+ return false
1270
+ }
1271
+ }
1272
+ return true
1273
+ }
1274
+
1275
+ /** `bytes[start .. end)` as a decimal number that is not negative and fits an `i32`, or -1. */
1276
+ const handlerDecimal = (bytes: u8[], start: i32, end: i32): i32 => {
1277
+ if (start < 0 || end <= start || end - start > 10 || end > toI32(bytes.length)) {
1278
+ return -1
1279
+ }
1280
+ let n: i64 = 0
1281
+ for (let k: i32 = start; k < end && k < toI32(bytes.length); k++) {
1282
+ const digit: i32 = toI32(bytes[k]) - 48
1283
+ if (digit < 0 || digit > 9) {
1284
+ return -1
1285
+ }
1286
+ n = n * toI64(10) + toI64(digit)
1287
+ }
1288
+ return n > toI64(2147483647) ? -1 : toI32(n)
1289
+ }
1290
+
1291
+ /**
1292
+ * The byte the form encoding spells at `bytes[at ..)`, before `end`, and how
1293
+ * many bytes it took, packed as `byte | taken << 8`; -1 for a malformed escape.
1294
+ */
1295
+ const handlerFormByte = (bytes: u8[], at: i32, end: i32): i32 => {
1296
+ const c: i32 = toI32(bytes[at])
1297
+ if (c === 43) {
1298
+ return 32 | HANDLER_TOOK_ONE
1299
+ }
1300
+ if (c !== 37) {
1301
+ return c | HANDLER_TOOK_ONE
1302
+ }
1303
+ if (at + 2 >= end) {
1304
+ return -1
1305
+ }
1306
+ const hi: i32 = http1HexValue(toI32(bytes[at + 1]))
1307
+ const lo: i32 = http1HexValue(toI32(bytes[at + 2]))
1308
+ if (hi < 0 || lo < 0) {
1309
+ return -1
1310
+ }
1311
+ return (hi * 16 + lo) | HANDLER_TOOK_THREE
1312
+ }
1313
+
1314
+ /** Whether `bytes[start .. end)`, form-decoded, is exactly `name`; a malformed escape matches nothing. */
1315
+ const handlerFormIs = (bytes: u8[], start: i32, end: i32, name: string): boolean => {
1316
+ const n: i32 = toI32(name.length)
1317
+ let at: i32 = start
1318
+ let k: i32 = 0
1319
+ while (at < end) {
1320
+ const packed: i32 = handlerFormByte(bytes, at, end)
1321
+ if (packed < 0 || k >= n || (packed & 255) !== toI32(name.charCodeAt(k))) {
1322
+ return false
1323
+ }
1324
+ at += packed >> 8
1325
+ k += 1
1326
+ }
1327
+ return k === n
1328
+ }
1329
+
1330
+ /**
1331
+ * `bytes[start .. end)` form-decoded into `out` from 0: its length,
1332
+ * `ROUTER_BAD_ESCAPE`, or `ROUTER_NO_ROOM`. `%00` decodes like any byte, as a
1333
+ * route parameter's does.
1334
+ */
1335
+ const handlerFormDecode = (bytes: u8[], start: i32, end: i32, out: u8[]): i32 => {
1336
+ const length: i32 = percentDecode(
1337
+ bytes,
1338
+ start,
1339
+ end - start,
1340
+ out,
1341
+ HANDLER_ZERO,
1342
+ URL_DECODE_FORM | URL_DECODE_ALLOW_NUL
1343
+ )
1344
+ if (length === URL_DECODE_ERR_SPACE) {
1345
+ return ROUTER_NO_ROOM
1346
+ }
1347
+ return length < 0 ? ROUTER_BAD_ESCAPE : length
1348
+ }