@amritk/nish-aarch64-darwin 0.35.0 → 0.37.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.
- package/INSTALL.md +73 -59
- package/bin/nish +0 -0
- package/docs/AI.md +50 -4
- package/package.json +1 -1
- package/runtime/async.d.mts +2 -0
- package/runtime/async.mjs +7 -0
- package/runtime/nish.d.ts +4849 -430
- package/runtime/nish.h +25 -0
- package/runtime/page.d.ts +53 -0
- package/runtime/page.mjs +146 -0
- package/runtime/runtime-async.c +557 -0
- package/runtime/runtime-wasm.c +63 -7
- package/runtime/vite.d.ts +30 -0
- package/runtime/vite.mjs +150 -0
- package/scripts/build.sh +252 -15
- package/std/README.md +107 -82
- package/std/base64.ts +34 -5
- package/std/bytes.ts +279 -59
- package/std/cli-args.ts +296 -70
- package/std/cli-flags.ts +184 -19
- package/std/cli.ts +475 -54
- package/std/collections.ts +121 -23
- package/std/compress/checksum.ts +44 -14
- package/std/compress/deflate.ts +224 -111
- package/std/compress/gzip.ts +217 -53
- package/std/compress/zlib.ts +63 -7
- package/std/crypto/aes.ts +128 -52
- package/std/crypto/base64url.ts +29 -5
- package/std/crypto/bcrypt-batch.ts +101 -34
- package/std/crypto/bcrypt.ts +93 -27
- package/std/crypto/chacha20poly1305.ts +69 -14
- package/std/crypto/ct.ts +32 -13
- package/std/crypto/ed25519.ts +66 -16
- package/std/crypto/hkdf.ts +198 -13
- package/std/crypto/hmac.ts +242 -37
- package/std/crypto/jwt.ts +204 -72
- package/std/crypto/p256.ts +222 -72
- package/std/crypto/p384.ts +51 -23
- package/std/crypto/pki-roots.ts +32 -2
- package/std/crypto/pki.ts +99 -30
- package/std/crypto/rsa.ts +56 -22
- package/std/crypto/sha1.ts +3 -0
- package/std/crypto/sha256.ts +24 -1
- package/std/crypto/sha512.ts +99 -14
- package/std/crypto/x25519.ts +163 -24
- package/std/crypto/x509-random.ts +10 -0
- package/std/crypto/x509.ts +133 -66
- package/std/host/ring.ts +124 -0
- package/std/json-equal.ts +31 -11
- package/std/json-format.ts +112 -13
- package/std/json-number.ts +76 -26
- package/std/json-pointer.ts +23 -1
- package/std/json-tokenizer.ts +126 -51
- package/std/json-typed.ts +757 -96
- package/std/json-value.ts +438 -157
- package/std/json.ts +170 -38
- package/std/map.ts +25 -7
- package/std/math.ts +36 -4
- package/std/net/buffer-pool.ts +42 -11
- package/std/net/dns-config.ts +109 -23
- package/std/net/dns-message.ts +62 -7
- package/std/net/dns-resolver.ts +247 -53
- package/std/net/dns.ts +203 -35
- package/std/net/fetch.ts +1267 -290
- package/std/net/handler.ts +296 -80
- package/std/net/hpack.ts +290 -77
- package/std/net/http-compress.ts +152 -36
- package/std/net/http-fields.ts +85 -9
- package/std/net/http1-client-codec.ts +129 -44
- package/std/net/http1-codec.ts +279 -114
- package/std/net/http1-server.ts +409 -139
- package/std/net/http2-frame.ts +289 -51
- package/std/net/http2-tls.ts +152 -49
- package/std/net/http2.ts +314 -160
- package/std/net/http3-frame.ts +165 -27
- package/std/net/http3-server.ts +282 -131
- package/std/net/http3.ts +377 -173
- package/std/net/outbound.ts +37 -3
- package/std/net/postgres-carrier.ts +144 -8
- package/std/net/postgres.ts +898 -260
- package/std/net/prefork.ts +202 -65
- package/std/net/qpack.ts +120 -32
- package/std/net/quic-conn-ack.ts +60 -12
- package/std/net/quic-conn-cid.ts +135 -27
- package/std/net/quic-conn-params.ts +109 -22
- package/std/net/quic-datagram.ts +50 -3
- package/std/net/quic-frame.ts +333 -23
- package/std/net/quic-listener.ts +161 -75
- package/std/net/quic-packet.ts +373 -73
- package/std/net/quic-recovery.ts +287 -65
- package/std/net/quic-stream.ts +436 -146
- package/std/net/quic.ts +693 -323
- package/std/net/router.ts +87 -40
- package/std/net/tls/client.ts +167 -51
- package/std/net/tls/codec.ts +494 -68
- package/std/net/tls/record-client.ts +140 -73
- package/std/net/tls/record-server.ts +233 -105
- package/std/net/tls/record.ts +142 -36
- package/std/net/tls/schedule.ts +235 -26
- package/std/net/tls/ticket.ts +161 -40
- package/std/net/tls/verify.ts +57 -10
- package/std/net/tls-tcp.ts +308 -95
- package/std/net/tls.ts +276 -102
- package/std/net/upstream.ts +81 -45
- package/std/net/url.ts +109 -22
- package/std/net/websocket.ts +140 -26
- package/std/net/webtransport.ts +243 -106
- package/std/pair.ts +10 -2
- package/std/random.ts +42 -15
- package/std/regex.ts +422 -257
- package/std/region.ts +70 -15
- package/std/secret.ts +52 -11
- package/std/sort.ts +17 -2
- package/std/sqlite.ts +256 -35
- package/std/sync/ring.ts +229 -56
- package/std/sync/shm.ts +203 -23
- package/std/term-io.ts +154 -78
- package/std/term-pixels.ts +144 -19
- package/std/term-probe.ts +77 -35
- package/std/term-rate.ts +199 -118
- package/std/term.ts +400 -175
- package/std/testing.ts +130 -32
- package/std/text.ts +84 -17
- package/std/threads.ts +248 -56
- package/std/yaml-json.ts +111 -97
- package/std/yaml-lines.ts +47 -15
- package/std/yaml-write.ts +45 -30
- package/std/yaml.ts +764 -384
package/std/net/handler.ts
CHANGED
|
@@ -162,7 +162,10 @@ export class HandlerLimits {
|
|
|
162
162
|
http: Http1Config
|
|
163
163
|
/** The most connections at once. */
|
|
164
164
|
slots: i32 = 256
|
|
165
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* Bytes in the JSON scratch buffer: the largest body `json` and `problem`
|
|
167
|
+
* can write. A value past it makes `sendJson` answer 500. At least 256.
|
|
168
|
+
*/
|
|
166
169
|
jsonBytes: i32 = 65536
|
|
167
170
|
/** The most readiness events one `wait` takes. */
|
|
168
171
|
maxEvents: i32 = 256
|
|
@@ -176,6 +179,11 @@ export class HandlerLimits {
|
|
|
176
179
|
*/
|
|
177
180
|
bodyTimeout: i32 = 30000
|
|
178
181
|
|
|
182
|
+
/**
|
|
183
|
+
* The defaults: 256 slots, a 64 KiB JSON buffer, 256 events a turn, a 30
|
|
184
|
+
* second body timeout, and the carrier's own defaults with the body cap
|
|
185
|
+
* lowered to one MiB. Change a field after construction to tune one.
|
|
186
|
+
*/
|
|
179
187
|
constructor() {
|
|
180
188
|
this.http = new Http1Config()
|
|
181
189
|
this.http.maxBody = HANDLER_ONE_MIB
|
|
@@ -190,37 +198,85 @@ export class HandlerLimits {
|
|
|
190
198
|
export class HttpRequest {
|
|
191
199
|
/** The method, the target and the header section, as the parser kept them (field names lowercased). */
|
|
192
200
|
head: u8[]
|
|
193
|
-
/**
|
|
201
|
+
/**
|
|
202
|
+
* Four per field, into `head`: name start and end, value start and end.
|
|
203
|
+
* @internal Filled by `HttpHandler` in this module when it copies the head, and read by `header`, `valueStart` and `valueEnd`.
|
|
204
|
+
*/
|
|
194
205
|
spans: i32[]
|
|
195
|
-
/**
|
|
206
|
+
/**
|
|
207
|
+
* The route parameters as `[start, end)` pairs into `head`, in the pattern's order.
|
|
208
|
+
* @internal Filled by `HttpHandler` in this module from `Router.match`; programs read them through `paramStart`, `param` and the rest.
|
|
209
|
+
*/
|
|
196
210
|
params: i32[]
|
|
197
211
|
/** The body, `body[0 .. bodyLength)`. */
|
|
198
212
|
body: u8[]
|
|
199
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* What the carrier had no room for yet: `pending[pendingStart .. pendingEnd)`.
|
|
215
|
+
* @internal Written and flushed by `HttpHandler` in this module while it streams the response.
|
|
216
|
+
*/
|
|
200
217
|
pending: u8[]
|
|
201
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* When the head was read, as the handler's turn saw it (`monotonicNanos`); `bodyTimeout` runs from here.
|
|
220
|
+
* @internal Stamped and checked by `HttpHandler` in this module to close a connection whose body trickles past `bodyTimeout`.
|
|
221
|
+
*/
|
|
202
222
|
since: i64 = 0
|
|
203
223
|
/** The index `Router.add` answered for the route, or a negative `ROUTER_*` code. */
|
|
204
224
|
route: i32 = 0
|
|
205
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* How many parameter pairs `params` holds.
|
|
227
|
+
* @internal Set by `HttpHandler` in this module after routing; the param accessors check `k` against it.
|
|
228
|
+
*/
|
|
206
229
|
paramCount: i32 = 0
|
|
230
|
+
/** Where the method starts in `head`; the method is `head[methodStart .. methodEnd)`, for a log line or a `Router.match`. */
|
|
207
231
|
methodStart: i32 = 0
|
|
232
|
+
/** Where the method ends in `head`, one past its last byte. */
|
|
208
233
|
methodEnd: i32 = 0
|
|
234
|
+
/** Where the request target (path and query) starts in `head`; the target is `head[targetStart .. targetEnd)`. */
|
|
209
235
|
targetStart: i32 = 0
|
|
236
|
+
/** Where the request target ends in `head`, one past its last byte. */
|
|
210
237
|
targetEnd: i32 = 0
|
|
211
|
-
/**
|
|
238
|
+
/**
|
|
239
|
+
* Where the path ends: the target's first `?`, or its end.
|
|
240
|
+
* @internal Set by `HttpHandler` in this module when it copies the head, and read by `pathIs` and `queryIndex`.
|
|
241
|
+
*/
|
|
212
242
|
pathEnd: i32 = 0
|
|
243
|
+
/**
|
|
244
|
+
* How many fields `spans` describes.
|
|
245
|
+
* @internal Set by `HttpHandler` in this module when it copies the head, and read by `header`.
|
|
246
|
+
*/
|
|
213
247
|
headerCount: i32 = 0
|
|
248
|
+
/** How many bytes of `body` are the request's body, 0 for none; read the body as `body[0 .. bodyLength)`. */
|
|
214
249
|
bodyLength: i32 = 0
|
|
215
250
|
/** The connection's slot, and its generation when this request was read. */
|
|
216
251
|
slot: i32 = 0
|
|
252
|
+
/**
|
|
253
|
+
* The slot's generation when this request was read.
|
|
254
|
+
* @internal Read by `HttpHandler` in this module, which drops a reply to a slot that has since closed and been reused.
|
|
255
|
+
*/
|
|
217
256
|
gen: i32 = 0
|
|
218
257
|
/** Its index in the handler's pool. */
|
|
219
258
|
index: i32 = 0
|
|
259
|
+
/**
|
|
260
|
+
* Where the request is in its life, one of the module's private `HANDLER_*` states.
|
|
261
|
+
* @internal Moved by `HttpHandler` in this module from the free list through reading, queued, held and streaming, and back.
|
|
262
|
+
*/
|
|
220
263
|
state: i32 = 0
|
|
264
|
+
/**
|
|
265
|
+
* Where the unsent bytes in `pending` start.
|
|
266
|
+
* @internal Advanced by `HttpHandler` in this module as the carrier takes pending bytes.
|
|
267
|
+
*/
|
|
221
268
|
pendingStart: i32 = 0
|
|
269
|
+
/**
|
|
270
|
+
* Where the unsent bytes in `pending` end.
|
|
271
|
+
* @internal Advanced by `HttpHandler` in this module as `write` keeps what the carrier had no room for.
|
|
272
|
+
*/
|
|
222
273
|
pendingEnd: i32 = 0
|
|
223
274
|
|
|
275
|
+
/**
|
|
276
|
+
* An empty request for the handler's pool; its buffers grow to the first
|
|
277
|
+
* request it holds and are kept for the ones after.
|
|
278
|
+
* @param index its place in the handler's pool, which the free list and the queue name it by
|
|
279
|
+
*/
|
|
224
280
|
constructor(index: i32) {
|
|
225
281
|
this.index = index
|
|
226
282
|
this.head = []
|
|
@@ -230,29 +286,50 @@ export class HttpRequest {
|
|
|
230
286
|
this.pending = []
|
|
231
287
|
}
|
|
232
288
|
|
|
233
|
-
/**
|
|
289
|
+
/**
|
|
290
|
+
* Whether the method is exactly `method` (methods are case-sensitive, RFC 9110 §9.1).
|
|
291
|
+
* @param method the method name to compare with, such as `"GET"`
|
|
292
|
+
* @returns true when the request's method is byte for byte `method`
|
|
293
|
+
*/
|
|
234
294
|
methodIs(method: string): boolean {
|
|
235
295
|
return handlerSpanIs(this.head, this.methodStart, this.methodEnd, method)
|
|
236
296
|
}
|
|
237
297
|
|
|
238
|
-
/**
|
|
298
|
+
/**
|
|
299
|
+
* Whether the path, the target up to its `?`, is exactly `path`.
|
|
300
|
+
* @param path the raw path to compare with, still percent-encoded as it came on the wire
|
|
301
|
+
* @returns true when the target's path, without its query, is byte for byte `path`
|
|
302
|
+
*/
|
|
239
303
|
pathIs(path: string): boolean {
|
|
240
304
|
return handlerSpanIs(this.head, this.targetStart, this.pathEnd, path)
|
|
241
305
|
}
|
|
242
306
|
|
|
243
|
-
/**
|
|
307
|
+
/**
|
|
308
|
+
* Where parameter `k` starts in `head`. Panics on a parameter the route does not have.
|
|
309
|
+
* @param k the parameter's position in the route pattern, from 0
|
|
310
|
+
* @returns the byte offset in `head` of the parameter's first raw byte
|
|
311
|
+
*/
|
|
244
312
|
paramStart(k: i32): i32 {
|
|
245
313
|
this.checkParam(k)
|
|
246
314
|
return this.params[2 * k]
|
|
247
315
|
}
|
|
248
316
|
|
|
249
|
-
/**
|
|
317
|
+
/**
|
|
318
|
+
* Where parameter `k` ends in `head`.
|
|
319
|
+
* @param k the parameter's position in the route pattern, from 0
|
|
320
|
+
* @returns the byte offset in `head` just past the parameter's last raw byte
|
|
321
|
+
*/
|
|
250
322
|
paramEnd(k: i32): i32 {
|
|
251
323
|
this.checkParam(k)
|
|
252
324
|
return this.params[2 * k + 1]
|
|
253
325
|
}
|
|
254
326
|
|
|
255
|
-
/**
|
|
327
|
+
/**
|
|
328
|
+
* Whether parameter `k`, raw, is exactly `text`.
|
|
329
|
+
* @param k the parameter's position in the route pattern, from 0
|
|
330
|
+
* @param text the value to compare with, as it would appear still percent-encoded
|
|
331
|
+
* @returns true when the raw parameter bytes are exactly `text`
|
|
332
|
+
*/
|
|
256
333
|
paramIs(k: i32, text: string): boolean {
|
|
257
334
|
this.checkParam(k)
|
|
258
335
|
return handlerSpanIs(this.head, this.params[2 * k], this.params[2 * k + 1], text)
|
|
@@ -262,19 +339,26 @@ export class HttpRequest {
|
|
|
262
339
|
* Parameter `k` percent-decoded into `out` from 0 (`routerDecode`): its
|
|
263
340
|
* length, `ROUTER_BAD_ESCAPE` for a malformed escape, or `ROUTER_NO_ROOM`
|
|
264
341
|
* when `out` is too short. Nothing is written on a code.
|
|
342
|
+
* @param k the parameter's position in the route pattern, from 0
|
|
343
|
+
* @param out where the decoded bytes go, from index 0; the caller's buffer
|
|
344
|
+
* @returns how many decoded bytes were written, or a negative `ROUTER_*` code
|
|
265
345
|
*/
|
|
266
346
|
param(k: i32, out: u8[]): i32 {
|
|
267
347
|
this.checkParam(k)
|
|
268
348
|
return routerDecode(this.head, this.params[2 * k], this.params[2 * k + 1], out)
|
|
269
349
|
}
|
|
270
350
|
|
|
271
|
-
/**
|
|
351
|
+
/**
|
|
352
|
+
* Parameter `k` read as a decimal number that is not negative and fits an `i32`, or -1.
|
|
353
|
+
* @param k the parameter's position in the route pattern, from 0
|
|
354
|
+
* @returns the number the parameter's digits spell, or -1 when it is empty, holds a non-digit or overflows
|
|
355
|
+
*/
|
|
272
356
|
paramDecimal(k: i32): i32 {
|
|
273
357
|
this.checkParam(k)
|
|
274
358
|
return handlerDecimal(this.head, this.params[2 * k], this.params[2 * k + 1])
|
|
275
359
|
}
|
|
276
360
|
|
|
277
|
-
checkParam(k: i32): void {
|
|
361
|
+
private checkParam(k: i32): void {
|
|
278
362
|
if (k < 0 || k >= this.paramCount) {
|
|
279
363
|
panic(`HttpRequest.param: the route has ${this.paramCount} parameter(s), not one at ${k}`)
|
|
280
364
|
}
|
|
@@ -287,6 +371,9 @@ export class HttpRequest {
|
|
|
287
371
|
* decoded too. `HANDLER_ABSENT` when no parameter has the name,
|
|
288
372
|
* `ROUTER_BAD_ESCAPE` for a malformed escape in its value, `ROUTER_NO_ROOM`
|
|
289
373
|
* when `out` is too short. A name with no `=` has an empty value.
|
|
374
|
+
* @param name the parameter name to look for, compared with each name after decoding
|
|
375
|
+
* @param out where the decoded value goes, from index 0; the caller's buffer
|
|
376
|
+
* @returns how many decoded bytes were written, or `HANDLER_ABSENT` or a negative `ROUTER_*` code
|
|
290
377
|
*/
|
|
291
378
|
query(name: string, out: u8[]): i32 {
|
|
292
379
|
const at: i32 = this.queryIndex(name)
|
|
@@ -296,16 +383,25 @@ export class HttpRequest {
|
|
|
296
383
|
return handlerFormDecode(this.head, this.queryValueStart, this.queryValueEnd, out)
|
|
297
384
|
}
|
|
298
385
|
|
|
299
|
-
/**
|
|
386
|
+
/**
|
|
387
|
+
* Whether the query holds a parameter named `name`.
|
|
388
|
+
* @param name the parameter name to look for, compared with each name after decoding
|
|
389
|
+
* @returns true when some pair in the query has that name, even with an empty value
|
|
390
|
+
*/
|
|
300
391
|
hasQuery(name: string): boolean {
|
|
301
392
|
return this.queryIndex(name) >= 0
|
|
302
393
|
}
|
|
303
394
|
|
|
304
395
|
/** The span `queryIndex` found, raw. */
|
|
305
396
|
queryValueStart: i32 = 0
|
|
397
|
+
/** Where the raw value `queryIndex` found ends in `head`, one past its last byte; it starts at `queryValueStart`. */
|
|
306
398
|
queryValueEnd: i32 = 0
|
|
307
399
|
|
|
308
|
-
/**
|
|
400
|
+
/**
|
|
401
|
+
* Finds the first parameter named `name`: where its pair starts, or -1; its raw value goes to `queryValue...`.
|
|
402
|
+
* @param name the parameter name to look for, compared with each name after decoding
|
|
403
|
+
* @returns the byte offset in `head` where the matching `name=value` pair starts, or -1 when none matches
|
|
404
|
+
*/
|
|
309
405
|
queryIndex(name: string): i32 {
|
|
310
406
|
let at: i32 = this.pathEnd + 1
|
|
311
407
|
const end: i32 = this.targetEnd
|
|
@@ -328,7 +424,11 @@ export class HttpRequest {
|
|
|
328
424
|
return -1
|
|
329
425
|
}
|
|
330
426
|
|
|
331
|
-
/**
|
|
427
|
+
/**
|
|
428
|
+
* The index of the first field named `name`, which is lowercase, or `HANDLER_ABSENT`.
|
|
429
|
+
* @param name the field name in lowercase, since the parser lowercased every name it kept
|
|
430
|
+
* @returns the field's index for `valueStart` and `valueEnd`, or `HANDLER_ABSENT` when no field has the name
|
|
431
|
+
*/
|
|
332
432
|
header(name: string): i32 {
|
|
333
433
|
for (let i: i32 = 0; i < this.headerCount; i++) {
|
|
334
434
|
if (handlerSpanIs(this.head, this.spans[4 * i], this.spans[4 * i + 1], name)) {
|
|
@@ -338,17 +438,30 @@ export class HttpRequest {
|
|
|
338
438
|
return HANDLER_ABSENT
|
|
339
439
|
}
|
|
340
440
|
|
|
341
|
-
/**
|
|
441
|
+
/**
|
|
442
|
+
* Where field `i`'s value starts in `head`.
|
|
443
|
+
* @param i a field index, as `header` answered it
|
|
444
|
+
* @returns the byte offset in `head` of the value's first byte
|
|
445
|
+
*/
|
|
342
446
|
valueStart(i: i32): i32 {
|
|
343
447
|
return this.spans[4 * i + 2]
|
|
344
448
|
}
|
|
345
449
|
|
|
346
|
-
/**
|
|
450
|
+
/**
|
|
451
|
+
* Where field `i`'s value ends in `head`.
|
|
452
|
+
* @param i a field index, as `header` answered it
|
|
453
|
+
* @returns the byte offset in `head` just past the value's last byte
|
|
454
|
+
*/
|
|
347
455
|
valueEnd(i: i32): i32 {
|
|
348
456
|
return this.spans[4 * i + 3]
|
|
349
457
|
}
|
|
350
458
|
|
|
351
|
-
/**
|
|
459
|
+
/**
|
|
460
|
+
* Whether the first field named `name` (lowercase) has exactly the value `value`.
|
|
461
|
+
* @param name the field name in lowercase
|
|
462
|
+
* @param value the value to compare with, byte for byte and case-sensitive
|
|
463
|
+
* @returns true when the field is present and its first occurrence has exactly that value
|
|
464
|
+
*/
|
|
352
465
|
headerIs(name: string, value: string): boolean {
|
|
353
466
|
const i: i32 = this.header(name)
|
|
354
467
|
return i >= 0 && handlerSpanIs(this.head, this.spans[4 * i + 2], this.spans[4 * i + 3], value)
|
|
@@ -360,64 +473,85 @@ export class HttpRequest {
|
|
|
360
473
|
* requests. One per thread; nothing in it is shared.
|
|
361
474
|
*/
|
|
362
475
|
export class HttpHandler {
|
|
476
|
+
/**
|
|
477
|
+
* The HTTP/1.1 carrier the handler drives.
|
|
478
|
+
* @internal Read only by this class, which accepts, reads, flushes and closes connections through it.
|
|
479
|
+
*/
|
|
363
480
|
server: Http1Server
|
|
364
|
-
router: Router
|
|
481
|
+
private router: Router
|
|
365
482
|
/** Every request the pool has made, and the indices of the free ones. */
|
|
366
|
-
requests: HttpRequest[]
|
|
367
|
-
free: i32[]
|
|
483
|
+
private requests: HttpRequest[]
|
|
484
|
+
private free: i32[]
|
|
368
485
|
/** Whole requests waiting for `take`, a ring of `queue.length` with `queueCount` from `queueStart`. */
|
|
369
|
-
queue: i32[]
|
|
486
|
+
private queue: i32[]
|
|
370
487
|
/** Per slot: its generation, its request (-1 for none), and whether it holds a connection. */
|
|
371
|
-
slotGen: i32[]
|
|
372
|
-
current: i32[]
|
|
373
|
-
open: boolean[]
|
|
488
|
+
private slotGen: i32[]
|
|
489
|
+
private current: i32[]
|
|
490
|
+
private open: boolean[]
|
|
374
491
|
/** `pollWait`'s output. */
|
|
375
|
-
readyFds: i32[]
|
|
492
|
+
private readyFds: i32[]
|
|
376
493
|
/** Per watched id: whether its descriptor was ready in the last `wait`. */
|
|
377
|
-
watchedFired: boolean[]
|
|
494
|
+
private watchedFired: boolean[]
|
|
378
495
|
/** `json` and `problem` write here. */
|
|
379
496
|
scratch: u8[]
|
|
497
|
+
/**
|
|
498
|
+
* The JSON writer over `scratch`.
|
|
499
|
+
* @internal Read only by this class; programs get it from `json`.
|
|
500
|
+
*/
|
|
380
501
|
writer: JsonWriter
|
|
381
502
|
/** `refuse`'s scratch for `Router.match`. */
|
|
382
|
-
probe: i32[]
|
|
503
|
+
private probe: i32[]
|
|
383
504
|
/** One field for `send`; two for a 405. */
|
|
384
|
-
typeName: u8[][]
|
|
385
|
-
typeValue: u8[][]
|
|
386
|
-
allowNames: u8[][]
|
|
387
|
-
allowValues: u8[][]
|
|
505
|
+
private typeName: u8[][]
|
|
506
|
+
private typeValue: u8[][]
|
|
507
|
+
private allowNames: u8[][]
|
|
508
|
+
private allowValues: u8[][]
|
|
388
509
|
/** 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[][]
|
|
510
|
+
private allowMasks: i64[]
|
|
511
|
+
private allowBytes: u8[][]
|
|
512
|
+
private jsonType: u8[]
|
|
513
|
+
private problemType: u8[]
|
|
514
|
+
private noFields: u8[][]
|
|
394
515
|
/**
|
|
395
516
|
* When an accept failed for want of a resource (EMFILE, ENFILE, ENOMEM):
|
|
396
517
|
* the listener is out of the loop until then, which it would otherwise wake
|
|
397
518
|
* every turn while its connection waits. 0 while it is in the loop.
|
|
398
519
|
*/
|
|
399
|
-
acceptRestUntil: i64 = 0
|
|
520
|
+
private acceptRestUntil: i64 = 0
|
|
400
521
|
/** `bodyTimeout` in nanoseconds. */
|
|
401
|
-
bodyTimeout: i64 = 0
|
|
402
|
-
/**
|
|
522
|
+
private bodyTimeout: i64 = 0
|
|
523
|
+
/**
|
|
524
|
+
* When the current turn's sockets were ready (`monotonicNanos`): a head read in it is stamped with this.
|
|
525
|
+
* @internal Read by std/net/upstream.ts, whose `pump` compares it with the last turn it saw to run once per handler turn.
|
|
526
|
+
*/
|
|
403
527
|
turnAt: i64 = 0
|
|
404
528
|
/** When `wait` next looks for a body past `bodyTimeout`. */
|
|
405
|
-
bodyCheckAt: i64 = 0
|
|
406
|
-
listener: i32 = 0
|
|
529
|
+
private bodyCheckAt: i64 = 0
|
|
530
|
+
private listener: i32 = 0
|
|
531
|
+
/**
|
|
532
|
+
* The readiness loop's descriptor, from `pollCreate`.
|
|
533
|
+
* @internal Read by std/net/upstream.ts to add its upstream sockets to the same loop, so one `wait` serves both.
|
|
534
|
+
*/
|
|
407
535
|
loop: i32 = 0
|
|
408
|
-
queueStart: i32 = 0
|
|
409
|
-
queueCount: i32 = 0
|
|
410
|
-
outputSize: i32 = 0
|
|
536
|
+
private queueStart: i32 = 0
|
|
537
|
+
private queueCount: i32 = 0
|
|
538
|
+
private outputSize: i32 = 0
|
|
411
539
|
/** Set by `drain`: accept nothing more and close each connection once it is idle. */
|
|
412
|
-
draining: boolean = false
|
|
540
|
+
private draining: boolean = false
|
|
413
541
|
/** Whether `json` gave out the writer for a value `sendJson` has not yet sent. */
|
|
414
|
-
jsonBegun: boolean = false
|
|
542
|
+
private jsonBegun: boolean = false
|
|
415
543
|
|
|
416
544
|
/**
|
|
417
545
|
* A handler over `listener`, a listening socket the program made
|
|
418
546
|
* (`tcpListen`, or `tcpListenFlags` with SO_REUSEPORT for a worker of
|
|
419
547
|
* `nish/net/prefork`), routing through `router`, whose routes are added
|
|
420
548
|
* before the first `wait`.
|
|
549
|
+
* @param limits the caps and timeouts. `slots`, `jsonBytes`, `maxEvents`
|
|
550
|
+
* and `bodyTimeout` are read once here, so changing them later has no
|
|
551
|
+
* effect; `http` is kept by the carrier, which reads its timeouts and
|
|
552
|
+
* caps live
|
|
553
|
+
* @param listener the listening socket's descriptor, still the program's to close
|
|
554
|
+
* @param router the routes, which the handler keeps and consults at each request head
|
|
421
555
|
*/
|
|
422
556
|
constructor(limits: HandlerLimits, listener: i32, router: Router) {
|
|
423
557
|
if (limits.slots < 1 || limits.jsonBytes < 256 || limits.maxEvents < 1 || limits.bodyTimeout < 1) {
|
|
@@ -469,6 +603,8 @@ export class HttpHandler {
|
|
|
469
603
|
* `HANDLER_MAX_WATCHED - 1`): `fired(id)` says whether it was ready in the
|
|
470
604
|
* last `wait`. For a program's own descriptors — `signalFd()` for a
|
|
471
605
|
* graceful stop, a pipe from another thread.
|
|
606
|
+
* @param fd the program's descriptor to watch for reading; it stays the program's to read and close
|
|
607
|
+
* @param id the name `fired` asks about it by, from 0 to `HANDLER_MAX_WATCHED - 1`
|
|
472
608
|
*/
|
|
473
609
|
watch(fd: i32, id: i32): void {
|
|
474
610
|
if (id < 0 || id >= HANDLER_MAX_WATCHED) {
|
|
@@ -477,7 +613,11 @@ export class HttpHandler {
|
|
|
477
613
|
pollAdd(this.loop, fd, H1_WANT_READ, -2 - id)
|
|
478
614
|
}
|
|
479
615
|
|
|
480
|
-
/**
|
|
616
|
+
/**
|
|
617
|
+
* Whether watched descriptor `id` was ready in the last `wait`.
|
|
618
|
+
* @param id the name the descriptor was given to `watch`
|
|
619
|
+
* @returns true when it was readable in the last turn; false for an id outside the range
|
|
620
|
+
*/
|
|
481
621
|
fired(id: i32): boolean {
|
|
482
622
|
return id >= 0 && id < HANDLER_MAX_WATCHED && this.watchedFired[id]
|
|
483
623
|
}
|
|
@@ -487,6 +627,8 @@ export class HttpHandler {
|
|
|
487
627
|
* socket to be ready, accepts, reads and writes what is, closes the
|
|
488
628
|
* connections past their timeouts, and answers how many whole requests
|
|
489
629
|
* are waiting for `take`.
|
|
630
|
+
* @param timeoutMs the longest to block, in milliseconds, or -1 to block until something is ready
|
|
631
|
+
* @returns how many whole requests are queued for `take` after this turn
|
|
490
632
|
*/
|
|
491
633
|
wait(timeoutMs: i32): i32 {
|
|
492
634
|
for (let id: i32 = 0; id < HANDLER_MAX_WATCHED; id++) {
|
|
@@ -569,7 +711,7 @@ export class HttpHandler {
|
|
|
569
711
|
}
|
|
570
712
|
|
|
571
713
|
/** Takes every waiting connection. */
|
|
572
|
-
acceptAll(): void {
|
|
714
|
+
private acceptAll(): void {
|
|
573
715
|
if (this.draining) {
|
|
574
716
|
return
|
|
575
717
|
}
|
|
@@ -594,7 +736,7 @@ export class HttpHandler {
|
|
|
594
736
|
* Takes `slot`'s events until it needs the socket again, then sends what it
|
|
595
737
|
* holds and re-arms it, or closes it once it is over.
|
|
596
738
|
*/
|
|
597
|
-
drive(slot: i32): void {
|
|
739
|
+
private drive(slot: i32): void {
|
|
598
740
|
const conn: Http1Connection = this.server.connection(slot)
|
|
599
741
|
let event: i32 = this.server.next(slot)
|
|
600
742
|
while (event !== H1_NEED_MORE && event !== H1_ERROR) {
|
|
@@ -633,12 +775,12 @@ export class HttpHandler {
|
|
|
633
775
|
}
|
|
634
776
|
|
|
635
777
|
/** Whether `conn`'s output holds at most half its size unsent, so a response head fits behind it. */
|
|
636
|
-
roomy(conn: Http1Connection): boolean {
|
|
778
|
+
private roomy(conn: Http1Connection): boolean {
|
|
637
779
|
return (conn.outputEnd - conn.outputStart) * 2 <= this.outputSize
|
|
638
780
|
}
|
|
639
781
|
|
|
640
782
|
/** A head: a request from the free list, the head copied into it, and its route. */
|
|
641
|
-
begin(slot: i32, conn: Http1Connection): void {
|
|
783
|
+
private begin(slot: i32, conn: Http1Connection): void {
|
|
642
784
|
if (this.draining) {
|
|
643
785
|
// A head read during the drain is the connection's last.
|
|
644
786
|
conn.closeAfter = true
|
|
@@ -683,7 +825,7 @@ export class HttpHandler {
|
|
|
683
825
|
* that route, since a GET resource answers HEAD (RFC 9110 §9.3.2) and the
|
|
684
826
|
* carrier sends no body for it.
|
|
685
827
|
*/
|
|
686
|
-
route(r: HttpRequest): void {
|
|
828
|
+
private route(r: HttpRequest): void {
|
|
687
829
|
const params: i32[] = r.params
|
|
688
830
|
let route: i32 = this.router.match(
|
|
689
831
|
r.head,
|
|
@@ -716,7 +858,7 @@ export class HttpHandler {
|
|
|
716
858
|
}
|
|
717
859
|
|
|
718
860
|
/** Body bytes: appended to the request's body, which the carrier's cap bounds. */
|
|
719
|
-
gather(slot: i32, conn: Http1Connection): void {
|
|
861
|
+
private gather(slot: i32, conn: Http1Connection): void {
|
|
720
862
|
const at: i32 = this.current[slot]
|
|
721
863
|
if (at < 0) {
|
|
722
864
|
return
|
|
@@ -734,7 +876,7 @@ export class HttpHandler {
|
|
|
734
876
|
}
|
|
735
877
|
|
|
736
878
|
/** 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 {
|
|
879
|
+
private whole(slot: i32, conn: Http1Connection): void {
|
|
738
880
|
const at: i32 = this.current[slot]
|
|
739
881
|
if (at < 0) {
|
|
740
882
|
return
|
|
@@ -747,7 +889,7 @@ export class HttpHandler {
|
|
|
747
889
|
}
|
|
748
890
|
}
|
|
749
891
|
|
|
750
|
-
enqueue(r: HttpRequest): void {
|
|
892
|
+
private enqueue(r: HttpRequest): void {
|
|
751
893
|
r.state = HANDLER_QUEUED
|
|
752
894
|
const size: i32 = toI32(this.queue.length)
|
|
753
895
|
if (this.queueCount === size) {
|
|
@@ -763,7 +905,7 @@ export class HttpHandler {
|
|
|
763
905
|
}
|
|
764
906
|
|
|
765
907
|
/** `H1_WRITE`: the carrier has room again for the response under way. */
|
|
766
|
-
resume(slot: i32, conn: Http1Connection): void {
|
|
908
|
+
private resume(slot: i32, conn: Http1Connection): void {
|
|
767
909
|
const at: i32 = this.current[slot]
|
|
768
910
|
if (at < 0) {
|
|
769
911
|
return
|
|
@@ -779,7 +921,7 @@ export class HttpHandler {
|
|
|
779
921
|
}
|
|
780
922
|
|
|
781
923
|
/** Hands the carrier as much of `r`'s pending bytes as it takes. */
|
|
782
|
-
pushPending(r: HttpRequest, conn: Http1Connection): void {
|
|
924
|
+
private pushPending(r: HttpRequest, conn: Http1Connection): void {
|
|
783
925
|
const left: i32 = r.pendingEnd - r.pendingStart
|
|
784
926
|
if (left === 0) {
|
|
785
927
|
return
|
|
@@ -795,7 +937,7 @@ export class HttpHandler {
|
|
|
795
937
|
}
|
|
796
938
|
|
|
797
939
|
/** Ends `r`'s response once nothing is pending, and hands `r` back; otherwise leaves it flushing. */
|
|
798
|
-
finish(r: HttpRequest, conn: Http1Connection): void {
|
|
940
|
+
private finish(r: HttpRequest, conn: Http1Connection): void {
|
|
799
941
|
r.state = HANDLER_FLUSHING
|
|
800
942
|
if (r.pendingEnd > r.pendingStart) {
|
|
801
943
|
return
|
|
@@ -809,7 +951,7 @@ export class HttpHandler {
|
|
|
809
951
|
}
|
|
810
952
|
|
|
811
953
|
/** A free request, or a new one. */
|
|
812
|
-
obtain(): HttpRequest {
|
|
954
|
+
private obtain(): HttpRequest {
|
|
813
955
|
const count: i32 = toI32(this.free.length)
|
|
814
956
|
if (count > 0) {
|
|
815
957
|
const at: i32 = this.free[count - 1]
|
|
@@ -822,7 +964,7 @@ export class HttpHandler {
|
|
|
822
964
|
return r
|
|
823
965
|
}
|
|
824
966
|
|
|
825
|
-
release(r: HttpRequest): void {
|
|
967
|
+
private release(r: HttpRequest): void {
|
|
826
968
|
if (r.state === HANDLER_FREE) {
|
|
827
969
|
return
|
|
828
970
|
}
|
|
@@ -831,7 +973,7 @@ export class HttpHandler {
|
|
|
831
973
|
}
|
|
832
974
|
|
|
833
975
|
/** Hands back the request `slot` was reading when the carrier refused it. */
|
|
834
|
-
releaseReading(slot: i32): void {
|
|
976
|
+
private releaseReading(slot: i32): void {
|
|
835
977
|
const at: i32 = this.current[slot]
|
|
836
978
|
if (at >= 0 && this.requests[at].state === HANDLER_READING) {
|
|
837
979
|
this.current[slot] = -1
|
|
@@ -844,13 +986,13 @@ export class HttpHandler {
|
|
|
844
986
|
* dropped. A request the handler alone holds goes back now; one queued or
|
|
845
987
|
* taken goes back when `ready` passes it or the program answers it.
|
|
846
988
|
*/
|
|
847
|
-
closeSlot(slot: i32): void {
|
|
989
|
+
private closeSlot(slot: i32): void {
|
|
848
990
|
this.server.close(slot)
|
|
849
991
|
this.forget(slot)
|
|
850
992
|
}
|
|
851
993
|
|
|
852
994
|
/** The bookkeeping of a slot the carrier has closed. */
|
|
853
|
-
forget(slot: i32): void {
|
|
995
|
+
private forget(slot: i32): void {
|
|
854
996
|
this.open[slot] = false
|
|
855
997
|
this.slotGen[slot] = this.slotGen[slot] + 1
|
|
856
998
|
const at: i32 = this.current[slot]
|
|
@@ -864,7 +1006,7 @@ export class HttpHandler {
|
|
|
864
1006
|
}
|
|
865
1007
|
|
|
866
1008
|
/** Closes every connection whose request has been reading its body for longer than `bodyTimeout` at `now`. */
|
|
867
|
-
expireBodies(now: i64): void {
|
|
1009
|
+
private expireBodies(now: i64): void {
|
|
868
1010
|
for (let s: i32 = 0; s < toI32(this.current.length) && s < toI32(this.open.length); s++) {
|
|
869
1011
|
const at: i32 = this.current[s]
|
|
870
1012
|
if (this.open[s] && at >= 0 && at < toI32(this.requests.length)) {
|
|
@@ -877,7 +1019,7 @@ export class HttpHandler {
|
|
|
877
1019
|
}
|
|
878
1020
|
|
|
879
1021
|
/** Every slot the carrier's `expire` closed. */
|
|
880
|
-
sweep(): void {
|
|
1022
|
+
private sweep(): void {
|
|
881
1023
|
for (let s: i32 = 0; s < toI32(this.open.length); s++) {
|
|
882
1024
|
if (this.open[s] && !this.server.holds(s)) {
|
|
883
1025
|
this.forget(s)
|
|
@@ -887,7 +1029,10 @@ export class HttpHandler {
|
|
|
887
1029
|
|
|
888
1030
|
// ---- Requests -------------------------------------------------------------------
|
|
889
1031
|
|
|
890
|
-
/**
|
|
1032
|
+
/**
|
|
1033
|
+
* Whether a whole request is waiting for `take`. Requests whose connection closed while queued are handed back here.
|
|
1034
|
+
* @returns true when `take` will give out a request whose connection is still open
|
|
1035
|
+
*/
|
|
891
1036
|
ready(): boolean {
|
|
892
1037
|
while (this.queueCount > 0) {
|
|
893
1038
|
const r: HttpRequest = this.requests[this.queue[this.queueStart]]
|
|
@@ -901,7 +1046,10 @@ export class HttpHandler {
|
|
|
901
1046
|
return false
|
|
902
1047
|
}
|
|
903
1048
|
|
|
904
|
-
/**
|
|
1049
|
+
/**
|
|
1050
|
+
* The next whole request, which the program now owes an answer. Panics when none is `ready`.
|
|
1051
|
+
* @returns the oldest queued request, the program's until it answers it
|
|
1052
|
+
*/
|
|
905
1053
|
take(): HttpRequest {
|
|
906
1054
|
if (!this.ready()) {
|
|
907
1055
|
panic("HttpHandler.take: no request is ready")
|
|
@@ -914,16 +1062,22 @@ export class HttpHandler {
|
|
|
914
1062
|
}
|
|
915
1063
|
|
|
916
1064
|
/** Whether `r`'s connection is the one it was read on, still open. */
|
|
917
|
-
live(r: HttpRequest): boolean {
|
|
1065
|
+
private live(r: HttpRequest): boolean {
|
|
918
1066
|
return this.server.holds(r.slot) && this.slotGen[r.slot] === r.gen
|
|
919
1067
|
}
|
|
920
1068
|
|
|
921
|
-
/**
|
|
1069
|
+
/**
|
|
1070
|
+
* How many requests the pool has made: the most ever in flight at once.
|
|
1071
|
+
* @returns the pool's size, which only grows, so a flat count shows nothing leaks
|
|
1072
|
+
*/
|
|
922
1073
|
pooled(): i32 {
|
|
923
1074
|
return toI32(this.requests.length)
|
|
924
1075
|
}
|
|
925
1076
|
|
|
926
|
-
/**
|
|
1077
|
+
/**
|
|
1078
|
+
* How many connections are open.
|
|
1079
|
+
* @returns the number of carrier slots holding a connection, which falls to 0 after `drain`
|
|
1080
|
+
*/
|
|
927
1081
|
connections(): i32 {
|
|
928
1082
|
return this.server.busy()
|
|
929
1083
|
}
|
|
@@ -936,6 +1090,14 @@ export class HttpHandler {
|
|
|
936
1090
|
* `write` the body and `end`. Answers `HANDLER_SENT`, or `HANDLER_DROPPED`
|
|
937
1091
|
* when `r`'s connection has closed — every later `write` and `end` for it
|
|
938
1092
|
* then does nothing, and `end` hands it back.
|
|
1093
|
+
* @param r a request taken and not yet answered
|
|
1094
|
+
* @param status the response status, from 200 to 599
|
|
1095
|
+
* @param names the field names, as bytes, beside `values`
|
|
1096
|
+
* @param values each field's value, `values[k]` for `names[k]`
|
|
1097
|
+
* @param bodyLength how many body bytes will follow, or `HTTP1_CHUNKED` for a body of unknown length
|
|
1098
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed
|
|
1099
|
+
* first, or when the carrier's output was too full to take the head and
|
|
1100
|
+
* the connection was closed for it
|
|
939
1101
|
*/
|
|
940
1102
|
start(r: HttpRequest, status: i32, names: u8[][], values: u8[][], bodyLength: i32): i32 {
|
|
941
1103
|
if (r.state !== HANDLER_HELD) {
|
|
@@ -974,6 +1136,11 @@ export class HttpHandler {
|
|
|
974
1136
|
* Writes `buf[off .. off + len)` of `r`'s body. What the carrier has no room
|
|
975
1137
|
* for is copied and sent as room comes, so `buf` is the caller's again on
|
|
976
1138
|
* return. Answers `HANDLER_SENT` or `HANDLER_DROPPED`.
|
|
1139
|
+
* @param r a request whose response `start` began
|
|
1140
|
+
* @param buf the bytes to send from; the caller's again on return
|
|
1141
|
+
* @param off where in `buf` the body bytes start
|
|
1142
|
+
* @param len how many bytes to send
|
|
1143
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed first
|
|
977
1144
|
*/
|
|
978
1145
|
write(r: HttpRequest, buf: u8[], off: i32, len: i32): i32 {
|
|
979
1146
|
if (r.state !== HANDLER_STREAMING) {
|
|
@@ -1013,7 +1180,7 @@ export class HttpHandler {
|
|
|
1013
1180
|
}
|
|
1014
1181
|
|
|
1015
1182
|
/** 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 {
|
|
1183
|
+
private keep(r: HttpRequest, buf: u8[], off: i32, len: i32): void {
|
|
1017
1184
|
const live: i32 = r.pendingEnd - r.pendingStart
|
|
1018
1185
|
if (toI32(r.pending.length) - r.pendingEnd < len) {
|
|
1019
1186
|
if (r.pendingStart > 0 || toI32(r.pending.length) < live + len) {
|
|
@@ -1035,6 +1202,8 @@ export class HttpHandler {
|
|
|
1035
1202
|
* Ends `r`'s response. `r` is the handler's again: it goes back once the
|
|
1036
1203
|
* carrier has taken every byte, and the connection's next request is read.
|
|
1037
1204
|
* Answers `HANDLER_SENT` or `HANDLER_DROPPED`.
|
|
1205
|
+
* @param r a request whose response `start` began; the handler's again after this call
|
|
1206
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed first
|
|
1038
1207
|
*/
|
|
1039
1208
|
end(r: HttpRequest): i32 {
|
|
1040
1209
|
if (r.state !== HANDLER_STREAMING) {
|
|
@@ -1055,13 +1224,36 @@ export class HttpHandler {
|
|
|
1055
1224
|
return HANDLER_SENT
|
|
1056
1225
|
}
|
|
1057
1226
|
|
|
1058
|
-
/**
|
|
1227
|
+
/**
|
|
1228
|
+
* Answers `r` with `status`, a `Content-Type` of `contentType` and the body `body[off .. off + len)`, in one call.
|
|
1229
|
+
* @param r a request taken and not yet answered; the handler's again after this call
|
|
1230
|
+
* @param status the response status, from 200 to 599
|
|
1231
|
+
* @param contentType the `Content-Type` value, as bytes
|
|
1232
|
+
* @param body the buffer holding the body; the caller's again on return
|
|
1233
|
+
* @param off where in `body` the body bytes start
|
|
1234
|
+
* @param len how many body bytes to send
|
|
1235
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed
|
|
1236
|
+
* first, or when the carrier's output was too full to take the head and
|
|
1237
|
+
* the connection was closed for it
|
|
1238
|
+
*/
|
|
1059
1239
|
send(r: HttpRequest, status: i32, contentType: u8[], body: u8[], off: i32, len: i32): i32 {
|
|
1060
1240
|
this.typeValue[0] = contentType
|
|
1061
1241
|
return this.sendFields(r, status, this.typeName, this.typeValue, body, off, len)
|
|
1062
1242
|
}
|
|
1063
1243
|
|
|
1064
|
-
/**
|
|
1244
|
+
/**
|
|
1245
|
+
* Answers `r` with `status`, the fields `names[k]`: `values[k]` and the body `body[off .. off + len)`, in one call.
|
|
1246
|
+
* @param r a request taken and not yet answered; the handler's again after this call
|
|
1247
|
+
* @param status the response status, from 200 to 599
|
|
1248
|
+
* @param names the field names, as bytes, beside `values`
|
|
1249
|
+
* @param values each field's value, `values[k]` for `names[k]`
|
|
1250
|
+
* @param body the buffer holding the body; the caller's again on return
|
|
1251
|
+
* @param off where in `body` the body bytes start
|
|
1252
|
+
* @param len how many body bytes to send
|
|
1253
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed
|
|
1254
|
+
* first, or when the carrier's output was too full to take the head and
|
|
1255
|
+
* the connection was closed for it
|
|
1256
|
+
*/
|
|
1065
1257
|
sendFields(
|
|
1066
1258
|
r: HttpRequest,
|
|
1067
1259
|
status: i32,
|
|
@@ -1082,7 +1274,14 @@ export class HttpHandler {
|
|
|
1082
1274
|
return this.end(r)
|
|
1083
1275
|
}
|
|
1084
1276
|
|
|
1085
|
-
/**
|
|
1277
|
+
/**
|
|
1278
|
+
* Answers `r` with `status` and no body: a 204, or a status whose meaning is all there is to say.
|
|
1279
|
+
* @param r a request taken and not yet answered; the handler's again after this call
|
|
1280
|
+
* @param status the response status, from 200 to 599
|
|
1281
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed
|
|
1282
|
+
* first, or when the carrier's output was too full to take the head and
|
|
1283
|
+
* the connection was closed for it
|
|
1284
|
+
*/
|
|
1086
1285
|
empty(r: HttpRequest, status: i32): i32 {
|
|
1087
1286
|
return this.sendFields(r, status, this.noFields, this.noFields, this.scratch, HANDLER_ZERO, HANDLER_ZERO)
|
|
1088
1287
|
}
|
|
@@ -1091,6 +1290,7 @@ export class HttpHandler {
|
|
|
1091
1290
|
* The JSON writer, reset onto the handler's scratch buffer: write one value
|
|
1092
1291
|
* with it, then `sendJson`. One JSON body at a time, since every request
|
|
1093
1292
|
* shares the buffer.
|
|
1293
|
+
* @returns the handler's own writer, empty, valid until the next `json`, `sendJson` or `problem`
|
|
1094
1294
|
*/
|
|
1095
1295
|
json(): JsonWriter {
|
|
1096
1296
|
this.writer.reset(this.scratch, HANDLER_ZERO)
|
|
@@ -1102,6 +1302,11 @@ export class HttpHandler {
|
|
|
1102
1302
|
* Answers `r` with `status` and the value `json`'s writer holds, as
|
|
1103
1303
|
* `application/json`. A value the writer refused or did not finish — it
|
|
1104
1304
|
* outgrew `jsonBytes`, say — is answered as a 500 problem instead.
|
|
1305
|
+
* @param r a request taken and not yet answered; the handler's again after this call
|
|
1306
|
+
* @param status the response status for a value that fit, from 200 to 599
|
|
1307
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed
|
|
1308
|
+
* first, or when the carrier's output was too full to take the head and
|
|
1309
|
+
* the connection was closed for it
|
|
1105
1310
|
*/
|
|
1106
1311
|
sendJson(r: HttpRequest, status: i32): i32 {
|
|
1107
1312
|
if (!this.jsonBegun) {
|
|
@@ -1120,6 +1325,13 @@ export class HttpHandler {
|
|
|
1120
1325
|
* `status` and, when not empty, `detail`, a sentence for a person about
|
|
1121
1326
|
* this occurrence. Nothing of the request is echoed, so a detail is the
|
|
1122
1327
|
* program's own words.
|
|
1328
|
+
* @param r a request taken and not yet answered; the handler's again after this call
|
|
1329
|
+
* @param status the response status, from 200 to 599, also written into the object
|
|
1330
|
+
* @param title the status's reason phrase, or empty to leave it out
|
|
1331
|
+
* @param detail a sentence for a person about this occurrence, or empty to leave it out
|
|
1332
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed
|
|
1333
|
+
* first, or when the carrier's output was too full to take the head and
|
|
1334
|
+
* the connection was closed for it
|
|
1123
1335
|
*/
|
|
1124
1336
|
problem(r: HttpRequest, status: i32, title: string, detail: string): i32 {
|
|
1125
1337
|
const length: i32 = this.writeProblem(status, title, detail)
|
|
@@ -1131,7 +1343,7 @@ export class HttpHandler {
|
|
|
1131
1343
|
* One too large for it is written without its detail, and then without its
|
|
1132
1344
|
* title, which leaves room enough in the least `jsonBytes`.
|
|
1133
1345
|
*/
|
|
1134
|
-
writeProblem(status: i32, title: string, detail: string): i32 {
|
|
1346
|
+
private writeProblem(status: i32, title: string, detail: string): i32 {
|
|
1135
1347
|
let length: i32 = this.writeProblemObject(status, title, detail)
|
|
1136
1348
|
if (length < 0) {
|
|
1137
1349
|
length = this.writeProblemObject(status, title, "")
|
|
@@ -1144,7 +1356,7 @@ export class HttpHandler {
|
|
|
1144
1356
|
}
|
|
1145
1357
|
|
|
1146
1358
|
/** `writeProblem`'s one try: the object's length, or -1 when it did not fit. */
|
|
1147
|
-
writeProblemObject(status: i32, title: string, detail: string): i32 {
|
|
1359
|
+
private writeProblemObject(status: i32, title: string, detail: string): i32 {
|
|
1148
1360
|
const w: JsonWriter = this.json()
|
|
1149
1361
|
w.beginObject()
|
|
1150
1362
|
w.key("type")
|
|
@@ -1168,6 +1380,10 @@ export class HttpHandler {
|
|
|
1168
1380
|
* field naming the methods the path has (RFC 9110 §15.5.6), and 404 for
|
|
1169
1381
|
* anything else. A parameter's malformed escape is the program's to refuse,
|
|
1170
1382
|
* when it decodes the parameter.
|
|
1383
|
+
* @param r a request whose `route` is negative, taken and not yet answered; the handler's again after this call
|
|
1384
|
+
* @returns `HANDLER_SENT`, or `HANDLER_DROPPED` when the connection closed
|
|
1385
|
+
* first, or when the carrier's output was too full to take the head and
|
|
1386
|
+
* the connection was closed for it
|
|
1171
1387
|
*/
|
|
1172
1388
|
refuse(r: HttpRequest): i32 {
|
|
1173
1389
|
if (r.route === ROUTER_METHOD_NOT_ALLOWED) {
|
|
@@ -1191,7 +1407,7 @@ export class HttpHandler {
|
|
|
1191
1407
|
* HEAD wherever GET is, since a GET route answers HEAD too. Made once for
|
|
1192
1408
|
* each set of methods.
|
|
1193
1409
|
*/
|
|
1194
|
-
allowFor(r: HttpRequest): u8[] {
|
|
1410
|
+
private allowFor(r: HttpRequest): u8[] {
|
|
1195
1411
|
const router: Router = this.router
|
|
1196
1412
|
const methods: i32 = toI32(router.methodStart.length)
|
|
1197
1413
|
let mask: i64 = 0
|