@amritk/nish-x86_64-linux 0.20.0 → 0.21.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 +22 -10
- package/bin/nish +0 -0
- package/docs/AI.md +2759 -0
- package/package.json +2 -1
- package/runtime/browser.d.ts +14 -0
- package/runtime/browser.mjs +88 -0
- package/runtime/core/arrays.d.ts +18 -0
- package/runtime/core/arrays.mjs +125 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +70 -0
- package/runtime/core/host.mjs +311 -0
- package/runtime/core/numbers.d.ts +53 -0
- package/runtime/core/numbers.mjs +316 -0
- package/runtime/core/result.d.ts +41 -0
- package/runtime/core/result.mjs +127 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +44 -0
- package/runtime/nish.h +75 -2
- package/runtime/nish.mjs +23 -3
- package/runtime/node.mjs +383 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +1 -1
- package/runtime/runtime-host.c +1 -1
- package/runtime/runtime-os.c +1 -1
- package/runtime/runtime-parallel.c +1 -1
- package/runtime/runtime-simd.c +231 -7
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +0 -11
- package/runtime/runtime.c +190 -6
- package/runtime/shim.mjs +6 -985
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +261 -53
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +31 -11
- package/std/base64.ts +116 -0
- package/std/bytes.ts +256 -0
- package/std/cli.ts +328 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +760 -0
- package/std/crypto/hmac.ts +138 -2
- package/std/crypto/x509.ts +11 -122
- package/std/json-tokenizer.ts +2998 -57
- package/std/json-value.ts +22 -3
- package/std/json.ts +83 -1
- package/std/math.ts +608 -0
- package/std/net/http1-server.ts +85 -8
- package/std/net/http3-server.ts +50 -12
- package/std/net/postgres.ts +1875 -0
- package/std/net/quic-listener.ts +20 -4
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/yaml-lines.ts +141 -0
- package/std/yaml.ts +4675 -0
package/std/net/http1-server.ts
CHANGED
|
@@ -88,7 +88,8 @@
|
|
|
88
88
|
*
|
|
89
89
|
* **Caps are start-up numbers**, all in `Http1Config`: the request target,
|
|
90
90
|
* the header section and its field count, a request body, the chunk handed
|
|
91
|
-
* over, the output buffer, a WebSocket message
|
|
91
|
+
* over, the output buffer, a WebSocket message, the idle timeout and the
|
|
92
|
+
* deadline for a whole request head (`headTimeout`, against slowloris); the
|
|
92
93
|
* slots are the pool's size. `Http1Server` builds a slot's connection on the
|
|
93
94
|
* first accept into it and keeps it, so a pool sized for its peak costs at
|
|
94
95
|
* start-up only what does not depend on the size, and holds at most `size`
|
|
@@ -344,6 +345,13 @@ export class Http1Config {
|
|
|
344
345
|
maxMessage: i32 = 1048576
|
|
345
346
|
/** Milliseconds without a byte read or written after which `expire` closes a slot. */
|
|
346
347
|
idleTimeout: i32 = 60000
|
|
348
|
+
/**
|
|
349
|
+
* Milliseconds a request head may take, from its first byte to its blank
|
|
350
|
+
* line, before `expire` closes the slot however steadily its bytes come. The
|
|
351
|
+
* idle timeout alone restarts on every byte, so a client that sends one byte
|
|
352
|
+
* of its head each `idleTimeout` would hold its slot for good (slowloris).
|
|
353
|
+
*/
|
|
354
|
+
headTimeout: i32 = 30000
|
|
347
355
|
}
|
|
348
356
|
|
|
349
357
|
/** The reason phrase a status is sent with; RFC 9112 §4 lets a client ignore it. */
|
|
@@ -482,6 +490,8 @@ export class Http1Connection {
|
|
|
482
490
|
noFields: u8[][]
|
|
483
491
|
/** When the carrier last moved a byte for the connection, in `monotonicNanos`. */
|
|
484
492
|
lastActive: i64 = 0
|
|
493
|
+
/** When the head being read began to arrive, in `monotonicNanos`; meaningful while `headTimed`. */
|
|
494
|
+
headSince: i64 = 0
|
|
485
495
|
outputStart: i32 = 0
|
|
486
496
|
outputEnd: i32 = 0
|
|
487
497
|
dataStart: i32 = 0
|
|
@@ -517,6 +527,8 @@ export class Http1Connection {
|
|
|
517
527
|
pongPending: boolean = false
|
|
518
528
|
/** The peer's byte stream has ended. */
|
|
519
529
|
inputEnded: boolean = false
|
|
530
|
+
/** A head has begun to arrive and is not read whole yet: `headSince` is its deadline's start. */
|
|
531
|
+
headTimed: boolean = false
|
|
520
532
|
|
|
521
533
|
/** A connection under `config`. A cap outside what the connection can work with panics. */
|
|
522
534
|
constructor(config: Http1Config) {
|
|
@@ -532,6 +544,7 @@ export class Http1Connection {
|
|
|
532
544
|
http1ServerCheckCap("outputSize", config.outputSize, output, big)
|
|
533
545
|
http1ServerCheckCap("maxMessage", config.maxMessage, H1_ZERO, big - H1_FRAME_HEADER)
|
|
534
546
|
http1ServerCheckCap("idleTimeout", config.idleTimeout, one, big)
|
|
547
|
+
http1ServerCheckCap("headTimeout", config.headTimeout, one, big)
|
|
535
548
|
// The buffers the peer's bytes fill grow by doubling and stop at 2^30
|
|
536
549
|
// (`Http1Parser`'s input and head, `WsDecoder`'s input), so a cap whose
|
|
537
550
|
// buffer could need more is refused here rather than reached by a peer:
|
|
@@ -592,6 +605,8 @@ export class Http1Connection {
|
|
|
592
605
|
this.writeHeld = false
|
|
593
606
|
this.pongPending = false
|
|
594
607
|
this.inputEnded = false
|
|
608
|
+
this.headTimed = false
|
|
609
|
+
this.headSince = 0
|
|
595
610
|
this.release()
|
|
596
611
|
}
|
|
597
612
|
|
|
@@ -778,9 +793,16 @@ export class Http1Connection {
|
|
|
778
793
|
this.state = H1_S_CLOSING
|
|
779
794
|
return H1_NEED_MORE
|
|
780
795
|
}
|
|
796
|
+
// A pipelined head that waited behind the exchange starts its clock
|
|
797
|
+
// now that it is the one being read. The carrier stamped `lastActive`
|
|
798
|
+
// when it last moved a byte, which is no earlier than these bytes came.
|
|
799
|
+
this.headArrived(this.lastActive)
|
|
781
800
|
}
|
|
782
801
|
const event: i32 = this.parser.next()
|
|
783
802
|
if (event === HTTP1_HEAD) {
|
|
803
|
+
// The head is whole, so its deadline is met; the next head's starts
|
|
804
|
+
// when its first byte is fed (`headArrived`).
|
|
805
|
+
this.headTimed = false
|
|
784
806
|
this.requestOpen = true
|
|
785
807
|
this.response = H1_R_NONE
|
|
786
808
|
this.closeAfter = !this.parser.keepAlive
|
|
@@ -1185,6 +1207,44 @@ export class Http1Connection {
|
|
|
1185
1207
|
idle(now: i64): boolean {
|
|
1186
1208
|
return now - this.lastActive > toI64(this.config.idleTimeout) * toI64(1000000)
|
|
1187
1209
|
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Starts the head deadline at `now` (`monotonicNanos`) when bytes of a
|
|
1213
|
+
* request head wait to be read and its clock has not started. The carriers
|
|
1214
|
+
* call it after each `feed`; a program that feeds a connection itself calls
|
|
1215
|
+
* it the same way. Bytes that wait while the previous exchange is still
|
|
1216
|
+
* open start no clock: they count from when it is over (`nextRequest`).
|
|
1217
|
+
*/
|
|
1218
|
+
headArrived(now: i64): void {
|
|
1219
|
+
if (this.parser.buffered() > 0) {
|
|
1220
|
+
this.headAwaited(now)
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/**
|
|
1225
|
+
* Starts the head deadline at `now` (`monotonicNanos`) when the connection
|
|
1226
|
+
* waits for a head and its clock has not started, whether or not a byte of
|
|
1227
|
+
* it has been read. `Http1TlsServer` calls it when ciphertext arrives after
|
|
1228
|
+
* the handshake: a record trickled a byte at a time hands no plaintext to
|
|
1229
|
+
* `headArrived` until it is whole, so its bytes would otherwise only keep
|
|
1230
|
+
* the slot from going idle.
|
|
1231
|
+
*/
|
|
1232
|
+
headAwaited(now: i64): void {
|
|
1233
|
+
if (!this.headTimed && this.state === H1_S_HTTP && !this.requestOpen) {
|
|
1234
|
+
this.headTimed = true
|
|
1235
|
+
this.headSince = now
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/** Whether a head has been arriving for longer than `headTimeout` at `now` (`monotonicNanos`). */
|
|
1240
|
+
headLate(now: i64): boolean {
|
|
1241
|
+
return this.headTimed && now - this.headSince > toI64(this.config.headTimeout) * toI64(1000000)
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/** Whether `expire` closes the slot at `now`: idle, or a head past its deadline. */
|
|
1245
|
+
expired(now: i64): boolean {
|
|
1246
|
+
return this.idle(now) || this.headLate(now)
|
|
1247
|
+
}
|
|
1188
1248
|
}
|
|
1189
1249
|
|
|
1190
1250
|
// ---- Carriers -------------------------------------------------------------------
|
|
@@ -1369,7 +1429,9 @@ export class Http1Server {
|
|
|
1369
1429
|
}
|
|
1370
1430
|
}
|
|
1371
1431
|
if (moved) {
|
|
1372
|
-
|
|
1432
|
+
const now: i64 = monotonicNanos()
|
|
1433
|
+
conn.lastActive = now
|
|
1434
|
+
conn.headArrived(now)
|
|
1373
1435
|
}
|
|
1374
1436
|
return this.flush(slot)
|
|
1375
1437
|
}
|
|
@@ -1471,11 +1533,14 @@ export class Http1Server {
|
|
|
1471
1533
|
}
|
|
1472
1534
|
}
|
|
1473
1535
|
|
|
1474
|
-
/**
|
|
1536
|
+
/**
|
|
1537
|
+
* Closes every slot that has been idle past `idleTimeout`, or reading one
|
|
1538
|
+
* head for longer than `headTimeout`, at `now` (`monotonicNanos`); answers how many.
|
|
1539
|
+
*/
|
|
1475
1540
|
expire(now: i64): i32 {
|
|
1476
1541
|
let n: i32 = 0
|
|
1477
1542
|
for (let k: i32 = 0; k < toI32(this.fds.length) && k < toI32(this.connections.length); k++) {
|
|
1478
|
-
if (this.fds[k] >= 0 && this.connections[k].
|
|
1543
|
+
if (this.fds[k] >= 0 && this.connections[k].expired(now)) {
|
|
1479
1544
|
this.close(k)
|
|
1480
1545
|
n = n + 1
|
|
1481
1546
|
}
|
|
@@ -1633,10 +1698,19 @@ export class Http1TlsServer {
|
|
|
1633
1698
|
|
|
1634
1699
|
/** The socket of `slot` is readable: TLS reads it, and what the connection has to send goes after. Answers the interest. */
|
|
1635
1700
|
readable(slot: i32): i32 {
|
|
1636
|
-
|
|
1637
|
-
|
|
1701
|
+
const live: boolean = this.live(slot)
|
|
1702
|
+
const now: i64 = monotonicNanos()
|
|
1703
|
+
if (live) {
|
|
1704
|
+
this.connections[slot].lastActive = now
|
|
1638
1705
|
}
|
|
1639
1706
|
this.tls.readable(slot)
|
|
1707
|
+
// Ciphertext after the handshake is the start of a head, or of a record
|
|
1708
|
+
// that will hold one, so the head's clock starts here; a record trickled
|
|
1709
|
+
// a byte at a time would hand `next` no plaintext until it is whole. The
|
|
1710
|
+
// handshake itself is bounded by `idleTimeout` alone (TLS-4).
|
|
1711
|
+
if (live && this.tls.connection(slot).state === TLS_RECORD_STATE_OPEN) {
|
|
1712
|
+
this.connections[slot].headAwaited(now)
|
|
1713
|
+
}
|
|
1640
1714
|
return this.flush(slot)
|
|
1641
1715
|
}
|
|
1642
1716
|
|
|
@@ -1737,11 +1811,14 @@ export class Http1TlsServer {
|
|
|
1737
1811
|
}
|
|
1738
1812
|
}
|
|
1739
1813
|
|
|
1740
|
-
/**
|
|
1814
|
+
/**
|
|
1815
|
+
* Closes every slot that has been idle past `idleTimeout`, or reading one
|
|
1816
|
+
* head for longer than `headTimeout`, at `now` (`monotonicNanos`); answers how many.
|
|
1817
|
+
*/
|
|
1741
1818
|
expire(now: i64): i32 {
|
|
1742
1819
|
let n: i32 = 0
|
|
1743
1820
|
for (let k: i32 = 0; k < this.tls.size() && k < toI32(this.connections.length); k++) {
|
|
1744
|
-
if (this.tls.holds(k) && this.connections[k].
|
|
1821
|
+
if (this.tls.holds(k) && this.connections[k].expired(now)) {
|
|
1745
1822
|
this.tls.close(k)
|
|
1746
1823
|
n = n + 1
|
|
1747
1824
|
}
|
package/std/net/http3-server.ts
CHANGED
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
* **Sending.** `flush` writes each touched slot's datagrams with
|
|
42
42
|
* `quicListenerTakeFlight`, a paced flight at a time in one `udpSendTo` with
|
|
43
43
|
* GSO where the platform has it and one datagram at a time where it does not.
|
|
44
|
+
* Each flight goes with the ECN codepoint its connection marked it with
|
|
45
|
+
* (`QuicFlight.ecn`, RFC 9000 §13.4), so the peer's ACK_ECN counts are
|
|
46
|
+
* validated and a CE mark shrinks the window. Where the platform sends no
|
|
47
|
+
* ECN (Darwin, whose `udpSendTo` answers -95 to a marked send), the first
|
|
48
|
+
* marked send goes again unmarked and no slot marks after it. A -95 is also
|
|
49
|
+
* what a platform without segmentation offload answers, so a refused GSO
|
|
50
|
+
* send goes again one datagram at a time, still marked, and only a -95 to
|
|
51
|
+
* one of those turns marking off.
|
|
44
52
|
* The pacer counts the microseconds `clock` gives it, so on a path whose
|
|
45
53
|
* round trip is under a millisecond it neither waits for the next
|
|
46
54
|
* millisecond to earn credit nor holds a slot back for a whole one; without
|
|
@@ -122,8 +130,8 @@ export const H3_SERVER_ANSWER_INTERVAL: i64 = 100
|
|
|
122
130
|
/** The address form `nish:net` reads and writes. */
|
|
123
131
|
const H3_SERVER_ADDRESS: i32 = 18
|
|
124
132
|
|
|
125
|
-
/** What `udpSendTo` answers where the platform has no segmentation offload. */
|
|
126
|
-
const
|
|
133
|
+
/** What `udpSendTo` answers where the platform has no segmentation offload, or sends no ECN. */
|
|
134
|
+
const H3_SERVER_UNSUPPORTED: i32 = -95
|
|
127
135
|
|
|
128
136
|
/** The largest datagram read: a GRO receive of up to 64 KB. */
|
|
129
137
|
const H3_SERVER_RECEIVE_SIZE: i32 = 65536
|
|
@@ -484,6 +492,8 @@ export class Http3Server {
|
|
|
484
492
|
refused: i32 = 0
|
|
485
493
|
limited: i32 = 0
|
|
486
494
|
sendErrors: i32 = 0
|
|
495
|
+
/** Whether the platform sends ECN marks: true until a marked send answers -95, and every slot's `ecnMarking` with it. */
|
|
496
|
+
ecn: boolean = true
|
|
487
497
|
/**
|
|
488
498
|
* The stream buffers every slot's connection borrows from, made by the
|
|
489
499
|
* constructor for the stream table `quicConfig` gives every slot: `null`
|
|
@@ -578,6 +588,7 @@ export class Http3Server {
|
|
|
578
588
|
build(): void {
|
|
579
589
|
crypto.getRandomValues(this.entropy)
|
|
580
590
|
const quic = new QuicConnection(this.quicConfig, this.entropy)
|
|
591
|
+
quic.ecnMarking = this.ecn
|
|
581
592
|
this.quics.push(quic)
|
|
582
593
|
const pools: QuicStreamPools | null = this.streamPools
|
|
583
594
|
if (pools !== null && pools.data.size === quic.streams.bufferSize && quic.streams.slots.length > 0) {
|
|
@@ -751,7 +762,7 @@ export class Http3Server {
|
|
|
751
762
|
if (this.listener.handleWindow(buf, at, len, address, now, answer) !== QUIC_LISTEN_ACCEPT) {
|
|
752
763
|
const reply: i32 = toI32(answer.reply.length)
|
|
753
764
|
if (reply > 0) {
|
|
754
|
-
this.send(answer.reply, H3_SERVER_ZERO, reply, address, H3_SERVER_ZERO)
|
|
765
|
+
this.send(answer.reply, H3_SERVER_ZERO, reply, address, H3_SERVER_ZERO, H3_SERVER_ZERO)
|
|
755
766
|
}
|
|
756
767
|
return
|
|
757
768
|
}
|
|
@@ -904,21 +915,40 @@ export class Http3Server {
|
|
|
904
915
|
}
|
|
905
916
|
}
|
|
906
917
|
|
|
907
|
-
/**
|
|
908
|
-
|
|
909
|
-
|
|
918
|
+
/**
|
|
919
|
+
* Sends `buf[at .. at + len)` to `to` with the ECN codepoint `ecn`, cut
|
|
920
|
+
* into `segment`-byte datagrams when it is above 0; answers whether it went.
|
|
921
|
+
*/
|
|
922
|
+
send(buf: u8[], at: i32, len: i32, to: u8[], segment: i32, ecn: i32): boolean {
|
|
923
|
+
const sent: i32 = udpSendTo(this.fd, buf, at, len, to, segment, ecn)
|
|
910
924
|
if (sent >= 0) {
|
|
911
925
|
return true
|
|
912
926
|
}
|
|
913
|
-
if (sent !==
|
|
927
|
+
if (sent !== H3_SERVER_UNSUPPORTED || (segment <= 0 && ecn === 0)) {
|
|
914
928
|
this.sendErrors = h3Count(this.sendErrors)
|
|
915
929
|
return false
|
|
916
930
|
}
|
|
917
|
-
//
|
|
931
|
+
// -95: no segmentation offload, or no ECN, or both. One datagram at a
|
|
932
|
+
// time, still marked: a -95 to a datagram with no segment is the mark's.
|
|
933
|
+
let mark: i32 = ecn
|
|
934
|
+
if (segment <= 0) {
|
|
935
|
+
this.unmark()
|
|
936
|
+
mark = H3_SERVER_ZERO
|
|
937
|
+
}
|
|
938
|
+
const step: i32 = segment > 0 ? segment : len
|
|
918
939
|
let p: i32 = at
|
|
919
940
|
while (p < at + len) {
|
|
920
|
-
const n: i32 = at + len - p <
|
|
921
|
-
|
|
941
|
+
const n: i32 = at + len - p < step ? at + len - p : step
|
|
942
|
+
let one: i32 = udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, mark)
|
|
943
|
+
if (one === H3_SERVER_UNSUPPORTED && mark !== 0) {
|
|
944
|
+
// No ECN here: this datagram and the rest go unmarked and no slot
|
|
945
|
+
// marks again. The packets just counted as marked fail their
|
|
946
|
+
// connection's validation.
|
|
947
|
+
this.unmark()
|
|
948
|
+
mark = H3_SERVER_ZERO
|
|
949
|
+
one = udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, mark)
|
|
950
|
+
}
|
|
951
|
+
if (one < 0) {
|
|
922
952
|
this.sendErrors = h3Count(this.sendErrors)
|
|
923
953
|
return false
|
|
924
954
|
}
|
|
@@ -927,6 +957,14 @@ export class Http3Server {
|
|
|
927
957
|
return true
|
|
928
958
|
}
|
|
929
959
|
|
|
960
|
+
/** The platform sends no ECN: no slot's connection marks from now on. */
|
|
961
|
+
unmark(): void {
|
|
962
|
+
this.ecn = false
|
|
963
|
+
for (let slot: i32 = 0; slot < toI32(this.quics.length); slot++) {
|
|
964
|
+
this.quics[slot].ecnMarking = false
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
930
968
|
/**
|
|
931
969
|
* Sends what every touched slot has, as far as its pacer lets it, closes a
|
|
932
970
|
* slot whose HTTP/3 side is done after GOAWAY, frees a slot whose
|
|
@@ -972,7 +1010,7 @@ export class Http3Server {
|
|
|
972
1010
|
break
|
|
973
1011
|
}
|
|
974
1012
|
const segment: i32 = this.flight.count > 1 ? this.flight.segment : H3_SERVER_ZERO
|
|
975
|
-
if (!this.send(this.tx, H3_SERVER_ZERO, n, to, segment)) {
|
|
1013
|
+
if (!this.send(this.tx, H3_SERVER_ZERO, n, to, segment, this.flight.ecn)) {
|
|
976
1014
|
break
|
|
977
1015
|
}
|
|
978
1016
|
retried = false
|
|
@@ -981,7 +1019,7 @@ export class Http3Server {
|
|
|
981
1019
|
// The CONNECTION_CLOSE goes whatever the pacer says: nothing follows it.
|
|
982
1020
|
const n: i32 = quic.takeDatagramInto(this.tx, H3_SERVER_ZERO, now)
|
|
983
1021
|
if (n > 0) {
|
|
984
|
-
this.send(this.tx, H3_SERVER_ZERO, n, to, H3_SERVER_ZERO)
|
|
1022
|
+
this.send(this.tx, H3_SERVER_ZERO, n, to, H3_SERVER_ZERO, quic.datagramEcn)
|
|
985
1023
|
}
|
|
986
1024
|
this.release(slot)
|
|
987
1025
|
return
|