@libp2p/yamux 7.0.4
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 +127 -0
- package/dist/index.min.js +4 -0
- package/dist/index.min.js.map +7 -0
- package/dist/src/config.d.ts +26 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +37 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/constants.d.ts +12 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +22 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/decode.d.ts +34 -0
- package/dist/src/decode.d.ts.map +1 -0
- package/dist/src/decode.js +75 -0
- package/dist/src/decode.js.map +1 -0
- package/dist/src/encode.d.ts +3 -0
- package/dist/src/encode.d.ts.map +1 -0
- package/dist/src/encode.js +19 -0
- package/dist/src/encode.js.map +1 -0
- package/dist/src/errors.d.ts +40 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +70 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/frame.d.ts +54 -0
- package/dist/src/frame.d.ts.map +1 -0
- package/dist/src/frame.js +36 -0
- package/dist/src/frame.js.map +1 -0
- package/dist/src/index.d.ts +85 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +84 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/muxer.d.ts +91 -0
- package/dist/src/muxer.d.ts.map +1 -0
- package/dist/src/muxer.js +428 -0
- package/dist/src/muxer.js.map +1 -0
- package/dist/src/stream.d.ts +96 -0
- package/dist/src/stream.d.ts.map +1 -0
- package/dist/src/stream.js +240 -0
- package/dist/src/stream.js.map +1 -0
- package/package.json +188 -0
- package/src/config.ts +66 -0
- package/src/constants.ts +25 -0
- package/src/decode.ts +104 -0
- package/src/encode.ts +26 -0
- package/src/errors.ts +89 -0
- package/src/frame.ts +64 -0
- package/src/index.ts +90 -0
- package/src/muxer.ts +506 -0
- package/src/stream.ts +293 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { AbstractStream } from '@libp2p/utils';
|
|
2
|
+
import { Uint8ArrayList } from 'uint8arraylist';
|
|
3
|
+
import { defaultConfig } from './config.js';
|
|
4
|
+
import { INITIAL_STREAM_WINDOW } from './constants.js';
|
|
5
|
+
import { isDataFrame } from "./decode.js";
|
|
6
|
+
import { InvalidFrameError, ReceiveWindowExceededError } from './errors.js';
|
|
7
|
+
import { Flag, FrameType, HEADER_LENGTH } from './frame.js';
|
|
8
|
+
export var StreamState;
|
|
9
|
+
(function (StreamState) {
|
|
10
|
+
StreamState[StreamState["Init"] = 0] = "Init";
|
|
11
|
+
StreamState[StreamState["SYNSent"] = 1] = "SYNSent";
|
|
12
|
+
StreamState[StreamState["SYNReceived"] = 2] = "SYNReceived";
|
|
13
|
+
StreamState[StreamState["Established"] = 3] = "Established";
|
|
14
|
+
StreamState[StreamState["Finished"] = 4] = "Finished";
|
|
15
|
+
StreamState[StreamState["Paused"] = 5] = "Paused";
|
|
16
|
+
})(StreamState || (StreamState = {}));
|
|
17
|
+
/** YamuxStream is used to represent a logical stream within a session */
|
|
18
|
+
export class YamuxStream extends AbstractStream {
|
|
19
|
+
streamId;
|
|
20
|
+
state;
|
|
21
|
+
/** The number of available bytes to send */
|
|
22
|
+
sendWindowCapacity;
|
|
23
|
+
/** The number of bytes available to receive in a full window */
|
|
24
|
+
recvWindow;
|
|
25
|
+
/** The number of available bytes to receive */
|
|
26
|
+
recvWindowCapacity;
|
|
27
|
+
maxStreamWindowSize;
|
|
28
|
+
/**
|
|
29
|
+
* An 'epoch' is the time it takes to process and read data
|
|
30
|
+
*
|
|
31
|
+
* Used in conjunction with RTT to determine whether to increase the recvWindow
|
|
32
|
+
*/
|
|
33
|
+
epochStart;
|
|
34
|
+
getRTT;
|
|
35
|
+
sendFrame;
|
|
36
|
+
constructor(init) {
|
|
37
|
+
super({
|
|
38
|
+
...init,
|
|
39
|
+
maxMessageSize: (init.maxMessageSize ?? defaultConfig.maxMessageSize) - HEADER_LENGTH
|
|
40
|
+
});
|
|
41
|
+
this.streamId = init.streamId;
|
|
42
|
+
this.state = init.state;
|
|
43
|
+
this.sendWindowCapacity = INITIAL_STREAM_WINDOW;
|
|
44
|
+
this.recvWindow = init.initialStreamWindowSize ?? defaultConfig.initialStreamWindowSize;
|
|
45
|
+
this.recvWindowCapacity = this.recvWindow;
|
|
46
|
+
this.maxStreamWindowSize = init.maxStreamWindowSize ?? defaultConfig.maxStreamWindowSize;
|
|
47
|
+
this.epochStart = Date.now();
|
|
48
|
+
this.getRTT = init.getRTT;
|
|
49
|
+
this.sendFrame = init.sendFrame;
|
|
50
|
+
const setStateToFinishedOnCloseListener = () => {
|
|
51
|
+
this.state = StreamState.Finished;
|
|
52
|
+
};
|
|
53
|
+
this.addEventListener('close', setStateToFinishedOnCloseListener);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Send a data message to the remote muxer
|
|
57
|
+
*/
|
|
58
|
+
sendData(buf) {
|
|
59
|
+
const totalBytes = buf.byteLength;
|
|
60
|
+
let sentBytes = 0;
|
|
61
|
+
let canSendMore = true;
|
|
62
|
+
// send in chunks, waiting for window updates
|
|
63
|
+
while (buf.byteLength !== 0) {
|
|
64
|
+
// we exhausted the send window, sending will resume later
|
|
65
|
+
if (this.sendWindowCapacity === 0) {
|
|
66
|
+
canSendMore = false;
|
|
67
|
+
this.log?.trace('sent %d/%d bytes, wait for send window update, status %s', sentBytes, totalBytes, this.status);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
// send as much as we can
|
|
71
|
+
const toSend = Math.min(this.sendWindowCapacity, buf.length);
|
|
72
|
+
const flags = this.getSendFlags();
|
|
73
|
+
const muxerSendMore = this.sendFrame({
|
|
74
|
+
type: FrameType.Data,
|
|
75
|
+
flag: flags,
|
|
76
|
+
streamID: this.streamId,
|
|
77
|
+
length: toSend
|
|
78
|
+
}, buf.sublist(0, toSend));
|
|
79
|
+
this.sendWindowCapacity -= toSend;
|
|
80
|
+
sentBytes += toSend;
|
|
81
|
+
buf.consume(toSend);
|
|
82
|
+
if (!muxerSendMore) {
|
|
83
|
+
canSendMore = muxerSendMore;
|
|
84
|
+
this.log('sent %d/%d bytes, wait for muxer to have more send capacity', sentBytes, totalBytes);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
sentBytes,
|
|
90
|
+
canSendMore
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Send a reset message to the remote muxer
|
|
95
|
+
*/
|
|
96
|
+
async sendReset() {
|
|
97
|
+
this.sendFrame({
|
|
98
|
+
type: FrameType.WindowUpdate,
|
|
99
|
+
flag: Flag.RST,
|
|
100
|
+
streamID: this.streamId,
|
|
101
|
+
length: 0
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Send a message to the remote muxer, informing them no more data messages
|
|
106
|
+
* will be sent by this end of the stream
|
|
107
|
+
*/
|
|
108
|
+
async sendCloseWrite() {
|
|
109
|
+
const flags = this.getSendFlags() | Flag.FIN;
|
|
110
|
+
this.sendFrame({
|
|
111
|
+
type: FrameType.WindowUpdate,
|
|
112
|
+
flag: flags,
|
|
113
|
+
streamID: this.streamId,
|
|
114
|
+
length: 0
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Send a message to the remote muxer, informing them no more data messages
|
|
119
|
+
* will be read by this end of the stream - this is a no-op on Yamux streams
|
|
120
|
+
*/
|
|
121
|
+
async sendCloseRead(options) {
|
|
122
|
+
options?.signal?.throwIfAborted();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Stop sending window updates temporarily - in the interim the the remote
|
|
126
|
+
* send window will exhaust and the remote will stop sending data
|
|
127
|
+
*/
|
|
128
|
+
sendPause() {
|
|
129
|
+
this.state = StreamState.Paused;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Start sending window updates as normal
|
|
133
|
+
*/
|
|
134
|
+
sendResume() {
|
|
135
|
+
this.state = StreamState.Established;
|
|
136
|
+
this.sendWindowUpdate();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* handleWindowUpdate is called when the stream receives a window update frame
|
|
140
|
+
*/
|
|
141
|
+
handleWindowUpdate(frame) {
|
|
142
|
+
this.processFlags(frame.header.flag);
|
|
143
|
+
// increase send window
|
|
144
|
+
const available = this.sendWindowCapacity;
|
|
145
|
+
this.sendWindowCapacity += frame.header.length;
|
|
146
|
+
// if the update increments a 0 availability, notify the stream that sending can resume
|
|
147
|
+
if (available === 0 && frame.header.length > 0) {
|
|
148
|
+
this.log?.trace('window update of %d bytes allows more data to be sent', frame.header.length);
|
|
149
|
+
this.safeDispatchEvent('drain');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* handleData is called when the stream receives a data frame
|
|
154
|
+
*/
|
|
155
|
+
handleData(frame) {
|
|
156
|
+
if (!isDataFrame(frame)) {
|
|
157
|
+
throw new InvalidFrameError('Frame was not data frame');
|
|
158
|
+
}
|
|
159
|
+
this.processFlags(frame.header.flag);
|
|
160
|
+
// check that our recv window is not exceeded
|
|
161
|
+
if (this.recvWindowCapacity < frame.header.length) {
|
|
162
|
+
throw new ReceiveWindowExceededError('Receive window exceeded');
|
|
163
|
+
}
|
|
164
|
+
this.recvWindowCapacity -= frame.header.length;
|
|
165
|
+
this.onData(frame.data);
|
|
166
|
+
this.sendWindowUpdate();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* processFlags is used to update the state of the stream based on set flags, if any.
|
|
170
|
+
*/
|
|
171
|
+
processFlags(flags) {
|
|
172
|
+
if ((flags & Flag.ACK) === Flag.ACK) {
|
|
173
|
+
if (this.state === StreamState.SYNSent) {
|
|
174
|
+
this.state = StreamState.Established;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if ((flags & Flag.FIN) === Flag.FIN) {
|
|
178
|
+
this.onRemoteCloseWrite();
|
|
179
|
+
}
|
|
180
|
+
if ((flags & Flag.RST) === Flag.RST) {
|
|
181
|
+
this.onRemoteReset();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* getSendFlags determines any flags that are appropriate
|
|
186
|
+
* based on the current stream state.
|
|
187
|
+
*
|
|
188
|
+
* The state is updated as a side-effect.
|
|
189
|
+
*/
|
|
190
|
+
getSendFlags() {
|
|
191
|
+
switch (this.state) {
|
|
192
|
+
case StreamState.Init:
|
|
193
|
+
this.state = StreamState.SYNSent;
|
|
194
|
+
return Flag.SYN;
|
|
195
|
+
case StreamState.SYNReceived:
|
|
196
|
+
this.state = StreamState.Established;
|
|
197
|
+
return Flag.ACK;
|
|
198
|
+
default:
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Potentially sends a window update enabling further remote writes to take
|
|
204
|
+
* place.
|
|
205
|
+
*/
|
|
206
|
+
sendWindowUpdate() {
|
|
207
|
+
// determine the flags if any
|
|
208
|
+
const flags = this.getSendFlags();
|
|
209
|
+
// If the stream has already been established
|
|
210
|
+
// and we've processed data within the time it takes for 4 round trips
|
|
211
|
+
// then we (up to) double the recvWindow
|
|
212
|
+
const now = Date.now();
|
|
213
|
+
const rtt = this.getRTT();
|
|
214
|
+
if (flags === 0 && rtt > -1 && now - this.epochStart < rtt * 4) {
|
|
215
|
+
// we've already validated that maxStreamWindowSize can't be more than MAX_UINT32
|
|
216
|
+
this.recvWindow = Math.min(this.recvWindow * 2, this.maxStreamWindowSize);
|
|
217
|
+
}
|
|
218
|
+
if (this.recvWindowCapacity >= this.recvWindow && flags === 0) {
|
|
219
|
+
// a window update isn't needed
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (this.state === StreamState.Paused) {
|
|
223
|
+
// we don't want any more data from the remote right now
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
// update the receive window
|
|
227
|
+
const delta = this.recvWindow - this.recvWindowCapacity;
|
|
228
|
+
this.recvWindowCapacity = this.recvWindow;
|
|
229
|
+
// update the epoch start
|
|
230
|
+
this.epochStart = now;
|
|
231
|
+
// send window update
|
|
232
|
+
this.sendFrame({
|
|
233
|
+
type: FrameType.WindowUpdate,
|
|
234
|
+
flag: flags,
|
|
235
|
+
streamID: this.streamId,
|
|
236
|
+
length: delta
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@libp2p/yamux",
|
|
3
|
+
"version": "7.0.4",
|
|
4
|
+
"description": "Yamux stream multiplexer for libp2p",
|
|
5
|
+
"license": "Apache-2.0 OR MIT",
|
|
6
|
+
"homepage": "https://github.com/ChainSafe/js-libp2p-yamux#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/ChainSafe/js-libp2p-yamux.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/ChainSafe/js-libp2p-yamux/issues"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"IPFS",
|
|
19
|
+
"libp2p",
|
|
20
|
+
"multiplexer",
|
|
21
|
+
"muxer",
|
|
22
|
+
"stream"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"types": "./dist/src/index.d.ts",
|
|
26
|
+
"typesVersions": {
|
|
27
|
+
"*": {
|
|
28
|
+
"*": [
|
|
29
|
+
"*",
|
|
30
|
+
"dist/*",
|
|
31
|
+
"dist/src/*",
|
|
32
|
+
"dist/src/*/index"
|
|
33
|
+
],
|
|
34
|
+
"src/*": [
|
|
35
|
+
"*",
|
|
36
|
+
"dist/*",
|
|
37
|
+
"dist/src/*",
|
|
38
|
+
"dist/src/*/index"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"src",
|
|
44
|
+
"dist",
|
|
45
|
+
"!dist/test",
|
|
46
|
+
"!**/*.tsbuildinfo"
|
|
47
|
+
],
|
|
48
|
+
"exports": {
|
|
49
|
+
".": {
|
|
50
|
+
"types": "./dist/src/index.d.ts",
|
|
51
|
+
"import": "./dist/src/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./config": {
|
|
54
|
+
"types": "./dist/src/config.d.ts",
|
|
55
|
+
"import": "./dist/src/config.js"
|
|
56
|
+
},
|
|
57
|
+
"./stream": {
|
|
58
|
+
"types": "./dist/src/stream.d.ts",
|
|
59
|
+
"import": "./dist/src/stream.js"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"release": {
|
|
63
|
+
"branches": [
|
|
64
|
+
"master"
|
|
65
|
+
],
|
|
66
|
+
"plugins": [
|
|
67
|
+
[
|
|
68
|
+
"@semantic-release/commit-analyzer",
|
|
69
|
+
{
|
|
70
|
+
"preset": "conventionalcommits",
|
|
71
|
+
"releaseRules": [
|
|
72
|
+
{
|
|
73
|
+
"breaking": true,
|
|
74
|
+
"release": "major"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"revert": true,
|
|
78
|
+
"release": "patch"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"type": "feat",
|
|
82
|
+
"release": "minor"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"type": "fix",
|
|
86
|
+
"release": "patch"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"type": "docs",
|
|
90
|
+
"release": "patch"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"type": "test",
|
|
94
|
+
"release": "patch"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"type": "deps",
|
|
98
|
+
"release": "patch"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"scope": "no-release",
|
|
102
|
+
"release": false
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"@semantic-release/release-notes-generator",
|
|
109
|
+
{
|
|
110
|
+
"preset": "conventionalcommits",
|
|
111
|
+
"presetConfig": {
|
|
112
|
+
"types": [
|
|
113
|
+
{
|
|
114
|
+
"type": "feat",
|
|
115
|
+
"section": "Features"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"type": "fix",
|
|
119
|
+
"section": "Bug Fixes"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"type": "chore",
|
|
123
|
+
"section": "Trivial Changes"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"type": "docs",
|
|
127
|
+
"section": "Documentation"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"type": "deps",
|
|
131
|
+
"section": "Dependencies"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"type": "test",
|
|
135
|
+
"section": "Tests"
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"@semantic-release/changelog",
|
|
142
|
+
"@semantic-release/npm",
|
|
143
|
+
"@semantic-release/github",
|
|
144
|
+
[
|
|
145
|
+
"@semantic-release/git",
|
|
146
|
+
{
|
|
147
|
+
"assets": [
|
|
148
|
+
"CHANGELOG.md",
|
|
149
|
+
"package.json"
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
]
|
|
154
|
+
},
|
|
155
|
+
"scripts": {
|
|
156
|
+
"clean": "aegir clean",
|
|
157
|
+
"lint": "aegir lint",
|
|
158
|
+
"dep-check": "aegir dep-check",
|
|
159
|
+
"doc-check": "aegir doc-check",
|
|
160
|
+
"benchmark": "benchmark dist/test/bench/*.bench.js --timeout 400000",
|
|
161
|
+
"build": "aegir build",
|
|
162
|
+
"test": "aegir test",
|
|
163
|
+
"test:chrome": "aegir test -t browser",
|
|
164
|
+
"test:chrome-webworker": "aegir test -t webworker",
|
|
165
|
+
"test:firefox": "aegir test -t browser -- --browser firefox",
|
|
166
|
+
"test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
|
|
167
|
+
"test:node": "aegir test -t node --cov",
|
|
168
|
+
"test:electron-main": "aegir test -t electron-main",
|
|
169
|
+
"release": "aegir release",
|
|
170
|
+
"docs": "aegir docs"
|
|
171
|
+
},
|
|
172
|
+
"dependencies": {
|
|
173
|
+
"@libp2p/interface": "^2.0.0",
|
|
174
|
+
"@libp2p/utils": "^6.0.0",
|
|
175
|
+
"race-signal": "^2.0.0",
|
|
176
|
+
"uint8arraylist": "^2.4.8"
|
|
177
|
+
},
|
|
178
|
+
"devDependencies": {
|
|
179
|
+
"@dapplion/benchmark": "^1.0.0",
|
|
180
|
+
"@libp2p/interface-compliance-tests": "^6.4.12",
|
|
181
|
+
"@libp2p/mplex": "^11.0.0",
|
|
182
|
+
"aegir": "^47.0.17",
|
|
183
|
+
"it-all": "^3.0.9",
|
|
184
|
+
"it-drain": "^3.0.5",
|
|
185
|
+
"it-pushable": "^3.2.3",
|
|
186
|
+
"p-event": "^6.0.1"
|
|
187
|
+
}
|
|
188
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { InvalidParametersError } from '@libp2p/interface'
|
|
2
|
+
import { INITIAL_STREAM_WINDOW, MAX_STREAM_WINDOW } from './constants.js'
|
|
3
|
+
import type { StreamMuxerOptions } from '@libp2p/interface'
|
|
4
|
+
|
|
5
|
+
// TODO use config items or delete them
|
|
6
|
+
export interface Config extends StreamMuxerOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Used to do periodic keep alive messages using a ping.
|
|
9
|
+
*/
|
|
10
|
+
enableKeepAlive?: boolean
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* How often to perform the keep alive
|
|
14
|
+
*
|
|
15
|
+
* measured in milliseconds
|
|
16
|
+
*/
|
|
17
|
+
keepAliveInterval?: number
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Used to control the initial window size that we allow for a stream.
|
|
21
|
+
*
|
|
22
|
+
* measured in bytes
|
|
23
|
+
*/
|
|
24
|
+
initialStreamWindowSize?: number
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Used to control the maximum window size that we allow for a stream.
|
|
28
|
+
*/
|
|
29
|
+
maxStreamWindowSize?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const defaultConfig: Required<Config> = {
|
|
33
|
+
enableKeepAlive: true,
|
|
34
|
+
keepAliveInterval: 30_000,
|
|
35
|
+
maxInboundStreams: 1_000,
|
|
36
|
+
maxOutboundStreams: 1_000,
|
|
37
|
+
initialStreamWindowSize: INITIAL_STREAM_WINDOW,
|
|
38
|
+
maxStreamWindowSize: MAX_STREAM_WINDOW,
|
|
39
|
+
maxMessageSize: 64 * 1024,
|
|
40
|
+
maxEarlyStreams: 10,
|
|
41
|
+
streamOptions: {}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function verifyConfig (config: Config): void {
|
|
45
|
+
if (config.keepAliveInterval != null && config.keepAliveInterval <= 0) {
|
|
46
|
+
throw new InvalidParametersError('keep-alive interval must be positive')
|
|
47
|
+
}
|
|
48
|
+
if (config.maxInboundStreams != null && config.maxInboundStreams < 0) {
|
|
49
|
+
throw new InvalidParametersError('max inbound streams must be larger or equal 0')
|
|
50
|
+
}
|
|
51
|
+
if (config.maxOutboundStreams != null && config.maxOutboundStreams < 0) {
|
|
52
|
+
throw new InvalidParametersError('max outbound streams must be larger or equal 0')
|
|
53
|
+
}
|
|
54
|
+
if (config.initialStreamWindowSize != null && config.initialStreamWindowSize < INITIAL_STREAM_WINDOW) {
|
|
55
|
+
throw new InvalidParametersError('InitialStreamWindowSize must be larger or equal 256 kB')
|
|
56
|
+
}
|
|
57
|
+
if (config.maxStreamWindowSize != null && config.initialStreamWindowSize != null && config.maxStreamWindowSize < config.initialStreamWindowSize) {
|
|
58
|
+
throw new InvalidParametersError('MaxStreamWindowSize must be larger than the InitialStreamWindowSize')
|
|
59
|
+
}
|
|
60
|
+
if (config.maxStreamWindowSize != null && config.maxStreamWindowSize > 2 ** 32 - 1) {
|
|
61
|
+
throw new InvalidParametersError('MaxStreamWindowSize must be less than equal MAX_UINT32')
|
|
62
|
+
}
|
|
63
|
+
if (config.maxMessageSize != null && config.maxMessageSize < 1024) {
|
|
64
|
+
throw new InvalidParametersError('MaxMessageSize must be greater than a kilobyte')
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Protocol violation errors
|
|
2
|
+
|
|
3
|
+
import { BothClientsError, DecodeInvalidVersionError, InvalidFrameError, NotMatchingPingError, ReceiveWindowExceededError, StreamAlreadyExistsError, UnRequestedPingError } from './errors.js'
|
|
4
|
+
|
|
5
|
+
export const PROTOCOL_ERRORS = new Set([
|
|
6
|
+
InvalidFrameError.name,
|
|
7
|
+
UnRequestedPingError.name,
|
|
8
|
+
NotMatchingPingError.name,
|
|
9
|
+
StreamAlreadyExistsError.name,
|
|
10
|
+
DecodeInvalidVersionError.name,
|
|
11
|
+
BothClientsError.name,
|
|
12
|
+
ReceiveWindowExceededError.name
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* INITIAL_STREAM_WINDOW is the initial stream window size.
|
|
17
|
+
*
|
|
18
|
+
* Not an implementation choice, this is defined in the specification
|
|
19
|
+
*/
|
|
20
|
+
export const INITIAL_STREAM_WINDOW = 256 * 1024
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Default max stream window
|
|
24
|
+
*/
|
|
25
|
+
export const MAX_STREAM_WINDOW = 16 * 1024 * 1024
|
package/src/decode.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
2
|
+
import { InvalidFrameError } from './errors.js'
|
|
3
|
+
import { FrameType, HEADER_LENGTH, YAMUX_VERSION } from './frame.js'
|
|
4
|
+
import type { FrameHeader } from './frame.js'
|
|
5
|
+
|
|
6
|
+
export interface Frame {
|
|
7
|
+
header: FrameHeader
|
|
8
|
+
data?: Uint8ArrayList
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface DataFrame {
|
|
12
|
+
header: FrameHeader
|
|
13
|
+
data: Uint8ArrayList
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isDataFrame (frame: Frame): frame is DataFrame {
|
|
17
|
+
return frame.header.type === FrameType.Data && frame.data !== null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// used to bit shift in decoding
|
|
21
|
+
// native bit shift can overflow into a negative number, so we bit shift by
|
|
22
|
+
// multiplying by a power of 2
|
|
23
|
+
const twoPow24 = 2 ** 24
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decode a header from the front of a buffer
|
|
27
|
+
*
|
|
28
|
+
* @param data - Assumed to have enough bytes for a header
|
|
29
|
+
*/
|
|
30
|
+
export function decodeHeader (data: Uint8Array): FrameHeader {
|
|
31
|
+
if (data[0] !== YAMUX_VERSION) {
|
|
32
|
+
throw new InvalidFrameError('Invalid frame version')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
type: data[1],
|
|
37
|
+
flag: (data[2] << 8) + data[3],
|
|
38
|
+
streamID: (data[4] * twoPow24) + (data[5] << 16) + (data[6] << 8) + data[7],
|
|
39
|
+
length: (data[8] * twoPow24) + (data[9] << 16) + (data[10] << 8) + data[11]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Decodes yamux frames from a source
|
|
45
|
+
*/
|
|
46
|
+
export class Decoder {
|
|
47
|
+
/** Buffer for in-progress frames */
|
|
48
|
+
private readonly buffer: Uint8ArrayList
|
|
49
|
+
|
|
50
|
+
constructor () {
|
|
51
|
+
this.buffer = new Uint8ArrayList()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Emits frames from the decoder source.
|
|
56
|
+
*
|
|
57
|
+
* Note: If `readData` is emitted, it _must_ be called before the next iteration
|
|
58
|
+
* Otherwise an error is thrown
|
|
59
|
+
*/
|
|
60
|
+
* emitFrames (buf: Uint8Array | Uint8ArrayList): Generator<Frame> {
|
|
61
|
+
this.buffer.append(buf)
|
|
62
|
+
|
|
63
|
+
// Loop to consume as many bytes from the buffer as possible
|
|
64
|
+
// Eg: when a single chunk contains several frames
|
|
65
|
+
while (true) {
|
|
66
|
+
const frame = this.readFrame()
|
|
67
|
+
|
|
68
|
+
if (frame === undefined) {
|
|
69
|
+
break
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
yield frame
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private readFrame (): Frame | undefined {
|
|
77
|
+
let frameSize = HEADER_LENGTH
|
|
78
|
+
|
|
79
|
+
if (this.buffer.byteLength < HEADER_LENGTH) {
|
|
80
|
+
// not enough data yet
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const header = decodeHeader(this.buffer.subarray(0, HEADER_LENGTH))
|
|
85
|
+
|
|
86
|
+
if (header.type === FrameType.Data) {
|
|
87
|
+
frameSize += header.length
|
|
88
|
+
|
|
89
|
+
if (this.buffer.byteLength < frameSize) {
|
|
90
|
+
// not enough data yet
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const data = this.buffer.sublist(HEADER_LENGTH, frameSize)
|
|
95
|
+
this.buffer.consume(frameSize)
|
|
96
|
+
|
|
97
|
+
return { header, data }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.buffer.consume(frameSize)
|
|
101
|
+
|
|
102
|
+
return { header }
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/encode.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { HEADER_LENGTH } from './frame.js'
|
|
2
|
+
import type { FrameHeader } from './frame.js'
|
|
3
|
+
|
|
4
|
+
export function encodeHeader (header: FrameHeader): Uint8Array {
|
|
5
|
+
const frame = new Uint8Array(HEADER_LENGTH)
|
|
6
|
+
|
|
7
|
+
// always assume version 0
|
|
8
|
+
// frameView.setUint8(0, header.version)
|
|
9
|
+
|
|
10
|
+
frame[1] = header.type
|
|
11
|
+
|
|
12
|
+
frame[2] = header.flag >>> 8
|
|
13
|
+
frame[3] = header.flag
|
|
14
|
+
|
|
15
|
+
frame[4] = header.streamID >>> 24
|
|
16
|
+
frame[5] = header.streamID >>> 16
|
|
17
|
+
frame[6] = header.streamID >>> 8
|
|
18
|
+
frame[7] = header.streamID
|
|
19
|
+
|
|
20
|
+
frame[8] = header.length >>> 24
|
|
21
|
+
frame[9] = header.length >>> 16
|
|
22
|
+
frame[10] = header.length >>> 8
|
|
23
|
+
frame[11] = header.length
|
|
24
|
+
|
|
25
|
+
return frame
|
|
26
|
+
}
|