@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.
- package/INSTALL.md +43 -34
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +117 -8
- package/runtime/nish.h +30 -7
- package/runtime/nish.mjs +71 -7
- package/runtime/runtime-host.c +43 -24
- package/runtime/runtime-net.c +57 -10
- package/runtime/runtime-os.c +62 -20
- package/runtime/runtime-wasm.c +1 -0
- package/runtime/runtime.c +46 -8
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +1 -3
- package/std/README.md +166 -35
- package/std/crypto/aes.ts +51 -8
- package/std/crypto/base64url.ts +25 -12
- package/std/crypto/chacha20poly1305.ts +43 -29
- package/std/crypto/ct.ts +6 -1
- package/std/crypto/hkdf.ts +364 -3
- package/std/crypto/hmac.ts +307 -7
- package/std/crypto/p256.ts +148 -27
- package/std/crypto/sha1.ts +147 -0
- package/std/crypto/sha256.ts +3 -2
- package/std/crypto/x25519.ts +88 -36
- package/std/crypto/x509.ts +130 -47
- package/std/net/hpack.ts +1094 -0
- package/std/net/http-fields.ts +604 -0
- package/std/net/http1-server.ts +1389 -0
- package/std/net/http1.ts +1492 -0
- package/std/net/http2-frame.ts +745 -0
- package/std/net/http2-tls.ts +216 -0
- package/std/net/http2.ts +1433 -0
- package/std/net/http3-frame.ts +457 -0
- package/std/net/http3-server.ts +905 -0
- package/std/net/http3.ts +2145 -0
- package/std/net/qpack.ts +1163 -0
- package/std/net/quic-conn-ack.ts +206 -0
- package/std/net/quic-conn-cid.ts +425 -0
- package/std/net/quic-conn-params.ts +392 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +653 -0
- package/std/net/quic-packet.ts +1474 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1603 -0
- package/std/net/quic.ts +3214 -0
- package/std/net/tls/codec.ts +1089 -0
- package/std/net/tls/record-server.ts +1173 -0
- package/std/net/tls/record.ts +621 -0
- package/std/net/tls/schedule.ts +429 -0
- package/std/net/tls-tcp.ts +410 -0
- package/std/net/tls.ts +1368 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- 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
|
+
}
|