@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 +50 -13
- 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-simd.c +203 -0
- package/runtime/runtime-wasm.c +12 -0
- package/runtime/runtime.c +66 -18
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +14 -12
- package/std/README.md +168 -36
- 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 +618 -34
- 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/json.ts +297 -41
- 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 +911 -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 +462 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +929 -0
- package/std/net/quic-packet.ts +1698 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1609 -0
- package/std/net/quic.ts +3578 -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 +414 -0
- package/std/net/tls.ts +1437 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- package/std/secret.ts +62 -0
- package/std/text.ts +77 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nish/net/http3-frame` — the frames and stream types of HTTP/3 (RFC 9114
|
|
3
|
+
* §6.2, §7): frame headers, SETTINGS, the one-identifier frames (GOAWAY,
|
|
4
|
+
* MAX_PUSH_ID, CANCEL_PUSH), and which types are HTTP/2's reserved ones or
|
|
5
|
+
* unknown. Sans-IO: every reader takes a byte window `(buf, off, len)` and
|
|
6
|
+
* reads it in place, and every writer writes into the caller's buffer at an
|
|
7
|
+
* offset, before an end, and answers the offset past what it wrote, or -1
|
|
8
|
+
* with nothing written. Nothing here allocates.
|
|
9
|
+
*
|
|
10
|
+
* import { Http3FrameHeader, h3ReadFrameHeader, h3PutFrameHeader, H3_FRAME_DATA } from "nish/net/http3-frame";
|
|
11
|
+
*
|
|
12
|
+
* const h = new Http3FrameHeader();
|
|
13
|
+
* const size: i32 = h3ReadFrameHeader(h, buf, off, len); // 0: the window ends inside the header
|
|
14
|
+
* // h.type, h.length; the payload is the next h.length bytes of the stream
|
|
15
|
+
* const at: i32 = h3PutFrameHeader(out, 0, toI32(out.length), H3_FRAME_DATA, toI64(n));
|
|
16
|
+
*
|
|
17
|
+
* **Frames** (§7.1) are a type and a length, each a QUIC variable-length
|
|
18
|
+
* integer, and then that many bytes of payload. On a QUIC stream a frame may
|
|
19
|
+
* arrive in any number of pieces, so a header is read only once both of its
|
|
20
|
+
* integers are in the window (`h3ReadFrameHeader` answers 0 until then) and a
|
|
21
|
+
* payload is the caller's to count down. The varints are
|
|
22
|
+
* `nish/net/quic-packet`'s, which QUIC and HTTP/3 share.
|
|
23
|
+
*
|
|
24
|
+
* **Types.** DATA, HEADERS, CANCEL_PUSH, SETTINGS, PUSH_PROMISE, GOAWAY and
|
|
25
|
+
* MAX_PUSH_ID are the seven RFC 9114 defines. The types HTTP/2 used with no
|
|
26
|
+
* HTTP/3 counterpart (0x02 PRIORITY, 0x06 PING, 0x08 WINDOW_UPDATE, 0x09
|
|
27
|
+
* CONTINUATION) are reserved, and receiving one is H3_FRAME_UNEXPECTED
|
|
28
|
+
* (§7.2.8): `h3ReservedHttp2Frame` names them. Every other type is unknown,
|
|
29
|
+
* the reserved `0x1f * N + 0x21` types that exercise that rule included
|
|
30
|
+
* (§7.2.8, §9), and its frame is skipped whole wherever it arrives.
|
|
31
|
+
*
|
|
32
|
+
* **SETTINGS** (§7.2.4). `h3ReadSettings` reads a whole payload into an
|
|
33
|
+
* `Http3Settings`: the three identifiers this endpoint understands, and the
|
|
34
|
+
* first `H3_SETTINGS_KEEP` it does not, kept rather than dropped so that an
|
|
35
|
+
* extension can read them. The four that extended CONNECT and WebTransport
|
|
36
|
+
* read (RFC 9220's, RFC 9297's and the two of the WebTransport drafts) are
|
|
37
|
+
* also recorded in fields of their own, refused when repeated, so a peer
|
|
38
|
+
* cannot push them out of reach behind eight others. A payload that ends
|
|
39
|
+
* inside a pair is H3_FRAME_ERROR (§7.1); an identifier HTTP/2 defined with
|
|
40
|
+
* no HTTP/3 counterpart (0x00, 0x02 to 0x05), or one that appears twice, is
|
|
41
|
+
* H3_SETTINGS_ERROR (§7.2.4, §7.2.4.1). A repeat among the unknown ones is
|
|
42
|
+
* caught while they are kept, and past `H3_SETTINGS_KEEP` they are counted,
|
|
43
|
+
* not compared, so a peer cannot make the read cost more than a fixed number
|
|
44
|
+
* of compares per setting.
|
|
45
|
+
*
|
|
46
|
+
* **Stream types** (§6.2): control 0x00, push 0x01, RFC 9204's QPACK
|
|
47
|
+
* encoder 0x02 and decoder 0x03, and WebTransport's 0x54, whose bidirectional
|
|
48
|
+
* counterpart is the signal 0x41 where a request's first frame would be. Each is one varint at the start of a
|
|
49
|
+
* unidirectional stream, written with `h3PutVarint`.
|
|
50
|
+
*
|
|
51
|
+
* **Error codes** (§8.1) are the `H3_*` values from 0x0100; QPACK's three
|
|
52
|
+
* (0x0200 to 0x0202) are `nish/net/qpack`'s.
|
|
53
|
+
*
|
|
54
|
+
* Written from RFC 9114 §6.2, §7, §8.1 and §11.2, RFC 9220 §5, RFC 9297
|
|
55
|
+
* §2.1.1 and §5.2 and draft-ietf-webtrans-http3-02, not ported from another
|
|
56
|
+
* implementation. Private names carry the `h3Frame` prefix, since a `std/`
|
|
57
|
+
* module's private functions share the importing program's flat symbol
|
|
58
|
+
* namespace (`docs/wp26-stdlib.md` §3e).
|
|
59
|
+
*/
|
|
60
|
+
import {
|
|
61
|
+
QPACK_DECODER_STREAM,
|
|
62
|
+
QPACK_ENCODER_STREAM,
|
|
63
|
+
QPACK_SETTINGS_BLOCKED_STREAMS,
|
|
64
|
+
QPACK_SETTINGS_MAX_TABLE_CAPACITY,
|
|
65
|
+
} from "nish/net/qpack"
|
|
66
|
+
import { quicVarintLength, quicVarintPut, quicVarintRead, quicVarintSize } from "nish/net/quic-packet"
|
|
67
|
+
|
|
68
|
+
// ---- Frame types (§7.2, §11.2.1) ------------------------------------------------
|
|
69
|
+
|
|
70
|
+
/** DATA (§7.2.1): a piece of a message's content. */
|
|
71
|
+
export const H3_FRAME_DATA: i64 = 0x00
|
|
72
|
+
/** HEADERS (§7.2.2): a QPACK-encoded field section. */
|
|
73
|
+
export const H3_FRAME_HEADERS: i64 = 0x01
|
|
74
|
+
/** CANCEL_PUSH (§7.2.3): one push ID. */
|
|
75
|
+
export const H3_FRAME_CANCEL_PUSH: i64 = 0x03
|
|
76
|
+
/** SETTINGS (§7.2.4): identifier and value pairs, first on a control stream. */
|
|
77
|
+
export const H3_FRAME_SETTINGS: i64 = 0x04
|
|
78
|
+
/** PUSH_PROMISE (§7.2.5): a push ID and a request's field section; only a server sends it. */
|
|
79
|
+
export const H3_FRAME_PUSH_PROMISE: i64 = 0x05
|
|
80
|
+
/** GOAWAY (§7.2.6): a stream ID from a server, a push ID from a client. */
|
|
81
|
+
export const H3_FRAME_GOAWAY: i64 = 0x07
|
|
82
|
+
/** MAX_PUSH_ID (§7.2.7): the largest push ID a client lets a server use. */
|
|
83
|
+
export const H3_FRAME_MAX_PUSH_ID: i64 = 0x0d
|
|
84
|
+
|
|
85
|
+
// ---- Unidirectional stream types (§6.2, RFC 9204 §4.2) --------------------------
|
|
86
|
+
|
|
87
|
+
/** The control stream: SETTINGS first, then the connection's control frames. */
|
|
88
|
+
export const H3_STREAM_CONTROL: i64 = 0x00
|
|
89
|
+
/** A push stream, which only a server opens. */
|
|
90
|
+
export const H3_STREAM_PUSH: i64 = 0x01
|
|
91
|
+
/** QPACK's encoder stream. */
|
|
92
|
+
export const H3_STREAM_QPACK_ENCODER: i64 = QPACK_ENCODER_STREAM
|
|
93
|
+
/** QPACK's decoder stream. */
|
|
94
|
+
export const H3_STREAM_QPACK_DECODER: i64 = QPACK_DECODER_STREAM
|
|
95
|
+
/**
|
|
96
|
+
* A WebTransport unidirectional stream (draft-ietf-webtrans-http3-02 §4.1):
|
|
97
|
+
* the type, then the session ID, then the session's bytes.
|
|
98
|
+
*/
|
|
99
|
+
export const H3_STREAM_WEBTRANSPORT: i64 = 0x54
|
|
100
|
+
/**
|
|
101
|
+
* The signal that opens a WebTransport bidirectional stream (draft-02 §4.2):
|
|
102
|
+
* where a request's first frame type would be, then the session ID, then the
|
|
103
|
+
* session's bytes. It is not a frame: no length follows it.
|
|
104
|
+
*/
|
|
105
|
+
export const H3_FRAME_WEBTRANSPORT_STREAM: i64 = 0x41
|
|
106
|
+
|
|
107
|
+
// ---- Settings (§7.2.4.1, RFC 9204 §5) -------------------------------------------
|
|
108
|
+
|
|
109
|
+
/** SETTINGS_QPACK_MAX_TABLE_CAPACITY. */
|
|
110
|
+
export const H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY: i64 = QPACK_SETTINGS_MAX_TABLE_CAPACITY
|
|
111
|
+
/** SETTINGS_MAX_FIELD_SECTION_SIZE: the largest field section the sender takes, as §4.2.2 counts it. */
|
|
112
|
+
export const H3_SETTINGS_MAX_FIELD_SECTION_SIZE: i64 = 0x06
|
|
113
|
+
/** SETTINGS_QPACK_BLOCKED_STREAMS. */
|
|
114
|
+
export const H3_SETTINGS_QPACK_BLOCKED_STREAMS: i64 = QPACK_SETTINGS_BLOCKED_STREAMS
|
|
115
|
+
/** SETTINGS_ENABLE_CONNECT_PROTOCOL (RFC 9220 §5, RFC 8441 §3): 1 lets the peer send an extended CONNECT. */
|
|
116
|
+
export const H3_SETTINGS_ENABLE_CONNECT_PROTOCOL: i64 = 0x08
|
|
117
|
+
/** SETTINGS_H3_DATAGRAM (RFC 9297 §2.1.1): 1 lets the peer send HTTP datagrams. */
|
|
118
|
+
export const H3_SETTINGS_H3_DATAGRAM: i64 = 0x33
|
|
119
|
+
/** SETTINGS_ENABLE_WEBTRANSPORT of draft-ietf-webtrans-http3-02 §3.1, which Chrome and `wtransport` 0.7 send. */
|
|
120
|
+
export const H3_SETTINGS_ENABLE_WEBTRANSPORT: i64 = 0x2b603742
|
|
121
|
+
/** SETTINGS_WEBTRANSPORT_MAX_SESSIONS of draft-ietf-webtrans-http3-07 §3.1, which `wtransport` 0.7 sends beside the draft-02 one. */
|
|
122
|
+
export const H3_SETTINGS_WEBTRANSPORT_MAX_SESSIONS: i64 = 0xc671706a
|
|
123
|
+
|
|
124
|
+
/** How many identifiers `Http3Settings` keeps that it does not understand. */
|
|
125
|
+
export const H3_SETTINGS_KEEP: i32 = 8
|
|
126
|
+
|
|
127
|
+
// ---- Error codes (§8.1) ---------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
/** What `h3ReadSettings` answers for a payload it read whole. */
|
|
130
|
+
export const H3_SETTINGS_OK: i64 = 0
|
|
131
|
+
export const H3_NO_ERROR: i64 = 0x0100
|
|
132
|
+
export const H3_GENERAL_PROTOCOL_ERROR: i64 = 0x0101
|
|
133
|
+
export const H3_INTERNAL_ERROR: i64 = 0x0102
|
|
134
|
+
export const H3_STREAM_CREATION_ERROR: i64 = 0x0103
|
|
135
|
+
export const H3_CLOSED_CRITICAL_STREAM: i64 = 0x0104
|
|
136
|
+
export const H3_FRAME_UNEXPECTED: i64 = 0x0105
|
|
137
|
+
export const H3_FRAME_ERROR: i64 = 0x0106
|
|
138
|
+
export const H3_EXCESSIVE_LOAD: i64 = 0x0107
|
|
139
|
+
export const H3_ID_ERROR: i64 = 0x0108
|
|
140
|
+
export const H3_SETTINGS_ERROR: i64 = 0x0109
|
|
141
|
+
export const H3_MISSING_SETTINGS: i64 = 0x010a
|
|
142
|
+
export const H3_REQUEST_REJECTED: i64 = 0x010b
|
|
143
|
+
export const H3_REQUEST_CANCELLED: i64 = 0x010c
|
|
144
|
+
export const H3_REQUEST_INCOMPLETE: i64 = 0x010d
|
|
145
|
+
export const H3_MESSAGE_ERROR: i64 = 0x010e
|
|
146
|
+
export const H3_CONNECT_ERROR: i64 = 0x010f
|
|
147
|
+
export const H3_VERSION_FALLBACK: i64 = 0x0110
|
|
148
|
+
/** WT_SESSION_GONE (draft-ietf-webtrans-http3-02 §4.5): a WebTransport stream of a session that is gone, or can never come. */
|
|
149
|
+
export const H3_WT_SESSION_GONE: i64 = 0x170d7b68
|
|
150
|
+
/** H3_DATAGRAM_ERROR (RFC 9297 §5.2): an HTTP datagram or a capsule that does not parse. */
|
|
151
|
+
export const H3_DATAGRAM_ERROR: i64 = 0x33
|
|
152
|
+
|
|
153
|
+
/** The largest frame header: two eight-byte varints. */
|
|
154
|
+
export const H3_FRAME_HEADER_MAX: i32 = 16
|
|
155
|
+
|
|
156
|
+
/** The setting bits `Http3Settings.seen` records, one per identifier it understands. */
|
|
157
|
+
const H3_FRAME_SEEN_CAPACITY: i32 = 1
|
|
158
|
+
const H3_FRAME_SEEN_FIELD_SECTION: i32 = 2
|
|
159
|
+
const H3_FRAME_SEEN_BLOCKED: i32 = 4
|
|
160
|
+
|
|
161
|
+
/** A typed -1 and 0, since a bare literal is an `f64` under `--number-mode f64`. */
|
|
162
|
+
const H3_FRAME_NONE: i64 = -1
|
|
163
|
+
const H3_FRAME_ZERO: i32 = 0
|
|
164
|
+
|
|
165
|
+
/** One frame header as `h3ReadFrameHeader` read it: the type and the payload's length. */
|
|
166
|
+
export class Http3FrameHeader {
|
|
167
|
+
type: i64 = 0
|
|
168
|
+
length: i64 = 0
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The peer's SETTINGS as `h3ReadSettings` read them. The three it understands
|
|
173
|
+
* hold RFC 9114's and RFC 9204's defaults until a frame sets them:
|
|
174
|
+
* `maxFieldSectionSize` is -1, unlimited (§7.2.4.1). The first
|
|
175
|
+
* `H3_SETTINGS_KEEP` identifiers it does not understand are kept with their
|
|
176
|
+
* values, in order, and the rest counted in `dropped`.
|
|
177
|
+
*/
|
|
178
|
+
export class Http3Settings {
|
|
179
|
+
unknownIds: i64[]
|
|
180
|
+
unknownValues: i64[]
|
|
181
|
+
maxTableCapacity: i64 = 0
|
|
182
|
+
maxFieldSectionSize: i64 = -1
|
|
183
|
+
blockedStreams: i64 = 0
|
|
184
|
+
/**
|
|
185
|
+
* The extension settings WebTransport reads, -1 when the frame did not
|
|
186
|
+
* carry them: SETTINGS_ENABLE_CONNECT_PROTOCOL, SETTINGS_H3_DATAGRAM,
|
|
187
|
+
* SETTINGS_ENABLE_WEBTRANSPORT and SETTINGS_WEBTRANSPORT_MAX_SESSIONS. Each
|
|
188
|
+
* is recorded here, where a peer cannot push it out of reach, and also kept
|
|
189
|
+
* as an unknown identifier while there is room, so `unknown` still finds it.
|
|
190
|
+
*/
|
|
191
|
+
enableConnectProtocol: i64 = -1
|
|
192
|
+
h3Datagram: i64 = -1
|
|
193
|
+
enableWebtransport: i64 = -1
|
|
194
|
+
webtransportMaxSessions: i64 = -1
|
|
195
|
+
/** The bits of the identifiers understood that the frame set. */
|
|
196
|
+
seen: i32 = 0
|
|
197
|
+
/** How many unknown identifiers are kept. */
|
|
198
|
+
unknownCount: i32 = 0
|
|
199
|
+
/** How many more there were, saturating at 2^31 - 1. */
|
|
200
|
+
dropped: i32 = 0
|
|
201
|
+
|
|
202
|
+
constructor() {
|
|
203
|
+
this.unknownIds = new Array<i64>(H3_SETTINGS_KEEP)
|
|
204
|
+
this.unknownValues = new Array<i64>(H3_SETTINGS_KEEP)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Back to the defaults, for the next connection. */
|
|
208
|
+
reset(): void {
|
|
209
|
+
this.maxTableCapacity = 0
|
|
210
|
+
this.maxFieldSectionSize = -1
|
|
211
|
+
this.blockedStreams = 0
|
|
212
|
+
this.enableConnectProtocol = -1
|
|
213
|
+
this.h3Datagram = -1
|
|
214
|
+
this.enableWebtransport = -1
|
|
215
|
+
this.webtransportMaxSessions = -1
|
|
216
|
+
this.seen = 0
|
|
217
|
+
this.unknownCount = 0
|
|
218
|
+
this.dropped = 0
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** The value of an identifier this module does not understand, or -1 when the frame did not carry it (or carried it past the kept ones). */
|
|
222
|
+
unknown(id: i64): i64 {
|
|
223
|
+
for (let k: i32 = 0; k < this.unknownCount && k < H3_SETTINGS_KEEP; k++) {
|
|
224
|
+
if (this.unknownIds[k] === id) {
|
|
225
|
+
return this.unknownValues[k]
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return H3_FRAME_NONE
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Records one pair; answers `H3_SETTINGS_OK` or H3_SETTINGS_ERROR for a reserved or repeated identifier. */
|
|
232
|
+
take(id: i64, value: i64): i64 {
|
|
233
|
+
if (id === 0x00 || (id >= 0x02 && id <= 0x05)) {
|
|
234
|
+
return H3_SETTINGS_ERROR
|
|
235
|
+
}
|
|
236
|
+
let bit: i32 = 0
|
|
237
|
+
if (id === H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY) {
|
|
238
|
+
bit = H3_FRAME_SEEN_CAPACITY
|
|
239
|
+
this.maxTableCapacity = value
|
|
240
|
+
} else if (id === H3_SETTINGS_MAX_FIELD_SECTION_SIZE) {
|
|
241
|
+
bit = H3_FRAME_SEEN_FIELD_SECTION
|
|
242
|
+
this.maxFieldSectionSize = value
|
|
243
|
+
} else if (id === H3_SETTINGS_QPACK_BLOCKED_STREAMS) {
|
|
244
|
+
bit = H3_FRAME_SEEN_BLOCKED
|
|
245
|
+
this.blockedStreams = value
|
|
246
|
+
}
|
|
247
|
+
if (bit !== 0) {
|
|
248
|
+
if ((this.seen & bit) !== 0) {
|
|
249
|
+
return H3_SETTINGS_ERROR
|
|
250
|
+
}
|
|
251
|
+
this.seen = this.seen | bit
|
|
252
|
+
return H3_SETTINGS_OK
|
|
253
|
+
}
|
|
254
|
+
// The extension settings: recorded (-1 until then, so a second is a repeat), then kept below like any unknown one.
|
|
255
|
+
if (id === H3_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
|
|
256
|
+
if (this.enableConnectProtocol >= 0) {
|
|
257
|
+
return H3_SETTINGS_ERROR
|
|
258
|
+
}
|
|
259
|
+
this.enableConnectProtocol = value
|
|
260
|
+
} else if (id === H3_SETTINGS_H3_DATAGRAM) {
|
|
261
|
+
if (this.h3Datagram >= 0) {
|
|
262
|
+
return H3_SETTINGS_ERROR
|
|
263
|
+
}
|
|
264
|
+
this.h3Datagram = value
|
|
265
|
+
} else if (id === H3_SETTINGS_ENABLE_WEBTRANSPORT) {
|
|
266
|
+
if (this.enableWebtransport >= 0) {
|
|
267
|
+
return H3_SETTINGS_ERROR
|
|
268
|
+
}
|
|
269
|
+
this.enableWebtransport = value
|
|
270
|
+
} else if (id === H3_SETTINGS_WEBTRANSPORT_MAX_SESSIONS) {
|
|
271
|
+
if (this.webtransportMaxSessions >= 0) {
|
|
272
|
+
return H3_SETTINGS_ERROR
|
|
273
|
+
}
|
|
274
|
+
this.webtransportMaxSessions = value
|
|
275
|
+
}
|
|
276
|
+
if (this.unknown(id) >= 0) {
|
|
277
|
+
return H3_SETTINGS_ERROR
|
|
278
|
+
}
|
|
279
|
+
const at: i32 = this.unknownCount
|
|
280
|
+
if (at >= 0 && at < H3_SETTINGS_KEEP) {
|
|
281
|
+
this.unknownIds[at] = id
|
|
282
|
+
this.unknownValues[at] = value
|
|
283
|
+
this.unknownCount = at + 1
|
|
284
|
+
} else if (this.dropped < 2147483647) {
|
|
285
|
+
this.dropped = this.dropped + 1
|
|
286
|
+
}
|
|
287
|
+
return H3_SETTINGS_OK
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Panics unless `[off, off + len)` lies inside `buf`: a window outside its buffer is the program's mistake. */
|
|
292
|
+
export const h3CheckWindow = (what: string, buf: u8[], off: i32, len: i32): void => {
|
|
293
|
+
if (off < 0 || len < 0 || toI64(off) + toI64(len) > toI64(buf.length)) {
|
|
294
|
+
panic(`${what}: the window [${off}, ${off} + ${len}) is outside a buffer of ${buf.length} bytes`)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** `count` plus one, saturating at 2^31 - 1: every counter a peer drives. */
|
|
299
|
+
export const h3Count = (count: i32): i32 => (count < 2147483647 ? count + 1 : count)
|
|
300
|
+
|
|
301
|
+
/** Whether `[at, end)` is a usable place to write in `buf`. */
|
|
302
|
+
const h3FrameRoom = (buf: u8[], at: i32, end: i32): boolean =>
|
|
303
|
+
at >= 0 && at <= end && end <= toI32(buf.length)
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The varint at `buf[at]`, read only from `buf[at .. end)`, or -1 when it
|
|
307
|
+
* does not fit there. Advance past it by `h3VarintLength(buf, at)`.
|
|
308
|
+
*/
|
|
309
|
+
export const h3ReadVarint = (buf: u8[], at: i32, end: i32): i64 => quicVarintRead(buf, at, end)
|
|
310
|
+
|
|
311
|
+
/** The length of the varint whose first byte is `buf[at]`: 1, 2, 4 or 8, or 0 outside `buf`. */
|
|
312
|
+
export const h3VarintLength = (buf: u8[], at: i32): i32 => quicVarintLength(buf, at)
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Writes `value` as a varint in the fewest bytes at `buf[at]`, before `end`,
|
|
316
|
+
* and answers the offset past it, or -1 when it does not fit or no varint
|
|
317
|
+
* holds the value.
|
|
318
|
+
*/
|
|
319
|
+
export const h3PutVarint = (buf: u8[], at: i32, end: i32, value: i64): i32 => {
|
|
320
|
+
const size: i32 = quicVarintSize(value)
|
|
321
|
+
if (size === 0 || !h3FrameRoom(buf, at, end) || at > end - size) {
|
|
322
|
+
return -1
|
|
323
|
+
}
|
|
324
|
+
return quicVarintPut(buf, at, value, size)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** How many bytes a frame header of `type` and `length` takes, or 0 when either is no varint. */
|
|
328
|
+
export const h3FrameHeaderSize = (type: i64, length: i64): i32 => {
|
|
329
|
+
const a: i32 = quicVarintSize(type)
|
|
330
|
+
const b: i32 = quicVarintSize(length)
|
|
331
|
+
return a === 0 || b === 0 ? H3_FRAME_ZERO : a + b
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Reads a frame header from `buf[off, off + len)` into `h` and answers how
|
|
336
|
+
* many bytes it took, or 0 when the window ends inside it (and `h` is not
|
|
337
|
+
* touched). Any type and any length up to 2^62 - 1 is a header; whether the
|
|
338
|
+
* type is allowed where it arrived is the stream's question.
|
|
339
|
+
*/
|
|
340
|
+
export const h3ReadFrameHeader = (h: Http3FrameHeader, buf: u8[], off: i32, len: i32): i32 => {
|
|
341
|
+
h3CheckWindow("h3ReadFrameHeader", buf, off, len)
|
|
342
|
+
const end: i32 = off + len
|
|
343
|
+
const type: i64 = quicVarintRead(buf, off, end)
|
|
344
|
+
if (type < 0) {
|
|
345
|
+
return 0
|
|
346
|
+
}
|
|
347
|
+
const at: i32 = off + quicVarintLength(buf, off)
|
|
348
|
+
const length: i64 = quicVarintRead(buf, at, end)
|
|
349
|
+
if (length < 0) {
|
|
350
|
+
return 0
|
|
351
|
+
}
|
|
352
|
+
h.type = type
|
|
353
|
+
h.length = length
|
|
354
|
+
return at + quicVarintLength(buf, at) - off
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Writes a frame header of `type` and `length` at `buf[at]`, before `end`; answers the offset past it, or -1. */
|
|
358
|
+
export const h3PutFrameHeader = (buf: u8[], at: i32, end: i32, type: i64, length: i64): i32 => {
|
|
359
|
+
const size: i32 = h3FrameHeaderSize(type, length)
|
|
360
|
+
if (size === 0 || !h3FrameRoom(buf, at, end) || at > end - size) {
|
|
361
|
+
return -1
|
|
362
|
+
}
|
|
363
|
+
return h3PutVarint(buf, h3PutVarint(buf, at, end, type), end, length)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Whether `type` is one HTTP/2 used that HTTP/3 reserves, whose receipt is H3_FRAME_UNEXPECTED (§7.2.8). */
|
|
367
|
+
export const h3ReservedHttp2Frame = (type: i64): boolean =>
|
|
368
|
+
type === 0x02 || type === 0x06 || type === 0x08 || type === 0x09
|
|
369
|
+
|
|
370
|
+
/** Whether `value` is one of the `0x1f * N + 0x21` values reserved to exercise the rule that unknowns are ignored (§7.2.8, §7.2.4.1, §6.2.3). */
|
|
371
|
+
export const h3Greased = (value: i64): boolean => value >= 0x21 && (value - 0x21) % 0x1f === 0
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Reads a whole SETTINGS payload, `buf[off, off + len)`, into `s` (which it
|
|
375
|
+
* resets first). Answers `H3_SETTINGS_OK`, H3_FRAME_ERROR for a payload that
|
|
376
|
+
* ends inside a pair (§7.1), or H3_SETTINGS_ERROR for a reserved or repeated
|
|
377
|
+
* identifier (§7.2.4).
|
|
378
|
+
*/
|
|
379
|
+
export const h3ReadSettings = (s: Http3Settings, buf: u8[], off: i32, len: i32): i64 => {
|
|
380
|
+
h3CheckWindow("h3ReadSettings", buf, off, len)
|
|
381
|
+
s.reset()
|
|
382
|
+
const end: i32 = off + len
|
|
383
|
+
let at: i32 = off
|
|
384
|
+
while (at < end) {
|
|
385
|
+
const id: i64 = quicVarintRead(buf, at, end)
|
|
386
|
+
if (id < 0) {
|
|
387
|
+
return H3_FRAME_ERROR
|
|
388
|
+
}
|
|
389
|
+
at = at + quicVarintLength(buf, at)
|
|
390
|
+
const value: i64 = quicVarintRead(buf, at, end)
|
|
391
|
+
if (value < 0) {
|
|
392
|
+
return H3_FRAME_ERROR
|
|
393
|
+
}
|
|
394
|
+
at = at + quicVarintLength(buf, at)
|
|
395
|
+
const result: i64 = s.take(id, value)
|
|
396
|
+
if (result !== H3_SETTINGS_OK) {
|
|
397
|
+
return result
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return H3_SETTINGS_OK
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Writes a whole SETTINGS frame of the pairs `ids[k]`, `values[k]` at
|
|
405
|
+
* `buf[at]`, before `end`, and answers the offset past it, or -1 when it does
|
|
406
|
+
* not fit, the lists differ in length, or a value is no varint. It writes
|
|
407
|
+
* what it is given: refusing a reserved identifier is the reader's job.
|
|
408
|
+
*/
|
|
409
|
+
export const h3PutSettings = (buf: u8[], at: i32, end: i32, ids: i64[], values: i64[]): i32 => {
|
|
410
|
+
const n: i32 = toI32(ids.length)
|
|
411
|
+
if (n !== toI32(values.length)) {
|
|
412
|
+
return -1
|
|
413
|
+
}
|
|
414
|
+
let length: i64 = 0
|
|
415
|
+
for (let k: i32 = 0; k < n && k < toI32(ids.length) && k < toI32(values.length); k++) {
|
|
416
|
+
const a: i32 = quicVarintSize(ids[k])
|
|
417
|
+
const b: i32 = quicVarintSize(values[k])
|
|
418
|
+
if (a === 0 || b === 0) {
|
|
419
|
+
return -1
|
|
420
|
+
}
|
|
421
|
+
length = length + toI64(a + b)
|
|
422
|
+
}
|
|
423
|
+
let p: i32 = h3PutFrameHeader(buf, at, end, H3_FRAME_SETTINGS, length)
|
|
424
|
+
if (p < 0 || toI64(p) + length > toI64(end)) {
|
|
425
|
+
return -1
|
|
426
|
+
}
|
|
427
|
+
for (let k: i32 = 0; k < n && k < toI32(ids.length) && k < toI32(values.length); k++) {
|
|
428
|
+
p = h3PutVarint(buf, p, end, ids[k])
|
|
429
|
+
p = h3PutVarint(buf, p, end, values[k])
|
|
430
|
+
}
|
|
431
|
+
return p
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Writes a frame whose payload is one identifier — GOAWAY, MAX_PUSH_ID or
|
|
436
|
+
* CANCEL_PUSH (§7.2.6, §7.2.7, §7.2.3) — at `buf[at]`, before `end`; answers
|
|
437
|
+
* the offset past it, or -1.
|
|
438
|
+
*/
|
|
439
|
+
export const h3PutIdFrame = (buf: u8[], at: i32, end: i32, type: i64, id: i64): i32 => {
|
|
440
|
+
const size: i32 = quicVarintSize(id)
|
|
441
|
+
if (size === 0) {
|
|
442
|
+
return -1
|
|
443
|
+
}
|
|
444
|
+
const p: i32 = h3PutFrameHeader(buf, at, end, type, toI64(size))
|
|
445
|
+
return p < 0 ? -1 : h3PutVarint(buf, p, end, id)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* The identifier a GOAWAY, MAX_PUSH_ID or CANCEL_PUSH payload
|
|
450
|
+
* `buf[off, off + len)` carries, or -1 when the payload is not exactly one
|
|
451
|
+
* varint, which is H3_FRAME_ERROR (§7.1).
|
|
452
|
+
*/
|
|
453
|
+
export const h3ReadIdPayload = (buf: u8[], off: i32, len: i32): i64 => {
|
|
454
|
+
h3CheckWindow("h3ReadIdPayload", buf, off, len)
|
|
455
|
+
const id: i64 = quicVarintRead(buf, off, off + len)
|
|
456
|
+
return id >= 0 && quicVarintLength(buf, off) === len ? id : H3_FRAME_NONE
|
|
457
|
+
}
|