@amritk/nish-x86_64-linux 0.30.0 → 0.31.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/bin/nish +0 -0
- package/docs/AI.md +56 -4
- package/package.json +1 -1
- package/runtime/core/host.d.ts +2 -0
- package/runtime/core/host.mjs +9 -0
- package/runtime/nish.d.ts +48 -2
- package/runtime/nish.h +9 -1
- package/runtime/nish.mjs +36 -0
- package/runtime/runtime-net.c +32 -3
- package/runtime/runtime-os.c +33 -1
- package/runtime/runtime-region.c +15 -3
- package/runtime/runtime.c +9 -2
- package/std/README.md +8 -7
- package/std/net/{http-client.ts → fetch.ts} +17 -17
- package/std/net/handler.ts +1 -1
- package/std/net/{http1-client.ts → http1-client-codec.ts} +6 -6
- package/std/net/{http1.ts → http1-codec.ts} +2 -2
- package/std/net/http1-server.ts +4 -4
- package/std/net/outbound.ts +2 -2
- package/std/net/postgres.ts +383 -49
- package/std/net/prefork.ts +149 -13
- package/std/net/router.ts +1 -1
- package/std/net/upstream.ts +3 -3
- package/std/net/websocket.ts +5 -5
- package/std/region.ts +95 -0
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `nish/net/
|
|
3
|
-
* programs: `
|
|
4
|
-
* response's head with a reader for its body, and
|
|
2
|
+
* `nish/net/fetch` — a blocking HTTP/1.1 client for command-line
|
|
3
|
+
* programs: `fetchSync(request, options)` dials, sends, and answers the
|
|
4
|
+
* response's head with a reader for its body, and a `FetchSyncClient` does the
|
|
5
5
|
* same while keeping one connection alive between fetches.
|
|
6
6
|
*
|
|
7
|
-
* import { HttpOptions, HttpRequest, HttpResponse,
|
|
7
|
+
* import { HttpOptions, HttpRequest, HttpResponse, fetchSync } from "nish/net/fetch";
|
|
8
8
|
*
|
|
9
9
|
* const req = new HttpRequest("GET", "http://example.com/");
|
|
10
10
|
* req.header("Accept", "application/json");
|
|
11
|
-
* const res: HttpResponse | null =
|
|
11
|
+
* const res: HttpResponse | null = fetchSync(req, new HttpOptions());
|
|
12
12
|
* if (res !== null) {
|
|
13
13
|
* const body: u8[] | null = res.body.readAll(1048576);
|
|
14
14
|
* }
|
|
15
15
|
*
|
|
16
16
|
* **It blocks the thread** at every step, the way a CLI wants it to: the
|
|
17
17
|
* lookup, the connect, each write and each read. A server, which must not
|
|
18
|
-
* stall every connection for one slow peer, drives `nish/net/http1-client`'s
|
|
18
|
+
* stall every connection for one slow peer, drives `nish/net/http1-client-codec`'s
|
|
19
19
|
* parser from its own loop instead. Every wait is bounded: `connectTimeoutMs`
|
|
20
20
|
* for the lookup and the connect (one budget across every address the name
|
|
21
21
|
* has), `readTimeoutMs` for each wait on the peer, and `totalTimeoutMs`, when
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* **Framing is the parser's.** The head is written by `http1RequestHead` and
|
|
26
26
|
* every byte read goes through `Http1ResponseParser`, so a value holding a CR
|
|
27
27
|
* or LF is refused rather than sent, and every response-smuggling shape is
|
|
28
|
-
* refused with the parser's own code (see `docs/security/http1-client.md`).
|
|
28
|
+
* refused with the parser's own code (see `docs/security/http1-client-codec.md`).
|
|
29
29
|
*
|
|
30
30
|
* **Keep-alive** holds one connection, to the last origin (or proxy) used. A
|
|
31
31
|
* fetch to the same place reuses it once the last body has been read to its
|
|
@@ -70,14 +70,14 @@ import {
|
|
|
70
70
|
HTTP1_HEAD,
|
|
71
71
|
HTTP1_NEED_MORE,
|
|
72
72
|
HTTP1_UPGRADE,
|
|
73
|
-
} from "nish/net/http1"
|
|
73
|
+
} from "nish/net/http1-codec"
|
|
74
74
|
import {
|
|
75
75
|
HTTP1_E_BODY_LIMIT,
|
|
76
76
|
HTTP1_E_TRUNCATED,
|
|
77
77
|
HTTP1_INFORMATIONAL,
|
|
78
78
|
Http1ResponseParser,
|
|
79
79
|
http1RequestHead,
|
|
80
|
-
} from "nish/net/http1-client"
|
|
80
|
+
} from "nish/net/http1-client-codec"
|
|
81
81
|
import { httpFieldBytes } from "nish/net/http-fields"
|
|
82
82
|
import { Url, urlDecode, urlDefaultPort, urlOrigin, urlParse, urlResolve } from "nish/net/url"
|
|
83
83
|
import { resolveHost } from "nish/net/dns"
|
|
@@ -293,10 +293,10 @@ export class HttpResponse {
|
|
|
293
293
|
* client that made it, and only until that client starts its next fetch.
|
|
294
294
|
*/
|
|
295
295
|
export class HttpBodyReader {
|
|
296
|
-
client:
|
|
296
|
+
client: FetchSyncClient
|
|
297
297
|
generation: i32
|
|
298
298
|
|
|
299
|
-
constructor(client:
|
|
299
|
+
constructor(client: FetchSyncClient, generation: i32) {
|
|
300
300
|
this.client = client
|
|
301
301
|
this.generation = generation
|
|
302
302
|
}
|
|
@@ -371,7 +371,7 @@ export class HttpBodyReader {
|
|
|
371
371
|
* A client: the options, the proxy settings, and at most one open
|
|
372
372
|
* connection, kept between fetches. Not for two threads at once.
|
|
373
373
|
*/
|
|
374
|
-
export class
|
|
374
|
+
export class FetchSyncClient {
|
|
375
375
|
// Declared widest first, so no byte of a client is padding.
|
|
376
376
|
options: HttpOptions
|
|
377
377
|
errorText: string = ""
|
|
@@ -416,7 +416,7 @@ export class HttpClient {
|
|
|
416
416
|
lost: boolean = false
|
|
417
417
|
/** Whether the latest response's body is unread. */
|
|
418
418
|
bodyOpen: boolean = false
|
|
419
|
-
/** Whether this is `
|
|
419
|
+
/** Whether this is `fetchSync`'s client, closed whole once its one body is done with. */
|
|
420
420
|
once: boolean = false
|
|
421
421
|
|
|
422
422
|
constructor(options: HttpOptions) {
|
|
@@ -1336,7 +1336,7 @@ export class HttpClient {
|
|
|
1336
1336
|
return take
|
|
1337
1337
|
}
|
|
1338
1338
|
|
|
1339
|
-
/** Closes the connection a body is done with, and `
|
|
1339
|
+
/** Closes the connection a body is done with, and `fetchSync`'s whole client with it. */
|
|
1340
1340
|
release(): void {
|
|
1341
1341
|
if (this.once) {
|
|
1342
1342
|
this.close()
|
|
@@ -1391,10 +1391,10 @@ export class HttpClient {
|
|
|
1391
1391
|
/**
|
|
1392
1392
|
* One fetch on a client of its own, closed once the body has been read or
|
|
1393
1393
|
* the fetch failed. Null on a failure, with no way to say why: a program
|
|
1394
|
-
* that reports its errors fetches through
|
|
1394
|
+
* that reports its errors fetches through a `FetchSyncClient`.
|
|
1395
1395
|
*/
|
|
1396
|
-
export const
|
|
1397
|
-
const client:
|
|
1396
|
+
export const fetchSync = (req: HttpRequest, options: HttpOptions): HttpResponse | null => {
|
|
1397
|
+
const client: FetchSyncClient = new FetchSyncClient(options)
|
|
1398
1398
|
client.once = true
|
|
1399
1399
|
const res: HttpResponse | null = client.fetch(req)
|
|
1400
1400
|
if (res === null) {
|
package/std/net/handler.ts
CHANGED
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
import { copyBytes } from "nish/bytes"
|
|
81
81
|
import { JsonWriter } from "nish/json"
|
|
82
82
|
import { httpFieldBytes } from "nish/net/http-fields"
|
|
83
|
-
import { HTTP1_CHUNKED, HTTP1_NO_BODY, http1HexValue } from "nish/net/http1"
|
|
83
|
+
import { HTTP1_CHUNKED, HTTP1_NO_BODY, http1HexValue } from "nish/net/http1-codec"
|
|
84
84
|
import {
|
|
85
85
|
H1_AGAIN,
|
|
86
86
|
H1_BODY,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `nish/net/http1-client` — the client side of HTTP/1.1, sans-IO: a request
|
|
2
|
+
* `nish/net/http1-client-codec` — the client side of HTTP/1.1, sans-IO: a request
|
|
3
3
|
* writer and an incremental response parser, bytes in, events and bytes out,
|
|
4
|
-
* and no socket anywhere. `nish/net/http1` is the server side; the event
|
|
4
|
+
* and no socket anywhere. `nish/net/http1-codec` is the server side; the event
|
|
5
5
|
* constants and the chunk writers are shared and imported from there.
|
|
6
6
|
*
|
|
7
7
|
* **The writer** is `http1RequestHead`, which answers a request line and
|
|
8
8
|
* header section with the framing field the body needs, and
|
|
9
9
|
* `http1WriteRequestHead`, which writes the same head into a caller's buffer
|
|
10
10
|
* with nothing allocated. A chunked body is written with `http1Chunk` and
|
|
11
|
-
* `http1LastChunk` from `nish/net/http1`. The writer refuses anything that
|
|
11
|
+
* `http1LastChunk` from `nish/net/http1-codec`. The writer refuses anything that
|
|
12
12
|
* would let a caller's string end a line early or frame the body itself — the
|
|
13
13
|
* request-splitting and request-smuggling shapes. `http1UpgradeRequestHead`
|
|
14
14
|
* is the one way to ask the server to switch protocols: it writes `Upgrade`
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* what it asked (`expect`), `feed`s whatever slice the socket gave it, cut
|
|
20
20
|
* anywhere, and calls `next()` until it answers `HTTP1_NEED_MORE`:
|
|
21
21
|
*
|
|
22
|
-
* import { HTTP1_NEED_MORE, HTTP1_HEAD, HTTP1_BODY, HTTP1_END, HTTP1_ERROR } from "nish/net/http1";
|
|
23
|
-
* import { Http1ResponseParser } from "nish/net/http1-client";
|
|
22
|
+
* import { HTTP1_NEED_MORE, HTTP1_HEAD, HTTP1_BODY, HTTP1_END, HTTP1_ERROR } from "nish/net/http1-codec";
|
|
23
|
+
* import { Http1ResponseParser } from "nish/net/http1-client-codec";
|
|
24
24
|
*
|
|
25
25
|
* const p = new Http1ResponseParser(maxStatusLine, maxHeaderBytes, maxHeaders, maxBody, maxTrailerBytes);
|
|
26
26
|
* p.expect(false, false);
|
|
@@ -96,7 +96,7 @@ import {
|
|
|
96
96
|
http1PutText,
|
|
97
97
|
http1SameWord,
|
|
98
98
|
http1Text,
|
|
99
|
-
} from "nish/net/http1"
|
|
99
|
+
} from "nish/net/http1-codec"
|
|
100
100
|
|
|
101
101
|
/** `next()`: an interim 1xx response was read; `status` and the fields are its. */
|
|
102
102
|
export const HTTP1_INFORMATIONAL: i32 = 7
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `nish/net/http1` — an incremental HTTP/1.1 request parser and a response
|
|
2
|
+
* `nish/net/http1-codec` — an incremental HTTP/1.1 request parser and a response
|
|
3
3
|
* writer, sans-IO: bytes in, events and bytes out, and no socket anywhere.
|
|
4
4
|
*
|
|
5
5
|
* **The parser** is a push-pull machine. A caller `feed`s whatever slice the
|
|
6
6
|
* socket gave it, of any length and cut anywhere, and then calls `next()`
|
|
7
7
|
* until it answers `HTTP1_NEED_MORE`:
|
|
8
8
|
*
|
|
9
|
-
* import { Http1Parser, HTTP1_HEAD, HTTP1_BODY, HTTP1_END, HTTP1_ERROR } from "nish/net/http1";
|
|
9
|
+
* import { Http1Parser, HTTP1_HEAD, HTTP1_BODY, HTTP1_END, HTTP1_ERROR } from "nish/net/http1-codec";
|
|
10
10
|
*
|
|
11
11
|
* const p = new Http1Parser(maxTarget, maxHeaderBytes, maxHeaders, maxBody);
|
|
12
12
|
* p.feed(received, 0, n);
|
package/std/net/http1-server.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `nish/net/http1-server` — the server side of HTTP/1.1 (RFC 9112): a
|
|
3
|
-
* sans-IO connection that drives `nish/net/http1`'s parser, and two pools of
|
|
3
|
+
* sans-IO connection that drives `nish/net/http1-codec`'s parser, and two pools of
|
|
4
4
|
* them for a program that owns its readiness loop, one on `nish:net`'s plain
|
|
5
5
|
* TCP and one on `nish/net/tls-tcp` with ALPN `http/1.1`.
|
|
6
6
|
*
|
|
@@ -76,12 +76,12 @@
|
|
|
76
76
|
* given later, reads the clock then. `date.pin(unixMs)` fixes it, for a test
|
|
77
77
|
* that compares bytes.
|
|
78
78
|
*
|
|
79
|
-
* **Refusals.** Every refusal of `nish/net/http1`'s parser — the smuggling
|
|
79
|
+
* **Refusals.** Every refusal of `nish/net/http1-codec`'s parser — the smuggling
|
|
80
80
|
* shapes, a `Content-Length` with a `Transfer-Encoding`, two that disagree,
|
|
81
81
|
* a malformed chunk size — is answered with its status (400, 413, 414, 431,
|
|
82
82
|
* 501, 505), `Content-Length: 0` and `Connection: close`, and ends the
|
|
83
83
|
* connection; so does a malformed WebSocket frame, with its close code.
|
|
84
|
-
* `docs/security/http1.md` is the record.
|
|
84
|
+
* `docs/security/http1-codec.md` is the record.
|
|
85
85
|
*
|
|
86
86
|
* **WebSocket.** A request that asked to upgrade reaches the program like any
|
|
87
87
|
* other; `acceptWebSocket(protocol)` answers it with the 101 of
|
|
@@ -145,7 +145,7 @@ import {
|
|
|
145
145
|
http1WriteChunk,
|
|
146
146
|
http1WriteLastChunk,
|
|
147
147
|
http1WriteResponseHead,
|
|
148
|
-
} from "nish/net/http1"
|
|
148
|
+
} from "nish/net/http1-codec"
|
|
149
149
|
import {
|
|
150
150
|
WS_CLOSE,
|
|
151
151
|
WS_ERROR,
|
package/std/net/outbound.ts
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* reaches services that trust the program's address.
|
|
8
8
|
*
|
|
9
9
|
* import { OutboundPolicy } from "nish/net/outbound";
|
|
10
|
-
* import { HttpOptions } from "nish/net/
|
|
10
|
+
* import { HttpOptions } from "nish/net/fetch";
|
|
11
11
|
*
|
|
12
12
|
* const options = new HttpOptions();
|
|
13
13
|
* options.outbound = new OutboundPolicy();
|
|
14
14
|
*
|
|
15
15
|
* **The check is on the address, after the lookup.** A name is never judged
|
|
16
|
-
* by its text: `nish/net/
|
|
16
|
+
* by its text: `nish/net/fetch` resolves it, drops every address
|
|
17
17
|
* `outboundAddressAllowed` refuses, and connects only to the bytes it kept,
|
|
18
18
|
* with no second lookup, so a name whose answer changes between a check and
|
|
19
19
|
* a connect (DNS rebinding) cannot move the connection. Every redirect hop is
|