@amritk/nish-x86_64-linux 0.23.1 → 0.25.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 +50 -23
- package/package.json +1 -1
- package/runtime/browser.mjs +4 -3
- package/runtime/core/arrays.d.ts +0 -2
- package/runtime/core/arrays.mjs +0 -2
- package/runtime/core/host.d.ts +8 -0
- package/runtime/core/host.mjs +50 -2
- package/runtime/core/numbers.mjs +2 -3
- package/runtime/nish.d.ts +100 -4
- package/runtime/nish.h +34 -0
- package/runtime/nish.mjs +23 -5
- package/runtime/node.mjs +57 -6
- package/runtime/runtime-net.c +45 -4
- package/runtime/runtime-os.c +115 -12
- package/runtime/runtime-term.c +2 -2
- package/runtime/runtime.c +1 -1
- package/scripts/build.sh +13 -3
- package/std/README.md +44 -7
- package/std/cli-args.ts +1216 -0
- package/std/cli-flags.ts +585 -0
- package/std/cli.ts +308 -19
- package/std/compress/checksum.ts +290 -0
- package/std/compress/deflate.ts +2790 -0
- package/std/compress/gzip.ts +761 -0
- package/std/compress/zlib.ts +168 -0
- package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
- package/std/crypto/p256.ts +63 -1
- package/std/crypto/p384.ts +6044 -0
- package/std/crypto/pki-roots.ts +322 -0
- package/std/crypto/pki.ts +1207 -0
- package/std/crypto/rsa.ts +663 -0
- package/std/crypto/sha256.ts +2 -3
- package/std/crypto/x25519.ts +3 -4
- package/std/crypto/x509.ts +1054 -58
- package/std/net/dns-config.ts +734 -0
- package/std/net/dns-message.ts +517 -0
- package/std/net/dns-resolver.ts +1015 -0
- package/std/net/dns.ts +478 -0
- package/std/net/http-compress.ts +393 -0
- package/std/net/http1-client.ts +1227 -0
- package/std/net/http1-server.ts +101 -1
- package/std/net/http1.ts +9 -9
- package/std/net/tls/client.ts +1094 -0
- package/std/net/tls/codec.ts +585 -3
- package/std/net/tls/record-client.ts +1192 -0
- package/std/net/tls/verify.ts +221 -0
- package/std/net/tls-tcp.ts +642 -11
- package/std/net/tls.ts +2 -2
- package/std/net/url.ts +559 -4
- package/std/yaml-write.ts +415 -0
package/std/net/http1-server.ts
CHANGED
|
@@ -114,6 +114,12 @@
|
|
|
114
114
|
*/
|
|
115
115
|
import { Secret } from "nish:secret"
|
|
116
116
|
import { copyBytes } from "nish/bytes"
|
|
117
|
+
import {
|
|
118
|
+
HTTP_CODING_IDENTITY,
|
|
119
|
+
HTTP_OFFER_GZIP,
|
|
120
|
+
HttpCompressor,
|
|
121
|
+
httpAcceptEncoding,
|
|
122
|
+
} from "nish/net/http-compress"
|
|
117
123
|
import { netClose, netRead, netWrite, tcpAccept } from "nish:net"
|
|
118
124
|
import {
|
|
119
125
|
HTTP1_BODY,
|
|
@@ -1205,6 +1211,98 @@ export class Http1Connection {
|
|
|
1205
1211
|
return 0
|
|
1206
1212
|
}
|
|
1207
1213
|
|
|
1214
|
+
// ---- Content coding -----------------------------------------------------------
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* The content coding to answer the current request with, read from its
|
|
1218
|
+
* `Accept-Encoding` with gzip offered (`httpAcceptEncoding`):
|
|
1219
|
+
* `HTTP_CODING_GZIP`, `HTTP_CODING_IDENTITY` (also with no such field), or
|
|
1220
|
+
* `HTTP_CODING_NONE` when the client refuses identity and does not take
|
|
1221
|
+
* gzip. Allocates nothing.
|
|
1222
|
+
*/
|
|
1223
|
+
acceptEncoding(): i32 {
|
|
1224
|
+
const parser: Http1Parser = this.parser
|
|
1225
|
+
const i: i32 = parser.headerIndex("accept-encoding")
|
|
1226
|
+
const spans: i32[] = parser.spans
|
|
1227
|
+
if (i < 0 || 4 * i + 3 >= toI32(spans.length)) {
|
|
1228
|
+
return HTTP_CODING_IDENTITY
|
|
1229
|
+
}
|
|
1230
|
+
const start: i32 = spans[4 * i + 2]
|
|
1231
|
+
const end: i32 = spans[4 * i + 3]
|
|
1232
|
+
if (start < 0 || start > end || end > toI32(parser.head.length)) {
|
|
1233
|
+
return HTTP_CODING_IDENTITY
|
|
1234
|
+
}
|
|
1235
|
+
return httpAcceptEncoding(parser.head, start, end, HTTP_OFFER_GZIP)
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Answers the current request with all of `body[off .. off + len)` in one
|
|
1240
|
+
* call: `respond`, `write` and `end`, the body compressed by `enc` when
|
|
1241
|
+
* `coding` (from `acceptEncoding`) is gzip and `enc.compress` takes it,
|
|
1242
|
+
* with `Content-Encoding: gzip` and the compressed `Content-Length`, and
|
|
1243
|
+
* otherwise sent as it is; `Vary: Accept-Encoding` either way while
|
|
1244
|
+
* `enc.vary` is set. The program's fields may not name `Content-Encoding`
|
|
1245
|
+
* or, with `enc.vary`, `Vary`. The whole response is written or none of
|
|
1246
|
+
* it: `H1_AGAIN` when the output has no room for it yet (`H1_WRITE` says
|
|
1247
|
+
* when to try again, compressing again), `H1_INVALID` for a response
|
|
1248
|
+
* larger than the output could ever hold or one `respond` refuses,
|
|
1249
|
+
* `H1_CLOSED` with no request to answer; 0 once it is queued. A window
|
|
1250
|
+
* outside `body` panics.
|
|
1251
|
+
*/
|
|
1252
|
+
respondEncoded(
|
|
1253
|
+
status: i32,
|
|
1254
|
+
names: u8[][],
|
|
1255
|
+
values: u8[][],
|
|
1256
|
+
body: u8[],
|
|
1257
|
+
off: i32,
|
|
1258
|
+
len: i32,
|
|
1259
|
+
enc: HttpCompressor,
|
|
1260
|
+
coding: i32
|
|
1261
|
+
): i32 {
|
|
1262
|
+
http1ServerCheckWindow("Http1Connection.respondEncoded", body, off, len)
|
|
1263
|
+
if (!this.answerable()) {
|
|
1264
|
+
return H1_CLOSED
|
|
1265
|
+
}
|
|
1266
|
+
const packed: i32 = enc.compress(coding, body, off, len)
|
|
1267
|
+
let n: i32 = len
|
|
1268
|
+
if (packed >= 0) {
|
|
1269
|
+
enc.addFields(names, values)
|
|
1270
|
+
n = packed
|
|
1271
|
+
} else {
|
|
1272
|
+
enc.identityFields(names, values)
|
|
1273
|
+
}
|
|
1274
|
+
// `respond` and the body together, or neither: what `respond` sets is
|
|
1275
|
+
// put back when the body does not fit behind the head. `room` first, so
|
|
1276
|
+
// that what was sent is already moved out of the way and the `outputEnd`
|
|
1277
|
+
// put back is one `respond` did not move.
|
|
1278
|
+
this.room()
|
|
1279
|
+
const outputEnd: i32 = this.outputEnd
|
|
1280
|
+
const closeAfter: boolean = this.closeAfter
|
|
1281
|
+
const r: i32 = this.respond(status, enc.names, enc.values, n)
|
|
1282
|
+
if (r !== 0) {
|
|
1283
|
+
return r
|
|
1284
|
+
}
|
|
1285
|
+
if (!this.responseEmpty && n > this.bodyRoom()) {
|
|
1286
|
+
const fits: boolean = this.outputEnd - outputEnd + n <= this.config.outputSize - H1_CONTROL_RESERVE
|
|
1287
|
+
this.outputEnd = outputEnd
|
|
1288
|
+
this.closeAfter = closeAfter
|
|
1289
|
+
this.response = H1_R_NONE
|
|
1290
|
+
this.responseFraming = 0
|
|
1291
|
+
this.responseLeft = 0
|
|
1292
|
+
this.responseEmpty = false
|
|
1293
|
+
if (!fits) {
|
|
1294
|
+
return H1_INVALID
|
|
1295
|
+
}
|
|
1296
|
+
this.writeHeld = true
|
|
1297
|
+
return H1_AGAIN
|
|
1298
|
+
}
|
|
1299
|
+
const w: i32 = packed >= 0 ? this.write(enc.output, toI32(0), n) : this.write(body, off, n)
|
|
1300
|
+
if (w !== n) {
|
|
1301
|
+
panic("Http1Connection.respondEncoded: a body the room was checked for did not fit")
|
|
1302
|
+
}
|
|
1303
|
+
return this.end()
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1208
1306
|
// ---- WebSocket ----------------------------------------------------------------
|
|
1209
1307
|
|
|
1210
1308
|
/**
|
|
@@ -1859,7 +1957,9 @@ export class Http1TlsServer {
|
|
|
1859
1957
|
readable(slot: i32): i32 {
|
|
1860
1958
|
const live: boolean = this.live(slot)
|
|
1861
1959
|
const now: i64 = monotonicNanos()
|
|
1862
|
-
|
|
1960
|
+
// What a lingering slot drains is not activity, so `idleTimeout` bounds
|
|
1961
|
+
// how long a client that trickles bytes after the close can hold it.
|
|
1962
|
+
if (live && !this.tls.lingering(slot)) {
|
|
1863
1963
|
this.connections[slot].lastActive = now
|
|
1864
1964
|
}
|
|
1865
1965
|
this.tls.readable(slot)
|
package/std/net/http1.ts
CHANGED
|
@@ -524,7 +524,7 @@ const http1TakeLine = (p: Http1Parser, limit: i32, overStatus: i32): i32 => {
|
|
|
524
524
|
}
|
|
525
525
|
|
|
526
526
|
/** Whether `c` is a tchar of RFC 9110 §5.6.2, the bytes a token is made of. */
|
|
527
|
-
const http1IsTokenByte = (c: i32): boolean =>
|
|
527
|
+
export const http1IsTokenByte = (c: i32): boolean =>
|
|
528
528
|
(c >= 97 && c <= 122) ||
|
|
529
529
|
(c >= 65 && c <= 90) ||
|
|
530
530
|
(c >= 48 && c <= 57) ||
|
|
@@ -541,10 +541,10 @@ const http1IsTokenByte = (c: i32): boolean =>
|
|
|
541
541
|
c === 126
|
|
542
542
|
|
|
543
543
|
/** Whether `c` may appear in a field value (RFC 9110 §5.5): HTAB, SP, VCHAR or obs-text. */
|
|
544
|
-
const http1IsValueByte = (c: i32): boolean => c === 9 || (c >= 32 && c !== 127)
|
|
544
|
+
export const http1IsValueByte = (c: i32): boolean => c === 9 || (c >= 32 && c !== 127)
|
|
545
545
|
|
|
546
546
|
/** `buf[from .. to)` as a string, each upper-case ASCII letter lowered when `lower`. */
|
|
547
|
-
const http1Text = (buf: u8[], from: i32, to: i32, lower: boolean): string => {
|
|
547
|
+
export const http1Text = (buf: u8[], from: i32, to: i32, lower: boolean): string => {
|
|
548
548
|
const parts: string[] = []
|
|
549
549
|
for (let k: i32 = from; k >= 0 && k < to && k < toI32(buf.length); k += 1) {
|
|
550
550
|
let c: i32 = toI32(buf[k])
|
|
@@ -557,10 +557,10 @@ const http1Text = (buf: u8[], from: i32, to: i32, lower: boolean): string => {
|
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
/** Whether `c` is optional whitespace, a space or a tab (OWS, RFC 9110 §5.6.3). */
|
|
560
|
-
const http1IsOws = (c: i32): boolean => c === 32 || c === 9
|
|
560
|
+
export const http1IsOws = (c: i32): boolean => c === 32 || c === 9
|
|
561
561
|
|
|
562
562
|
/** Whether `text[a .. b)` is `word`, which is lowercase, in any ASCII case. */
|
|
563
|
-
const http1SameWord = (text: string, a: i32, b: i32, word: string): boolean => {
|
|
563
|
+
export const http1SameWord = (text: string, a: i32, b: i32, word: string): boolean => {
|
|
564
564
|
const n: i32 = toI32(word.length)
|
|
565
565
|
if (a < 0 || b - a !== n || b > toI32(text.length)) {
|
|
566
566
|
return false
|
|
@@ -989,7 +989,7 @@ const http1ContentLength = (bytes: u8[], from: i32, to: i32, limit: i32): i32 =>
|
|
|
989
989
|
}
|
|
990
990
|
|
|
991
991
|
/** The value of the hex digit `c`, or -1. */
|
|
992
|
-
const http1HexValue = (c: i32): i32 => {
|
|
992
|
+
export const http1HexValue = (c: i32): i32 => {
|
|
993
993
|
if (c >= 48 && c <= 57) {
|
|
994
994
|
return c - 48
|
|
995
995
|
}
|
|
@@ -1190,7 +1190,7 @@ const http1BytesOf = (text: string): u8[] => {
|
|
|
1190
1190
|
}
|
|
1191
1191
|
|
|
1192
1192
|
/** Whether every byte of `text` is a tchar, and there is at least one. */
|
|
1193
|
-
const http1IsToken = (text: string): boolean => {
|
|
1193
|
+
export const http1IsToken = (text: string): boolean => {
|
|
1194
1194
|
const n: i32 = toI32(text.length)
|
|
1195
1195
|
if (n === 0) {
|
|
1196
1196
|
return false
|
|
@@ -1204,7 +1204,7 @@ const http1IsToken = (text: string): boolean => {
|
|
|
1204
1204
|
}
|
|
1205
1205
|
|
|
1206
1206
|
/** Whether every byte of `text` may stand in a field value or a reason phrase. */
|
|
1207
|
-
const http1IsFieldText = (text: string): boolean => {
|
|
1207
|
+
export const http1IsFieldText = (text: string): boolean => {
|
|
1208
1208
|
const n: i32 = toI32(text.length)
|
|
1209
1209
|
for (let k: i32 = 0; k < n; k += 1) {
|
|
1210
1210
|
if (!http1IsValueByte(toI32(text.charCodeAt(k)))) {
|
|
@@ -1395,7 +1395,7 @@ const http1DecimalDigits = (n: i32): i32 => {
|
|
|
1395
1395
|
}
|
|
1396
1396
|
|
|
1397
1397
|
/** Writes `text` at `out[at ..)`, which the caller has made sure holds it, and answers where it ends. */
|
|
1398
|
-
const http1PutText = (out: u8[], at: i32, text: string): i32 => {
|
|
1398
|
+
export const http1PutText = (out: u8[], at: i32, text: string): i32 => {
|
|
1399
1399
|
const n: i32 = toI32(text.length)
|
|
1400
1400
|
for (let k: i32 = 0; k < n && at + k < toI32(out.length); k += 1) {
|
|
1401
1401
|
if (at + k >= 0) {
|