@trevrpc/trevrpc-js 1.0.0 → 1.1.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/README.md +57 -18
- package/bin/protoc-gen-trevrpc-js.js +1 -1
- package/package.json +7 -10
- package/src/call.js +269 -36
- package/src/channel.js +152 -17
- package/src/client.js +481 -103
- package/src/datagram.js +336 -0
- package/src/framing.js +108 -111
- package/src/generator.js +41 -7
- package/src/index.d.ts +162 -17
- package/src/qmux/frames.js +262 -0
- package/src/qmux/params.js +104 -0
- package/src/qmux/session.js +739 -0
- package/src/race.js +90 -0
- package/src/status.js +1 -1
- package/src/varint.js +73 -0
- package/src/wire.js +17 -30
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readVarint, varintLength } from "../varint.js";
|
|
2
|
+
// QX_TRANSPORT_PARAMETERS (draft-ietf-quic-qmux-02 §6): the subset of QUIC transport parameters
|
|
3
|
+
// that QMux keeps, plus max_record_size.
|
|
4
|
+
import { FrameType, FrameWriter, QMuxProtocolError, TransportError } from "./frames.js";
|
|
5
|
+
|
|
6
|
+
/** The max_record_size a peer may assume until told otherwise, and the smallest it may set. */
|
|
7
|
+
export const DefaultMaxRecordSize = 16382;
|
|
8
|
+
|
|
9
|
+
const Param = Object.freeze({
|
|
10
|
+
MaxIdleTimeout: 0x01,
|
|
11
|
+
InitialMaxData: 0x04,
|
|
12
|
+
InitialMaxStreamDataBidiLocal: 0x05,
|
|
13
|
+
InitialMaxStreamDataBidiRemote: 0x06,
|
|
14
|
+
InitialMaxStreamDataUni: 0x07,
|
|
15
|
+
InitialMaxStreamsBidi: 0x08,
|
|
16
|
+
InitialMaxStreamsUni: 0x09,
|
|
17
|
+
MaxRecordSize: 0x0571_c594_29cd_0845n,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/** Parameters that only make sense for QUIC packets. */
|
|
21
|
+
const Forbidden = new Set([0x00, 0x02, 0x03, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]);
|
|
22
|
+
|
|
23
|
+
const Fields = new Map([
|
|
24
|
+
[Param.MaxIdleTimeout, "maxIdleTimeoutMs"],
|
|
25
|
+
[Param.InitialMaxData, "initialMaxData"],
|
|
26
|
+
[Param.InitialMaxStreamDataBidiLocal, "initialMaxStreamDataBidiLocal"],
|
|
27
|
+
[Param.InitialMaxStreamDataBidiRemote, "initialMaxStreamDataBidiRemote"],
|
|
28
|
+
[Param.InitialMaxStreamDataUni, "initialMaxStreamDataUni"],
|
|
29
|
+
[Param.InitialMaxStreamsBidi, "initialMaxStreamsBidi"],
|
|
30
|
+
[Param.InitialMaxStreamsUni, "initialMaxStreamsUni"],
|
|
31
|
+
[Param.MaxRecordSize, "maxRecordSize"],
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
/** Appends the QX_TRANSPORT_PARAMETERS frame advertising `params`; zero values are omitted. */
|
|
35
|
+
export function writeTransportParameters(writer, params) {
|
|
36
|
+
const body = new FrameWriter();
|
|
37
|
+
for (const [id, field] of Fields) {
|
|
38
|
+
const value = params[field] ?? (id === Param.MaxRecordSize ? DefaultMaxRecordSize : 0);
|
|
39
|
+
if (value !== 0) {
|
|
40
|
+
body.varint(id).varint(varintLength(value)).varint(value);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const bytes = body.take();
|
|
44
|
+
return writer.varint(FrameType.TransportParameters).varint(bytes.length).bytes(bytes);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parameterError(message) {
|
|
48
|
+
return new QMuxProtocolError(
|
|
49
|
+
TransportError.TransportParameterError,
|
|
50
|
+
message,
|
|
51
|
+
FrameType.TransportParameters,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Decodes a peer's transport parameters; unknown ones are ignored. */
|
|
56
|
+
export function decodeTransportParameters(bytes) {
|
|
57
|
+
const params = {
|
|
58
|
+
maxIdleTimeoutMs: 0,
|
|
59
|
+
initialMaxData: 0,
|
|
60
|
+
initialMaxStreamDataBidiLocal: 0,
|
|
61
|
+
initialMaxStreamDataBidiRemote: 0,
|
|
62
|
+
initialMaxStreamDataUni: 0,
|
|
63
|
+
initialMaxStreamsBidi: 0,
|
|
64
|
+
initialMaxStreamsUni: 0,
|
|
65
|
+
maxRecordSize: DefaultMaxRecordSize,
|
|
66
|
+
};
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
let offset = 0;
|
|
69
|
+
while (offset < bytes.length) {
|
|
70
|
+
const id = readVarint(bytes, offset);
|
|
71
|
+
const length = id && readVarint(bytes, id.next);
|
|
72
|
+
if (!length || typeof length.value === "bigint" || length.next + length.value > bytes.length) {
|
|
73
|
+
throw parameterError("truncated transport parameters");
|
|
74
|
+
}
|
|
75
|
+
const value = bytes.subarray(length.next, length.next + length.value);
|
|
76
|
+
offset = length.next + length.value;
|
|
77
|
+
if (seen.has(id.value)) {
|
|
78
|
+
throw parameterError(`duplicate transport parameter 0x${id.value.toString(16)}`);
|
|
79
|
+
}
|
|
80
|
+
seen.add(id.value);
|
|
81
|
+
if (Forbidden.has(id.value)) {
|
|
82
|
+
throw parameterError(`transport parameter 0x${id.value.toString(16)} is not allowed in QMux`);
|
|
83
|
+
}
|
|
84
|
+
const field = Fields.get(id.value);
|
|
85
|
+
if (field === undefined) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const parsed = readVarint(value, 0);
|
|
89
|
+
if (!parsed || parsed.next !== value.length) {
|
|
90
|
+
throw parameterError(`malformed transport parameter 0x${id.value.toString(16)}`);
|
|
91
|
+
}
|
|
92
|
+
// Limits beyond 2^53 are as good as unlimited.
|
|
93
|
+
params[field] = typeof parsed.value === "bigint" ? Number.MAX_SAFE_INTEGER : parsed.value;
|
|
94
|
+
}
|
|
95
|
+
if (params.maxRecordSize < DefaultMaxRecordSize) {
|
|
96
|
+
throw parameterError(
|
|
97
|
+
`max_record_size ${params.maxRecordSize} is below ${DefaultMaxRecordSize}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (params.initialMaxStreamsBidi > 2 ** 60) {
|
|
101
|
+
throw parameterError("initial_max_streams_bidi exceeds 2^60");
|
|
102
|
+
}
|
|
103
|
+
return params;
|
|
104
|
+
}
|