@amritk/nish-x86_64-linux 0.16.0 → 0.17.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 (55) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +30 -7
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-wasm.c +1 -0
  11. package/runtime/runtime.c +46 -8
  12. package/runtime/shim.mjs +114 -5
  13. package/scripts/build.sh +1 -3
  14. package/std/README.md +166 -35
  15. package/std/crypto/aes.ts +51 -8
  16. package/std/crypto/base64url.ts +25 -12
  17. package/std/crypto/chacha20poly1305.ts +43 -29
  18. package/std/crypto/ct.ts +6 -1
  19. package/std/crypto/hkdf.ts +364 -3
  20. package/std/crypto/hmac.ts +307 -7
  21. package/std/crypto/p256.ts +148 -27
  22. package/std/crypto/sha1.ts +147 -0
  23. package/std/crypto/sha256.ts +3 -2
  24. package/std/crypto/x25519.ts +88 -36
  25. package/std/crypto/x509.ts +130 -47
  26. package/std/net/hpack.ts +1094 -0
  27. package/std/net/http-fields.ts +604 -0
  28. package/std/net/http1-server.ts +1389 -0
  29. package/std/net/http1.ts +1492 -0
  30. package/std/net/http2-frame.ts +745 -0
  31. package/std/net/http2-tls.ts +216 -0
  32. package/std/net/http2.ts +1433 -0
  33. package/std/net/http3-frame.ts +457 -0
  34. package/std/net/http3-server.ts +905 -0
  35. package/std/net/http3.ts +2145 -0
  36. package/std/net/qpack.ts +1163 -0
  37. package/std/net/quic-conn-ack.ts +206 -0
  38. package/std/net/quic-conn-cid.ts +425 -0
  39. package/std/net/quic-conn-params.ts +392 -0
  40. package/std/net/quic-datagram.ts +130 -0
  41. package/std/net/quic-frame.ts +1206 -0
  42. package/std/net/quic-listener.ts +653 -0
  43. package/std/net/quic-packet.ts +1474 -0
  44. package/std/net/quic-recovery.ts +849 -0
  45. package/std/net/quic-stream.ts +1603 -0
  46. package/std/net/quic.ts +3214 -0
  47. package/std/net/tls/codec.ts +1089 -0
  48. package/std/net/tls/record-server.ts +1173 -0
  49. package/std/net/tls/record.ts +621 -0
  50. package/std/net/tls/schedule.ts +429 -0
  51. package/std/net/tls-tcp.ts +410 -0
  52. package/std/net/tls.ts +1368 -0
  53. package/std/net/websocket.ts +823 -0
  54. package/std/net/webtransport.ts +1334 -0
  55. package/std/secret.ts +62 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * `nish/net/http2-tls` — HTTP/2 over TLS 1.3 on TCP: a pool of
3
+ * `Http2Connection`s, one beside each slot of a `TlsTcpServer`, for a
4
+ * program that owns its readiness loop.
5
+ *
6
+ * const listener = tcpListen("0.0.0.0", 8443, 128);
7
+ * const server = new Http2TlsServer(tlsConfig, new Http2Config(), listener, 64);
8
+ * // tlsConfig.alpn holds "h2"
9
+ * // when the listener is readable:
10
+ * const slot: i32 = server.accept(serverRandom, ephemeralKey);
11
+ * if (slot >= 0) { pollAdd(loop, server.fd(slot), 1, slot); }
12
+ * // when a connection's token comes back:
13
+ * let wants: i32 = (events & 5) !== 0 ? server.readable(slot) : server.writable(slot);
14
+ * if ((wants & TLS_RECORD_SIGN) !== 0) { server.signP256(slot, leafKey); }
15
+ * let event: i32 = server.next(slot);
16
+ * while (event !== H2_NEED_MORE && event !== H2_ERROR) {
17
+ * const conn: Http2Connection = server.connection(slot);
18
+ * // H2_REQUEST: conn.respond(conn.stream, 200, names, values, false) ...
19
+ * event = server.next(slot);
20
+ * }
21
+ * wants = server.flush(slot);
22
+ * if ((wants & TLS_RECORD_DONE) !== 0) { server.close(slot); }
23
+ * else { pollModify(loop, server.fd(slot), wants & 3, slot); }
24
+ *
25
+ * It is `nish/net/tls-tcp`'s loop with one step added: `next` moves what TLS
26
+ * has decrypted into the slot's connection and answers the connection's next
27
+ * event, and `flush` moves what the connection wrote into TLS and on to the
28
+ * socket. Every call answers the TLS slot's interest, whose low two bits are
29
+ * the events `pollModify` takes. A connection whose input buffer is full
30
+ * stops reading TLS, TLS then stops asking for the socket to be read, and the
31
+ * peer's TCP window closes: back-pressure goes all the way down.
32
+ *
33
+ * **ALPN.** RFC 9113 §3.2 runs HTTP/2 over TLS only when ALPN chose `h2`. A
34
+ * slot whose handshake ends with any other protocol, or none, is shut down
35
+ * with TLS's `close_notify` and never reaches its connection; the program's
36
+ * configuration offers `h2` (and may offer `http/1.1` beside it, for a
37
+ * program that serves both and calls `alpn(slot)` itself).
38
+ *
39
+ * **Slots, sized at start-up.** The constructor makes every connection and
40
+ * the TLS pool beside them; `accept` resets the slot's connection, and
41
+ * nothing a connection does after that allocates but a header block's HPACK
42
+ * decoding (`docs/security/http2.md`, H2-1). The caps are the
43
+ * `Http2Config`'s, one for every slot, so a program sets them once.
44
+ *
45
+ * Written from RFC 9113 §3 and §9.2, not ported from another implementation.
46
+ */
47
+ import { Secret } from "nish:secret"
48
+ import { H2_ERROR, H2_NEED_MORE, Http2Config, Http2Connection } from "nish/net/http2"
49
+ import { TlsServerConfig } from "nish/net/tls"
50
+ import { TLS_RECORD_DONE, TLS_RECORD_STATE_OPEN, TLS_RECORD_WANT_WRITE } from "nish/net/tls/record-server"
51
+ import { TlsTcpServer } from "nish/net/tls-tcp"
52
+
53
+ /** A typed zero: a bare literal is an `f64` under `--number-mode f64`. */
54
+ const H2_TLS_ZERO: i32 = 0
55
+
56
+ /** The ALPN protocol identifier of HTTP/2 over TLS (RFC 9113 §3.2). */
57
+ export const H2_ALPN: string = "h2"
58
+
59
+ /**
60
+ * A pool of HTTP/2 connections over TLS on one listening socket. `slot`s are
61
+ * `TlsTcpServer`'s, and `connection(slot)` is the HTTP/2 side of each.
62
+ */
63
+ export class Http2TlsServer {
64
+ tls: TlsTcpServer
65
+ connections: Http2Connection[]
66
+
67
+ /**
68
+ * A pool of `size` slots, at least one, serving HTTP/2 under `config` over
69
+ * TLS under `tlsConfig` on `listener`. Every slot's buffers are allocated
70
+ * here.
71
+ */
72
+ constructor(tlsConfig: TlsServerConfig, config: Http2Config, listener: i32, size: i32) {
73
+ this.tls = new TlsTcpServer(tlsConfig, listener, size)
74
+ this.connections = []
75
+ for (let k: i32 = 0; k < this.tls.size(); k++) {
76
+ this.connections.push(new Http2Connection(config))
77
+ }
78
+ }
79
+
80
+ /** How many slots the pool has. */
81
+ size(): i32 {
82
+ return this.tls.size()
83
+ }
84
+
85
+ /** How many slots hold a connection. */
86
+ busy(): i32 {
87
+ return this.tls.busy()
88
+ }
89
+
90
+ /** Whether `slot` holds a connection. */
91
+ holds(slot: i32): boolean {
92
+ return this.tls.holds(slot)
93
+ }
94
+
95
+ /** The descriptor of `slot`, for `pollAdd`; -1 for a free one. */
96
+ fd(slot: i32): i32 {
97
+ return this.tls.fd(slot)
98
+ }
99
+
100
+ /** The HTTP/2 connection of `slot`; a slot out of range names the first. */
101
+ connection(slot: i32): Http2Connection {
102
+ const at: i32 = slot >= 0 && slot < toI32(this.connections.length) ? slot : H2_TLS_ZERO
103
+ return this.connections[at]
104
+ }
105
+
106
+ /** The ALPN protocol `slot`'s handshake chose, or empty. */
107
+ alpn(slot: i32): string {
108
+ return this.tls.connection(slot).tls.alpn
109
+ }
110
+
111
+ /**
112
+ * Accepts the next waiting connection into a free slot, as
113
+ * `TlsTcpServer.accept` does, and gives it a fresh HTTP/2 connection whose
114
+ * SETTINGS go out once the handshake is done. Answers the slot, or what
115
+ * `TlsTcpServer.accept` answers.
116
+ */
117
+ accept(serverRandom: u8[], ephemeralPrivate: u8[]): i32 {
118
+ const slot: i32 = this.tls.accept(serverRandom, ephemeralPrivate)
119
+ if (slot >= 0 && slot < toI32(this.connections.length)) {
120
+ this.connections[slot].restart()
121
+ }
122
+ return slot
123
+ }
124
+
125
+ /** The socket of `slot` is readable: TLS reads it, and what HTTP/2 has to send goes after. Answers the interest. */
126
+ readable(slot: i32): i32 {
127
+ this.tls.readable(slot)
128
+ return this.flush(slot)
129
+ }
130
+
131
+ /** The socket of `slot` is writable. Answers the interest. */
132
+ writable(slot: i32): i32 {
133
+ this.tls.writable(slot)
134
+ return this.flush(slot)
135
+ }
136
+
137
+ /** Signs `slot`'s CertificateVerify with `key`, as `TlsTcpServer.signP256` does. Answers the interest. */
138
+ signP256(slot: i32, key: Secret<u8[]>): i32 {
139
+ this.tls.signP256(slot, key)
140
+ return this.flush(slot)
141
+ }
142
+
143
+ /** Whether `slot`'s handshake is done and chose anything but `h2`. */
144
+ refused(slot: i32): boolean {
145
+ return this.tls.connection(slot).state === TLS_RECORD_STATE_OPEN && this.alpn(slot) !== H2_ALPN
146
+ }
147
+
148
+ /**
149
+ * Moves what TLS has decrypted for `slot` into its connection, as far as
150
+ * the connection has room, and answers the connection's next event. A free
151
+ * slot, or one whose ALPN was not `h2`, answers `H2_ERROR`.
152
+ */
153
+ next(slot: i32): i32 {
154
+ if (!this.holds(slot) || this.refused(slot)) {
155
+ return H2_ERROR
156
+ }
157
+ const conn: Http2Connection = this.connections[slot]
158
+ // TLS decrypts straight into the connection's input, so a byte is
159
+ // copied once on its way in.
160
+ while (!conn.inputEnded) {
161
+ const room: i32 = conn.inputTail()
162
+ if (room <= 0) {
163
+ break
164
+ }
165
+ const n: i32 = this.tls.read(slot, conn.input, conn.inputEnd, room)
166
+ if (n === 0) {
167
+ conn.endInput()
168
+ }
169
+ if (n <= 0) {
170
+ break
171
+ }
172
+ conn.received(n)
173
+ }
174
+ const event: i32 = conn.next()
175
+ // What the program answers to one event and the next shares a record:
176
+ // the connection's output goes to TLS only once it stalls or ends, and
177
+ // the program's own `flush` after the loop.
178
+ if (event === H2_NEED_MORE || event === H2_ERROR) {
179
+ this.flush(slot)
180
+ }
181
+ return event
182
+ }
183
+
184
+ /**
185
+ * Writes what `slot`'s connection holds into TLS, as far as TLS takes it,
186
+ * and sends it; shuts TLS down once the connection is over, the peer's
187
+ * stream has ended, or ALPN chose another protocol. Answers the interest,
188
+ * with the write bit while the connection still holds bytes.
189
+ */
190
+ flush(slot: i32): i32 {
191
+ if (!this.holds(slot)) {
192
+ return TLS_RECORD_DONE
193
+ }
194
+ if (this.refused(slot)) {
195
+ return this.tls.shutdown(slot)
196
+ }
197
+ const conn: Http2Connection = this.connections[slot]
198
+ while (conn.wantsWrite()) {
199
+ const n: i32 = this.tls.write(slot, conn.output, conn.outputStart, conn.outputEnd - conn.outputStart)
200
+ if (n <= 0) {
201
+ break
202
+ }
203
+ conn.consume(n)
204
+ }
205
+ if (conn.isDone()) {
206
+ return this.tls.shutdown(slot)
207
+ }
208
+ const wants: i32 = this.tls.interest(slot)
209
+ return conn.wantsWrite() && (wants & TLS_RECORD_DONE) === 0 ? wants | TLS_RECORD_WANT_WRITE : wants
210
+ }
211
+
212
+ /** Ends `slot`'s connection and frees the slot at once, as `TlsTcpServer.close` does. */
213
+ close(slot: i32): void {
214
+ this.tls.close(slot)
215
+ }
216
+ }