@amritk/nish-x86_64-linux 0.17.0 → 0.20.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 (61) hide show
  1. package/INSTALL.md +43 -25
  2. package/bin/nish +0 -0
  3. package/package.json +2 -2
  4. package/runtime/nish.d.ts +55 -5
  5. package/runtime/nish.h +231 -20
  6. package/runtime/nish.mjs +16 -0
  7. package/runtime/runtime-aes.c +490 -0
  8. package/runtime/runtime-cross.c +366 -0
  9. package/runtime/runtime-host.c +7 -6
  10. package/runtime/runtime-net.c +93 -34
  11. package/runtime/runtime-os.c +213 -15
  12. package/runtime/runtime-parallel.c +590 -107
  13. package/runtime/runtime-simd.c +203 -0
  14. package/runtime/runtime-wasm.c +16 -3
  15. package/runtime/runtime.c +89 -23
  16. package/runtime/shim.mjs +71 -1
  17. package/scripts/build.sh +225 -20
  18. package/std/README.md +86 -55
  19. package/std/bytes.ts +163 -0
  20. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  21. package/std/crypto/aes.ts +238 -76
  22. package/std/crypto/chacha20poly1305.ts +43 -9
  23. package/std/crypto/ed25519.ts +840 -0
  24. package/std/crypto/hkdf.ts +304 -12
  25. package/std/crypto/hmac.ts +245 -53
  26. package/std/crypto/p256.ts +1376 -42
  27. package/std/crypto/x25519.ts +55 -37
  28. package/std/crypto/x509-random.ts +71 -0
  29. package/std/crypto/x509.ts +9 -9
  30. package/std/json-tokenizer.ts +1296 -0
  31. package/std/json-typed.ts +1014 -0
  32. package/std/json-value.ts +1163 -0
  33. package/std/json.ts +1412 -70
  34. package/std/net/buffer-pool.ts +151 -0
  35. package/std/net/hpack.ts +416 -112
  36. package/std/net/http-fields.ts +93 -0
  37. package/std/net/http1-server.ts +417 -55
  38. package/std/net/http1.ts +31 -6
  39. package/std/net/http2-tls.ts +112 -24
  40. package/std/net/http2.ts +333 -75
  41. package/std/net/http3-server.ts +231 -45
  42. package/std/net/http3.ts +187 -28
  43. package/std/net/prefork.ts +412 -0
  44. package/std/net/quic-conn-params.ts +86 -16
  45. package/std/net/quic-listener.ts +576 -219
  46. package/std/net/quic-packet.ts +443 -58
  47. package/std/net/quic-recovery.ts +361 -30
  48. package/std/net/quic-stream.ts +434 -28
  49. package/std/net/quic.ts +922 -206
  50. package/std/net/router.ts +676 -0
  51. package/std/net/tls/codec.ts +426 -27
  52. package/std/net/tls/record-server.ts +478 -132
  53. package/std/net/tls/record.ts +133 -42
  54. package/std/net/tls/schedule.ts +464 -55
  55. package/std/net/tls/ticket.ts +610 -0
  56. package/std/net/tls-tcp.ts +204 -57
  57. package/std/net/tls.ts +1132 -305
  58. package/std/net/url.ts +571 -0
  59. package/std/random.ts +94 -0
  60. package/std/text.ts +77 -0
  61. package/std/threads.ts +438 -14
package/std/net/http3.ts CHANGED
@@ -86,9 +86,11 @@
86
86
  * **No server push.** It never sends PUSH_PROMISE; a client's MAX_PUSH_ID is
87
87
  * allowed, checked and ignored.
88
88
  *
89
- * **Caps are start-up numbers**, all in `Http3Config`, and every buffer is
90
- * made by the constructor: the stream state for every QUIC stream slot, the
91
- * field section, the body chunk and the control frame. A field section's
89
+ * **Caps are start-up numbers**, all in `Http3Config`. The stream state for
90
+ * every QUIC stream slot and the control frame are made by the constructor;
91
+ * the body chunk and the field section are borrowed, around one read, from
92
+ * pools that a server's connections share (`share`) and that a standalone
93
+ * connection keeps one buffer each of. A field section's
92
94
  * names and values are copied into arrays the connection keeps, which grow to
93
95
  * the largest section seen and are then reused, so a warm connection
94
96
  * allocates nothing per request (`docs/security/http3.md`, H3-2). A peer's
@@ -121,6 +123,13 @@
121
123
  * Private helpers share the importing program's flat symbol namespace
122
124
  * (`docs/wp26-stdlib.md` §3e), which is why each one carries the module's name.
123
125
  *
126
+ * **The Date.** A response carries the `date` in `conn.date`, 29 bytes of
127
+ * IMF-fixdate, right after `:status`, unless the program's fields hold their
128
+ * own; trailers carry none. It counts against the peer's
129
+ * SETTINGS_MAX_FIELD_SECTION_SIZE. A bare connection has no clock, so `date`
130
+ * starts empty and nothing is sent; `Http3Server` fills it from its
131
+ * `HttpDate` (RFC 9110 §6.6.1).
132
+ *
124
133
  * Written from RFC 9114 and RFC 9204, not ported from another implementation.
125
134
  */
126
135
  import {
@@ -191,8 +200,9 @@ import {
191
200
  h3VarintLength,
192
201
  } from "nish/net/http3-frame"
193
202
  import { QUIC_STATE_CONNECTED, QuicConnection } from "nish/net/quic"
194
- import { QUIC_STREAM_END, QUIC_STREAM_ERR_RESET, QuicStream } from "nish/net/quic-stream"
203
+ import { QUIC_STREAM_END, QUIC_STREAM_ERR_RESET, QuicStream, quicStreamGrowFree } from "nish/net/quic-stream"
195
204
  import { quicVarintPut } from "nish/net/quic-packet"
205
+ import { BufferPool } from "nish/net/buffer-pool"
196
206
 
197
207
  /** The ALPN protocol identifier of HTTP/3 (RFC 9114 §3.1). */
198
208
  export const H3_ALPN: string = "h3"
@@ -344,6 +354,21 @@ const http3CheckCap = (what: string, value: i32, low: i32, high: i32): void => {
344
354
  }
345
355
  }
346
356
 
357
+ /** Whether one of `names` is `date` in any ASCII case (RFC 9110 §5.1). */
358
+ const http3NamesHoldDate = (names: u8[][]): boolean => {
359
+ // An ASCII letter's case is its 0x20 bit, and every octet of `date` is a letter.
360
+ const bit: i32 = 32
361
+ for (const name of names) {
362
+ if (toI32(name.length) === 4) {
363
+ const d: boolean = (toI32(name[0]) | bit) === 100 && (toI32(name[1]) | bit) === 97
364
+ if (d && (toI32(name[2]) | bit) === 116 && (toI32(name[3]) | bit) === 101) {
365
+ return true
366
+ }
367
+ }
368
+ }
369
+ return false
370
+ }
371
+
347
372
  /** Empties `bytes`, keeping its storage for the next fill. */
348
373
  const http3Empty = (bytes: u8[]): void => {
349
374
  while (toI32(bytes.length) > 0) {
@@ -381,10 +406,20 @@ export class Http3Connection {
381
406
  /** The client's SETTINGS. */
382
407
  peer: Http3Settings
383
408
  header: Http3FrameHeader
384
- /** H3_DATA's bytes, and where a payload being skipped is read to. */
409
+ /**
410
+ * H3_DATA's bytes, and where a payload being skipped is read to: a
411
+ * `bodyChunk`-byte buffer borrowed from `dataPool` for the read that needs
412
+ * it and held while an H3_DATA window is live, else `none`.
413
+ */
385
414
  data: u8[]
386
- /** A HEADERS payload read whole, for the decoder. */
415
+ /** A HEADERS payload read whole, for the decoder: a buffer borrowed from `sectionPool` around one read and decode, else `none`. */
387
416
  section: u8[]
417
+ /** The empty array `data` and `section` are while they hold nothing borrowed. */
418
+ none: u8[]
419
+ /** Where `data` is borrowed from: the connection's own pool until `share` hands it its server's. */
420
+ dataPool: BufferPool
421
+ /** Where `section` is borrowed from, likewise. */
422
+ sectionPool: BufferPool
388
423
  /** A control frame's payload, read whole. */
389
424
  control: u8[]
390
425
  /** Frame headers, SETTINGS and GOAWAY on their way out. */
@@ -400,6 +435,15 @@ export class Http3Connection {
400
435
  valueList: u8[][]
401
436
  statusName: u8[]
402
437
  statusValue: u8[]
438
+ /**
439
+ * The `date` every response carries (RFC 9110 §6.6.1), as the 29 bytes of
440
+ * an IMF-fixdate, or empty for none. A bare connection has no clock, so it
441
+ * starts empty; `Http3Server` points it at its `HttpDate`'s bytes, which
442
+ * that rewrites in place once a second.
443
+ */
444
+ date: u8[]
445
+ /** The bytes of `date`, the field's name, made once. */
446
+ dateName: u8[]
403
447
  /** The SETTINGS this side sends. */
404
448
  settingIds: i64[]
405
449
  settingValues: i64[]
@@ -460,6 +504,8 @@ export class Http3Connection {
460
504
  dataLength: i32 = 0
461
505
  /** How much of `control` is filled. */
462
506
  controlFill: i32 = 0
507
+ /** How far into the borrowed `data` reads have written: what its give-back zeroes. */
508
+ dataUsed: i32 = 0
463
509
  state: i32 = 0
464
510
  /** Requests open: taken and not yet finished both ways. */
465
511
  live: i32 = 0
@@ -523,8 +569,13 @@ export class Http3Connection {
523
569
  this.fields = new HttpFields()
524
570
  this.peer = new Http3Settings()
525
571
  this.header = new Http3FrameHeader()
526
- this.data = new Array<u8>(config.bodyChunk)
527
- this.section = new Array<u8>(config.maxFieldSectionSize)
572
+ this.none = []
573
+ this.data = this.none
574
+ this.section = this.none
575
+ this.dataPool = new BufferPool("Http3Connection.data", config.bodyChunk, 1)
576
+ this.sectionPool = new BufferPool("Http3Connection.section", config.maxFieldSectionSize, 1)
577
+ quicStreamGrowFree(this.dataPool.free, 1, this.none)
578
+ quicStreamGrowFree(this.sectionPool.free, 1, this.none)
528
579
  this.control = new Array<u8>(H3_CONTROL_FRAME_MAX)
529
580
  this.scratch = new Array<u8>(64)
530
581
  this.zeros = new Array<u8>(256)
@@ -539,6 +590,8 @@ export class Http3Connection {
539
590
  this.valueList = []
540
591
  this.statusName = httpFieldBytes(":status")
541
592
  this.statusValue = [48, 48, 48]
593
+ this.date = []
594
+ this.dateName = httpFieldBytes("date")
542
595
  this.settingIds = [
543
596
  H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY,
544
597
  H3_SETTINGS_QPACK_BLOCKED_STREAMS,
@@ -580,9 +633,11 @@ export class Http3Connection {
580
633
  /**
581
634
  * Puts the connection back as the constructor left it, for the QUIC
582
635
  * connection's next peer (call it after `quic.reset`): no stream known,
583
- * QPACK's state and the client's SETTINGS forgotten. It allocates nothing.
636
+ * QPACK's state and the client's SETTINGS forgotten, and any buffer
637
+ * still borrowed given back. It allocates nothing.
584
638
  */
585
639
  restart(): void {
640
+ this.giveBack()
586
641
  this.slotId.fill(H3_NONE)
587
642
  this.flags.fill(H3_ZERO)
588
643
  this.headLen.fill(H3_ZERO)
@@ -713,6 +768,89 @@ export class Http3Connection {
713
768
  * connection takes, until it answers H3_NEED_MORE or H3_ERROR.
714
769
  */
715
770
  next(): i32 {
771
+ // The last H3_DATA window ends here. A body read chunk by chunk keeps the
772
+ // one buffer; any other event, H3_NEED_MORE among them, gives it back.
773
+ const event: i32 = this.nextEvent()
774
+ if (event !== H3_DATA) {
775
+ this.giveData()
776
+ }
777
+ return event
778
+ }
779
+
780
+ /**
781
+ * Lends this connection its server's pools in place of its own, giving
782
+ * back to the old pools whatever it holds. Panics unless their buffers are
783
+ * `bodyChunk` and `maxFieldSectionSize` bytes long, as this connection's
784
+ * reads need.
785
+ */
786
+ share(data: BufferPool, section: BufferPool): void {
787
+ if (data.size !== this.config.bodyChunk || section.size !== this.config.maxFieldSectionSize) {
788
+ panic(
789
+ `Http3Connection: pools of ${data.size} and ${section.size} bytes shared with a connection of bodyChunk ${this.config.bodyChunk} and maxFieldSectionSize ${this.config.maxFieldSectionSize}`
790
+ )
791
+ }
792
+ this.giveBack()
793
+ this.dataPool = data
794
+ this.sectionPool = section
795
+ }
796
+
797
+ /** Releases the QUIC connection (`QuicConnection.release`) and gives back every pooled buffer this connection holds. */
798
+ release(): void {
799
+ this.quic.release()
800
+ this.giveBack()
801
+ }
802
+
803
+ /** Gives back `data` and `section` if either is borrowed: the connection holds no pooled buffer after it. */
804
+ giveBack(): void {
805
+ this.giveData()
806
+ this.giveSection(H3_ZERO)
807
+ }
808
+
809
+ /** `data`, borrowed from `dataPool` first if it is not already. */
810
+ borrowData(): u8[] {
811
+ if (this.data.length === 0) {
812
+ this.data = this.dataPool.take()
813
+ this.dataUsed = 0
814
+ }
815
+ return this.data
816
+ }
817
+
818
+ /** Gives `data` back to `dataPool`, zeroed as far as reads wrote, if it is borrowed. */
819
+ giveData(): void {
820
+ if (this.data.length > 0) {
821
+ this.dataPool.give(this.data, this.dataUsed)
822
+ this.data = this.none
823
+ this.dataUsed = 0
824
+ }
825
+ }
826
+
827
+ /** Reads up to `want` bytes of stream `id` into the start of `data`, borrowed for it: `streamRead`'s answer. */
828
+ readData(id: i64, want: i32): i32 {
829
+ const n: i32 = this.quic.streamRead(id, this.borrowData(), H3_ZERO, want)
830
+ if (n > this.dataUsed) {
831
+ this.dataUsed = n
832
+ }
833
+ return n
834
+ }
835
+
836
+ /** `section`, borrowed from `sectionPool`. */
837
+ borrowSection(): u8[] {
838
+ if (this.section.length === 0) {
839
+ this.section = this.sectionPool.take()
840
+ }
841
+ return this.section
842
+ }
843
+
844
+ /** Gives `section` back to `sectionPool`, zeroed over `[0, used)`, if it is borrowed. */
845
+ giveSection(used: i32): void {
846
+ if (this.section.length > 0) {
847
+ this.sectionPool.give(this.section, used)
848
+ this.section = this.none
849
+ }
850
+ }
851
+
852
+ /** `next`'s work: everything but the lending of `data` around it. */
853
+ nextEvent(): i32 {
716
854
  if (this.state === H3_STATE_FAILED) {
717
855
  return H3_ERROR
718
856
  }
@@ -924,7 +1062,7 @@ export class Http3Connection {
924
1062
  } else if (phase === H3_PHASE_CONTROL) {
925
1063
  return this.controlStep(k, id)
926
1064
  } else if (phase === H3_PHASE_ENCODER || phase === H3_PHASE_DECODER) {
927
- const n: i32 = this.quic.streamRead(id, this.data, H3_ZERO, toI32(this.data.length))
1065
+ const n: i32 = this.readData(id, this.config.bodyChunk)
928
1066
  if (n === 0) {
929
1067
  return H3_NEED_MORE
930
1068
  }
@@ -1027,10 +1165,12 @@ export class Http3Connection {
1027
1165
  this.controlFill = 0
1028
1166
  }
1029
1167
  if (this.frameLeft[k] > 0) {
1030
- const into: u8[] = this.frameType[k] === H3_SKIP_TYPE ? this.data : this.control
1031
- const at: i32 = this.frameType[k] === H3_SKIP_TYPE ? H3_ZERO : this.controlFill
1032
- const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(toI32(into.length) - at)))
1033
- const n: i32 = this.quic.streamRead(id, into, at, want)
1168
+ const skip: boolean = this.frameType[k] === H3_SKIP_TYPE
1169
+ const room: i32 = skip ? this.config.bodyChunk : toI32(this.control.length) - this.controlFill
1170
+ const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(room)))
1171
+ const n: i32 = skip
1172
+ ? this.readData(id, want)
1173
+ : this.quic.streamRead(id, this.control, this.controlFill, want)
1034
1174
  if (n === 0) {
1035
1175
  return H3_NEED_MORE
1036
1176
  }
@@ -1118,9 +1258,9 @@ export class Http3Connection {
1118
1258
 
1119
1259
  /** Reads stream `id` into `data` until nothing more is there; answers 0, or the `QUIC_STREAM_*` that ended it. */
1120
1260
  drop(id: i64): i32 {
1121
- let n: i32 = this.quic.streamRead(id, this.data, H3_ZERO, toI32(this.data.length))
1261
+ let n: i32 = this.readData(id, this.config.bodyChunk)
1122
1262
  while (n > 0) {
1123
- n = this.quic.streamRead(id, this.data, H3_ZERO, toI32(this.data.length))
1263
+ n = this.readData(id, this.config.bodyChunk)
1124
1264
  }
1125
1265
  return n
1126
1266
  }
@@ -1189,7 +1329,7 @@ export class Http3Connection {
1189
1329
  if (phase === H3_PHASE_WT) {
1190
1330
  if ((this.flags[k] & H3_FLAG_RECV_DONE) === 0) {
1191
1331
  const resetCode: i64 = stream.resetCode
1192
- const n: i32 = this.quic.streamRead(id, this.data, H3_ZERO, toI32(this.data.length))
1332
+ const n: i32 = this.readData(id, this.config.bodyChunk)
1193
1333
  if (n > 0) {
1194
1334
  this.stream = id
1195
1335
  this.dataStart = 0
@@ -1421,24 +1561,28 @@ export class Http3Connection {
1421
1561
  return this.writable(k, id)
1422
1562
  }
1423
1563
  const length: i32 = toI32(this.frameLeft[k])
1424
- const n: i32 = this.quic.streamRead(id, this.section, H3_ZERO, length)
1564
+ const n: i32 = this.quic.streamRead(id, this.borrowSection(), H3_ZERO, length)
1425
1565
  if (n < 0) {
1566
+ this.giveSection(H3_ZERO)
1426
1567
  return this.ended(k, id, n, stream.resetCode)
1427
1568
  }
1428
1569
  if (n !== length) {
1570
+ this.giveSection(n)
1429
1571
  return this.writable(k, id)
1430
1572
  }
1431
1573
  this.frameLeft[k] = -1
1574
+ // `fieldSection` copies what it keeps into `fields`: the section is not read after it.
1432
1575
  const event: i32 = this.fieldSection(k, id, length)
1576
+ this.giveSection(length)
1433
1577
  if (event !== H3_NEED_MORE) {
1434
1578
  return event
1435
1579
  }
1436
1580
  continue
1437
1581
  }
1438
1582
  // DATA, a payload being skipped, or an unknown frame's.
1439
- const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(toI32(this.data.length))))
1583
+ const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(this.config.bodyChunk)))
1440
1584
  const resetCode: i64 = stream.resetCode
1441
- const n: i32 = this.quic.streamRead(id, this.data, H3_ZERO, want)
1585
+ const n: i32 = this.readData(id, want)
1442
1586
  if (n === 0) {
1443
1587
  return this.writable(k, id)
1444
1588
  }
@@ -1711,13 +1855,19 @@ export class Http3Connection {
1711
1855
  /**
1712
1856
  * A request whose field section is past `maxFieldSectionSize`: answered
1713
1857
  * with a 431 and its stream asked to stop with H3_NO_ERROR (§4.2.2,
1714
- * §4.1.2). The program never sees it.
1858
+ * §4.1.2). The program never sees it. A peer whose own
1859
+ * SETTINGS_MAX_FIELD_SECTION_SIZE has room for a bare 431 but not for one
1860
+ * with the date gets it without the date rather than a reset.
1715
1861
  */
1716
1862
  answerTooLarge(k: i32, id: i64): i32 {
1717
1863
  this.tooLarge = h3Count(this.tooLarge)
1718
1864
  const empty: u8[][] = this.nameList
1719
1865
  http3EmptyList(empty)
1720
- if (this.writeHead(k, id, H3_STATUS_TOO_LARGE, empty, empty, true) !== 0) {
1866
+ let r: i32 = this.writeHead(k, id, H3_STATUS_TOO_LARGE, empty, empty, true, true)
1867
+ if (r === H3_TOO_LARGE) {
1868
+ r = this.writeHead(k, id, H3_STATUS_TOO_LARGE, empty, empty, true, false)
1869
+ }
1870
+ if (r !== 0) {
1721
1871
  this.abandon(k, id, H3_EXCESSIVE_LOAD)
1722
1872
  }
1723
1873
  this.quic.streamStopSending(id, H3_NO_ERROR)
@@ -1833,11 +1983,14 @@ export class Http3Connection {
1833
1983
  }
1834
1984
 
1835
1985
  /**
1836
- * Encodes `:status` (unless it is 0, for trailers) and `names`/`values` into a HEADERS
1837
- * frame and writes it whole to stream `id`, with the FIN when `fin`.
1838
- * Answers 0, H3_AGAIN, or H3_TOO_LARGE.
1986
+ * Encodes `:status` (unless it is 0, for trailers), the server's `date`
1987
+ * for a final status when `dated` unless `names` holds one, and
1988
+ * `names`/`values` into a HEADERS frame and writes it whole to stream `id`,
1989
+ * with the FIN when `fin`. The date counts against the peer's
1990
+ * SETTINGS_MAX_FIELD_SECTION_SIZE as any field does. Answers 0, H3_AGAIN,
1991
+ * or H3_TOO_LARGE.
1839
1992
  */
1840
- writeHead(k: i32, id: i64, status: i32, names: u8[][], values: u8[][], fin: boolean): i32 {
1993
+ writeHead(k: i32, id: i64, status: i32, names: u8[][], values: u8[][], fin: boolean, dated: boolean): i32 {
1841
1994
  const out: u8[] = this.encoded
1842
1995
  http3Empty(out)
1843
1996
  this.encoder.beginSection(out)
@@ -1862,6 +2015,12 @@ export class Http3Connection {
1862
2015
  )
1863
2016
  size = toI64(nameLength + valueLength + QPACK_FIELD_OVERHEAD)
1864
2017
  }
2018
+ if (dated && status >= 200 && toI32(this.date.length) > 0 && !http3NamesHoldDate(names)) {
2019
+ const nl: i32 = toI32(this.dateName.length)
2020
+ const vl: i32 = toI32(this.date.length)
2021
+ this.encoder.encodeField(out, this.dateName, H3_ZERO, nl, this.date, H3_ZERO, vl, false, true)
2022
+ size = size + toI64(nl + vl + QPACK_FIELD_OVERHEAD)
2023
+ }
1865
2024
  const n: i32 = toI32(names.length)
1866
2025
  for (let j: i32 = 0; j < n && j < toI32(names.length) && j < toI32(values.length); j++) {
1867
2026
  const name: u8[] = names[j]
@@ -1922,7 +2081,7 @@ export class Http3Connection {
1922
2081
  ) {
1923
2082
  return H3_INVALID
1924
2083
  }
1925
- return this.writeHead(k, id, status, names, values, endStream)
2084
+ return this.writeHead(k, id, status, names, values, endStream, true)
1926
2085
  }
1927
2086
 
1928
2087
  /**
@@ -1942,7 +2101,7 @@ export class Http3Connection {
1942
2101
  ) {
1943
2102
  return H3_INVALID
1944
2103
  }
1945
- return this.writeHead(k, id, H3_ZERO, names, values, true)
2104
+ return this.writeHead(k, id, H3_ZERO, names, values, true, false)
1946
2105
  }
1947
2106
 
1948
2107
  /**