@amritk/nish-x86_64-linux 0.23.0 → 0.24.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 +26 -19
- package/package.json +1 -1
- package/runtime/core/arrays.d.ts +0 -2
- package/runtime/core/arrays.mjs +0 -2
- package/runtime/core/numbers.mjs +2 -3
- package/runtime/nish.d.ts +0 -4
- package/runtime/nish.mjs +2 -5
- package/runtime/runtime-net.c +1 -1
- package/runtime/runtime-term.c +2 -2
- package/runtime/runtime.c +1 -1
- package/std/README.md +20 -0
- package/std/compress/checksum.ts +178 -0
- package/std/compress/deflate.ts +2400 -0
- package/std/compress/gzip.ts +747 -0
- package/std/compress/zlib.ts +168 -0
- package/std/crypto/sha256.ts +2 -3
- package/std/crypto/x25519.ts +3 -4
- package/std/net/http-compress.ts +393 -0
- package/std/net/http1-server.ts +115 -14
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,95 @@ 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.
|
|
1276
|
+
const outputEnd: i32 = this.outputEnd
|
|
1277
|
+
const closeAfter: boolean = this.closeAfter
|
|
1278
|
+
const r: i32 = this.respond(status, enc.names, enc.values, n)
|
|
1279
|
+
if (r !== 0) {
|
|
1280
|
+
return r
|
|
1281
|
+
}
|
|
1282
|
+
if (!this.responseEmpty && n > this.bodyRoom()) {
|
|
1283
|
+
const fits: boolean = this.outputEnd - outputEnd + n <= this.config.outputSize - H1_CONTROL_RESERVE
|
|
1284
|
+
this.outputEnd = outputEnd
|
|
1285
|
+
this.closeAfter = closeAfter
|
|
1286
|
+
this.response = H1_R_NONE
|
|
1287
|
+
this.responseFraming = 0
|
|
1288
|
+
this.responseLeft = 0
|
|
1289
|
+
this.responseEmpty = false
|
|
1290
|
+
if (!fits) {
|
|
1291
|
+
return H1_INVALID
|
|
1292
|
+
}
|
|
1293
|
+
this.writeHeld = true
|
|
1294
|
+
return H1_AGAIN
|
|
1295
|
+
}
|
|
1296
|
+
const w: i32 = packed >= 0 ? this.write(enc.output, toI32(0), n) : this.write(body, off, n)
|
|
1297
|
+
if (w !== n) {
|
|
1298
|
+
panic("Http1Connection.respondEncoded: a body the room was checked for did not fit")
|
|
1299
|
+
}
|
|
1300
|
+
return this.end()
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1208
1303
|
// ---- WebSocket ----------------------------------------------------------------
|
|
1209
1304
|
|
|
1210
1305
|
/**
|
|
@@ -1334,18 +1429,24 @@ export class Http1Connection {
|
|
|
1334
1429
|
}
|
|
1335
1430
|
|
|
1336
1431
|
/**
|
|
1337
|
-
* The soonest `expired` can turn true
|
|
1338
|
-
* here: `lastActive` only grows, and a
|
|
1339
|
-
* no earlier than
|
|
1432
|
+
* The soonest `expired` can turn true at or after `now` (`monotonicNanos`),
|
|
1433
|
+
* however the connection moves from here: `lastActive` only grows, and a
|
|
1434
|
+
* head clock not yet started starts no earlier than `now`, unless an
|
|
1435
|
+
* exchange is open: `nextRequest` starts a pipelined head's clock at
|
|
1436
|
+
* `lastActive` when it ends. Counting a quiet keep-alive's clock from
|
|
1437
|
+
* `lastActive` put it in the past once it was quiet for longer than
|
|
1438
|
+
* `headTimeout`, so each `expire` walked every slot.
|
|
1340
1439
|
*/
|
|
1341
|
-
expiresNoSooner(): i64 {
|
|
1342
|
-
const idle: i64 = toI64(this.config.idleTimeout) * toI64(1000000)
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1440
|
+
expiresNoSooner(now: i64): i64 {
|
|
1441
|
+
const idle: i64 = this.lastActive + toI64(this.config.idleTimeout) * toI64(1000000)
|
|
1442
|
+
let start: i64 = now
|
|
1443
|
+
if (this.headTimed) {
|
|
1444
|
+
start = this.headSince
|
|
1445
|
+
} else if (this.requestOpen) {
|
|
1446
|
+
start = this.lastActive
|
|
1347
1447
|
}
|
|
1348
|
-
|
|
1448
|
+
const head: i64 = start + toI64(this.config.headTimeout) * toI64(1000000)
|
|
1449
|
+
return head < idle ? head : idle
|
|
1349
1450
|
}
|
|
1350
1451
|
}
|
|
1351
1452
|
|
|
@@ -1679,8 +1780,8 @@ export class Http1Server {
|
|
|
1679
1780
|
if (conn.expired(now)) {
|
|
1680
1781
|
this.close(k)
|
|
1681
1782
|
n = n + 1
|
|
1682
|
-
} else if (conn.expiresNoSooner() < soonest) {
|
|
1683
|
-
soonest = conn.expiresNoSooner()
|
|
1783
|
+
} else if (conn.expiresNoSooner(now) < soonest) {
|
|
1784
|
+
soonest = conn.expiresNoSooner(now)
|
|
1684
1785
|
}
|
|
1685
1786
|
}
|
|
1686
1787
|
}
|
|
@@ -1982,8 +2083,8 @@ export class Http1TlsServer {
|
|
|
1982
2083
|
if (conn.expired(now)) {
|
|
1983
2084
|
this.tls.close(k)
|
|
1984
2085
|
n = n + 1
|
|
1985
|
-
} else if (conn.expiresNoSooner() < soonest) {
|
|
1986
|
-
soonest = conn.expiresNoSooner()
|
|
2086
|
+
} else if (conn.expiresNoSooner(now) < soonest) {
|
|
2087
|
+
soonest = conn.expiresNoSooner(now)
|
|
1987
2088
|
}
|
|
1988
2089
|
}
|
|
1989
2090
|
}
|