@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.
Files changed (70) hide show
  1. package/INSTALL.md +22 -10
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +2 -1
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +44 -0
  22. package/runtime/nish.h +75 -2
  23. package/runtime/nish.mjs +23 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-bytes.c +401 -0
  26. package/runtime/runtime-cross.c +1 -1
  27. package/runtime/runtime-host.c +1 -1
  28. package/runtime/runtime-os.c +1 -1
  29. package/runtime/runtime-parallel.c +1 -1
  30. package/runtime/runtime-simd.c +231 -7
  31. package/runtime/runtime-term.c +504 -0
  32. package/runtime/runtime-wasm.c +0 -11
  33. package/runtime/runtime.c +190 -6
  34. package/runtime/shim.mjs +6 -985
  35. package/runtime/sqlite/LICENSE.md +85 -0
  36. package/runtime/sqlite/README.md +45 -0
  37. package/runtime/sqlite/nish-sqlite.c +359 -0
  38. package/runtime/sqlite/sqlite3.c +271131 -0
  39. package/runtime/sqlite/sqlite3.h +14447 -0
  40. package/scripts/build.sh +261 -53
  41. package/std/LICENSE-fast-float +23 -0
  42. package/std/LICENSE-fdlibm +8 -0
  43. package/std/README.md +31 -11
  44. package/std/base64.ts +116 -0
  45. package/std/bytes.ts +256 -0
  46. package/std/cli.ts +328 -0
  47. package/std/collections.ts +6 -5
  48. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  49. package/std/crypto/base64url.ts +125 -0
  50. package/std/crypto/bcrypt.ts +760 -0
  51. package/std/crypto/hmac.ts +138 -2
  52. package/std/crypto/x509.ts +11 -122
  53. package/std/json-tokenizer.ts +2998 -57
  54. package/std/json-value.ts +22 -3
  55. package/std/json.ts +83 -1
  56. package/std/math.ts +608 -0
  57. package/std/net/http1-server.ts +85 -8
  58. package/std/net/http3-server.ts +50 -12
  59. package/std/net/postgres.ts +1875 -0
  60. package/std/net/quic-listener.ts +20 -4
  61. package/std/regex.ts +4274 -0
  62. package/std/sqlite.ts +391 -0
  63. package/std/term-io.ts +978 -0
  64. package/std/term-pixels.ts +874 -0
  65. package/std/term-probe.ts +459 -0
  66. package/std/term-rate.ts +993 -0
  67. package/std/term.ts +2453 -0
  68. package/std/text.ts +90 -7
  69. package/std/yaml-lines.ts +141 -0
  70. package/std/yaml.ts +4675 -0
@@ -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 and the idle timeout; the
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
- conn.lastActive = monotonicNanos()
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
- /** Closes every slot that has been idle past `idleTimeout` at `now` (`monotonicNanos`); answers how many. */
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].idle(now)) {
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
- if (this.live(slot)) {
1637
- this.connections[slot].lastActive = monotonicNanos()
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
- /** Closes every slot that has been idle past `idleTimeout` at `now` (`monotonicNanos`); answers how many. */
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].idle(now)) {
1821
+ if (this.tls.holds(k) && this.connections[k].expired(now)) {
1745
1822
  this.tls.close(k)
1746
1823
  n = n + 1
1747
1824
  }
@@ -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 H3_SERVER_NO_GSO: i32 = -95
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
- /** Sends `buf[at .. at + len)` to `to`, cut into `segment`-byte datagrams when it is above 0; answers whether it went. */
908
- send(buf: u8[], at: i32, len: i32, to: u8[], segment: i32): boolean {
909
- const sent: i32 = udpSendTo(this.fd, buf, at, len, to, segment, H3_SERVER_ZERO)
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 !== H3_SERVER_NO_GSO || segment <= 0) {
927
+ if (sent !== H3_SERVER_UNSUPPORTED || (segment <= 0 && ecn === 0)) {
914
928
  this.sendErrors = h3Count(this.sendErrors)
915
929
  return false
916
930
  }
917
- // No segmentation offload here: one datagram at a time.
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 < segment ? at + len - p : segment
921
- if (udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, H3_SERVER_ZERO) < 0) {
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