@libp2p/yamux 7.0.4-a02cb0461

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 (49) hide show
  1. package/README.md +127 -0
  2. package/dist/index.min.js +4 -0
  3. package/dist/index.min.js.map +7 -0
  4. package/dist/src/config.d.ts +30 -0
  5. package/dist/src/config.d.ts.map +1 -0
  6. package/dist/src/config.js +41 -0
  7. package/dist/src/config.js.map +1 -0
  8. package/dist/src/constants.d.ts +12 -0
  9. package/dist/src/constants.d.ts.map +1 -0
  10. package/dist/src/constants.js +22 -0
  11. package/dist/src/constants.js.map +1 -0
  12. package/dist/src/decode.d.ts +34 -0
  13. package/dist/src/decode.d.ts.map +1 -0
  14. package/dist/src/decode.js +76 -0
  15. package/dist/src/decode.js.map +1 -0
  16. package/dist/src/encode.d.ts +3 -0
  17. package/dist/src/encode.d.ts.map +1 -0
  18. package/dist/src/encode.js +19 -0
  19. package/dist/src/encode.js.map +1 -0
  20. package/dist/src/errors.d.ts +40 -0
  21. package/dist/src/errors.d.ts.map +1 -0
  22. package/dist/src/errors.js +70 -0
  23. package/dist/src/errors.js.map +1 -0
  24. package/dist/src/frame.d.ts +54 -0
  25. package/dist/src/frame.d.ts.map +1 -0
  26. package/dist/src/frame.js +36 -0
  27. package/dist/src/frame.js.map +1 -0
  28. package/dist/src/index.d.ts +85 -0
  29. package/dist/src/index.d.ts.map +1 -0
  30. package/dist/src/index.js +84 -0
  31. package/dist/src/index.js.map +1 -0
  32. package/dist/src/muxer.d.ts +91 -0
  33. package/dist/src/muxer.d.ts.map +1 -0
  34. package/dist/src/muxer.js +428 -0
  35. package/dist/src/muxer.js.map +1 -0
  36. package/dist/src/stream.d.ts +96 -0
  37. package/dist/src/stream.d.ts.map +1 -0
  38. package/dist/src/stream.js +253 -0
  39. package/dist/src/stream.js.map +1 -0
  40. package/package.json +95 -0
  41. package/src/config.ts +72 -0
  42. package/src/constants.ts +25 -0
  43. package/src/decode.ts +105 -0
  44. package/src/encode.ts +26 -0
  45. package/src/errors.ts +89 -0
  46. package/src/frame.ts +64 -0
  47. package/src/index.ts +90 -0
  48. package/src/muxer.ts +507 -0
  49. package/src/stream.ts +313 -0
@@ -0,0 +1,253 @@
1
+ import { AbstractStream } from '@libp2p/utils';
2
+ import { Uint8ArrayList } from 'uint8arraylist';
3
+ import { INITIAL_STREAM_WINDOW, MAX_STREAM_WINDOW } from './constants.js';
4
+ import { isDataFrame } from "./decode.js";
5
+ import { InvalidFrameError, ReceiveWindowExceededError } from './errors.js';
6
+ import { Flag, FrameType, HEADER_LENGTH } from './frame.js';
7
+ export var StreamState;
8
+ (function (StreamState) {
9
+ StreamState[StreamState["Init"] = 0] = "Init";
10
+ StreamState[StreamState["SYNSent"] = 1] = "SYNSent";
11
+ StreamState[StreamState["SYNReceived"] = 2] = "SYNReceived";
12
+ StreamState[StreamState["Established"] = 3] = "Established";
13
+ StreamState[StreamState["Finished"] = 4] = "Finished";
14
+ StreamState[StreamState["Paused"] = 5] = "Paused";
15
+ })(StreamState || (StreamState = {}));
16
+ /** YamuxStream is used to represent a logical stream within a session */
17
+ export class YamuxStream extends AbstractStream {
18
+ streamId;
19
+ state;
20
+ /** The number of available bytes to send */
21
+ sendWindowCapacity;
22
+ /** The number of bytes available to receive in a full window */
23
+ recvWindow;
24
+ /** The number of available bytes to receive */
25
+ recvWindowCapacity;
26
+ maxStreamWindowSize;
27
+ /**
28
+ * An 'epoch' is the time it takes to process and read data
29
+ *
30
+ * Used in conjunction with RTT to determine whether to increase the recvWindow
31
+ */
32
+ epochStart;
33
+ getRTT;
34
+ sendFrame;
35
+ constructor(init) {
36
+ const initialWindowSize = init.initialStreamWindowSize ?? INITIAL_STREAM_WINDOW;
37
+ super({
38
+ ...init,
39
+ maxMessageSize: initialWindowSize - HEADER_LENGTH
40
+ });
41
+ this.streamId = init.streamId;
42
+ this.state = init.state;
43
+ this.sendWindowCapacity = initialWindowSize;
44
+ this.recvWindow = initialWindowSize;
45
+ this.recvWindowCapacity = this.recvWindow;
46
+ this.maxStreamWindowSize = init.maxStreamWindowSize ?? MAX_STREAM_WINDOW;
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
+ this.log?.trace('send window capacity is %d bytes', this.sendWindowCapacity);
63
+ // send in chunks, waiting for window updates
64
+ while (buf.byteLength > 0) {
65
+ // we exhausted the send window, sending will resume later
66
+ if (this.sendWindowCapacity === 0) {
67
+ canSendMore = false;
68
+ this.log?.trace('sent %d/%d bytes, exhausted send window, waiting for window update', sentBytes, totalBytes);
69
+ break;
70
+ }
71
+ // send as much as we can
72
+ const toSend = Math.min(this.sendWindowCapacity, buf.byteLength);
73
+ const flags = this.getSendFlags();
74
+ const data = buf.sublist(0, toSend);
75
+ buf.consume(toSend);
76
+ const muxerSendMore = this.sendFrame({
77
+ type: FrameType.Data,
78
+ flag: flags,
79
+ streamID: this.streamId,
80
+ length: toSend
81
+ }, data);
82
+ this.sendWindowCapacity -= toSend;
83
+ sentBytes += toSend;
84
+ if (!muxerSendMore) {
85
+ canSendMore = muxerSendMore;
86
+ this.log.trace('sent %d/%d bytes, wait for muxer to have more send capacity', sentBytes, totalBytes);
87
+ break;
88
+ }
89
+ }
90
+ return {
91
+ sentBytes,
92
+ canSendMore
93
+ };
94
+ }
95
+ /**
96
+ * Send a reset message to the remote muxer
97
+ */
98
+ async sendReset() {
99
+ this.sendFrame({
100
+ type: FrameType.WindowUpdate,
101
+ flag: Flag.RST,
102
+ streamID: this.streamId,
103
+ length: 0
104
+ });
105
+ }
106
+ /**
107
+ * Send a message to the remote muxer, informing them no more data messages
108
+ * will be sent by this end of the stream
109
+ */
110
+ async sendCloseWrite() {
111
+ const flags = this.getSendFlags() | Flag.FIN;
112
+ this.sendFrame({
113
+ type: FrameType.WindowUpdate,
114
+ flag: flags,
115
+ streamID: this.streamId,
116
+ length: 0
117
+ });
118
+ }
119
+ /**
120
+ * Send a message to the remote muxer, informing them no more data messages
121
+ * will be read by this end of the stream - this is a no-op on Yamux streams
122
+ */
123
+ async sendCloseRead(options) {
124
+ options?.signal?.throwIfAborted();
125
+ }
126
+ /**
127
+ * Stop sending window updates temporarily - in the interim the the remote
128
+ * send window will exhaust and the remote will stop sending data
129
+ */
130
+ sendPause() {
131
+ this.state = StreamState.Paused;
132
+ }
133
+ /**
134
+ * Start sending window updates as normal
135
+ */
136
+ sendResume() {
137
+ this.state = StreamState.Established;
138
+ this.sendWindowUpdate();
139
+ }
140
+ /**
141
+ * handleWindowUpdate is called when the stream receives a window update frame
142
+ */
143
+ handleWindowUpdate(frame) {
144
+ this.processFlags(frame.header.flag);
145
+ // increase send window
146
+ this.sendWindowCapacity += frame.header.length;
147
+ // change the chunk size the superclass uses
148
+ this.maxMessageSize = this.sendWindowCapacity - HEADER_LENGTH;
149
+ if (this.maxMessageSize < 0) {
150
+ this.maxMessageSize = 0;
151
+ }
152
+ if (this.maxMessageSize === 0) {
153
+ return;
154
+ }
155
+ // if writing is paused and the update increases our send window, notify
156
+ // writers that writing can resume
157
+ if (this.writeBuffer.byteLength > 0) {
158
+ this.log?.trace('window update of %d bytes allows more data to be sent, have %d bytes queued, sending data %s', frame.header.length, this.writeBuffer.byteLength, this.sendingData);
159
+ this.safeDispatchEvent('drain');
160
+ }
161
+ }
162
+ /**
163
+ * handleData is called when the stream receives a data frame
164
+ */
165
+ handleData(frame) {
166
+ if (!isDataFrame(frame)) {
167
+ throw new InvalidFrameError('Frame was not data frame');
168
+ }
169
+ this.processFlags(frame.header.flag);
170
+ // check that our recv window is not exceeded
171
+ if (this.recvWindowCapacity < frame.header.length) {
172
+ throw new ReceiveWindowExceededError('Receive window exceeded');
173
+ }
174
+ this.recvWindowCapacity -= frame.header.length;
175
+ this.onData(frame.data);
176
+ this.sendWindowUpdate();
177
+ }
178
+ /**
179
+ * processFlags is used to update the state of the stream based on set flags, if any.
180
+ */
181
+ processFlags(flags) {
182
+ if ((flags & Flag.ACK) === Flag.ACK) {
183
+ if (this.state === StreamState.SYNSent) {
184
+ this.state = StreamState.Established;
185
+ }
186
+ }
187
+ if ((flags & Flag.FIN) === Flag.FIN) {
188
+ this.onRemoteCloseWrite();
189
+ }
190
+ if ((flags & Flag.RST) === Flag.RST) {
191
+ this.onRemoteReset();
192
+ }
193
+ }
194
+ /**
195
+ * getSendFlags determines any flags that are appropriate
196
+ * based on the current stream state.
197
+ *
198
+ * The state is updated as a side-effect.
199
+ */
200
+ getSendFlags() {
201
+ switch (this.state) {
202
+ case StreamState.Init:
203
+ this.state = StreamState.SYNSent;
204
+ return Flag.SYN;
205
+ case StreamState.SYNReceived:
206
+ this.state = StreamState.Established;
207
+ return Flag.ACK;
208
+ default:
209
+ return 0;
210
+ }
211
+ }
212
+ /**
213
+ * Potentially sends a window update enabling further remote writes to take
214
+ * place.
215
+ */
216
+ sendWindowUpdate() {
217
+ if (this.state === StreamState.Paused) {
218
+ // we don't want any more data from the remote right now - update the
219
+ // epoch start as otherwise when we unpause we'd be looking at the epoch
220
+ // start from before we were paused
221
+ this.epochStart = Date.now();
222
+ return;
223
+ }
224
+ // determine the flags if any
225
+ const flags = this.getSendFlags();
226
+ // If the stream has already been established
227
+ // and we've processed data within the time it takes for 4 round trips
228
+ // then we (up to) double the recvWindow
229
+ const now = Date.now();
230
+ const rtt = this.getRTT();
231
+ if (flags === 0 && rtt > -1 && (now - this.epochStart) <= (rtt * 4)) {
232
+ // we've already validated that maxStreamWindowSize can't be more than MAX_UINT32
233
+ this.recvWindow = Math.min(this.recvWindow * 2, this.maxStreamWindowSize);
234
+ }
235
+ if (this.recvWindowCapacity >= this.recvWindow && flags === 0) {
236
+ // a window update isn't needed
237
+ return;
238
+ }
239
+ // update the receive window
240
+ const delta = this.recvWindow - this.recvWindowCapacity;
241
+ this.recvWindowCapacity = this.recvWindow;
242
+ // update the epoch start
243
+ this.epochStart = now;
244
+ // send window update
245
+ this.sendFrame({
246
+ type: FrameType.WindowUpdate,
247
+ flag: flags,
248
+ streamID: this.streamId,
249
+ length: delta
250
+ });
251
+ }
252
+ }
253
+ //# 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,95 @@
1
+ {
2
+ "name": "@libp2p/yamux",
3
+ "version": "7.0.4-a02cb0461",
4
+ "description": "Yamux stream multiplexer for libp2p",
5
+ "license": "Apache-2.0 OR MIT",
6
+ "homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/stream-multiplexer-yamux#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/libp2p/js-libp2p.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/libp2p/js-libp2p/issues"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public",
16
+ "provenance": true
17
+ },
18
+ "keywords": [
19
+ "IPFS",
20
+ "libp2p",
21
+ "multiplexer",
22
+ "muxer",
23
+ "stream"
24
+ ],
25
+ "type": "module",
26
+ "types": "./dist/src/index.d.ts",
27
+ "typesVersions": {
28
+ "*": {
29
+ "*": [
30
+ "*",
31
+ "dist/*",
32
+ "dist/src/*",
33
+ "dist/src/*/index"
34
+ ],
35
+ "src/*": [
36
+ "*",
37
+ "dist/*",
38
+ "dist/src/*",
39
+ "dist/src/*/index"
40
+ ]
41
+ }
42
+ },
43
+ "files": [
44
+ "src",
45
+ "dist",
46
+ "!dist/test",
47
+ "!**/*.tsbuildinfo"
48
+ ],
49
+ "exports": {
50
+ ".": {
51
+ "types": "./dist/src/index.d.ts",
52
+ "import": "./dist/src/index.js"
53
+ },
54
+ "./config": {
55
+ "types": "./dist/src/config.d.ts",
56
+ "import": "./dist/src/config.js"
57
+ },
58
+ "./stream": {
59
+ "types": "./dist/src/stream.d.ts",
60
+ "import": "./dist/src/stream.js"
61
+ }
62
+ },
63
+ "scripts": {
64
+ "clean": "aegir clean",
65
+ "lint": "aegir lint",
66
+ "dep-check": "aegir dep-check",
67
+ "doc-check": "aegir doc-check",
68
+ "benchmark": "benchmark dist/test/bench/*.bench.js --timeout 400000",
69
+ "build": "aegir build",
70
+ "test": "aegir test",
71
+ "test:chrome": "aegir test -t browser",
72
+ "test:chrome-webworker": "aegir test -t webworker",
73
+ "test:firefox": "aegir test -t browser -- --browser firefox",
74
+ "test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
75
+ "test:node": "aegir test -t node --cov",
76
+ "test:electron-main": "aegir test -t electron-main",
77
+ "docs": "aegir docs"
78
+ },
79
+ "dependencies": {
80
+ "@libp2p/interface": "2.11.0-a02cb0461",
81
+ "@libp2p/utils": "6.7.2-a02cb0461",
82
+ "race-signal": "^2.0.0",
83
+ "uint8arraylist": "^2.4.8"
84
+ },
85
+ "devDependencies": {
86
+ "@dapplion/benchmark": "^1.0.0",
87
+ "@libp2p/interface-compliance-tests": "6.5.0-a02cb0461",
88
+ "@libp2p/mplex": "11.0.47-a02cb0461",
89
+ "aegir": "^47.0.21",
90
+ "it-all": "^3.0.9",
91
+ "it-drain": "^3.0.10",
92
+ "it-pushable": "^3.2.3",
93
+ "p-event": "^6.0.1"
94
+ }
95
+ }
package/src/config.ts ADDED
@@ -0,0 +1,72 @@
1
+ import { InvalidParametersError } from '@libp2p/interface'
2
+ import { INITIAL_STREAM_WINDOW, MAX_STREAM_WINDOW } from './constants.js'
3
+ import type { StreamMuxerOptions, StreamOptions } from '@libp2p/interface'
4
+
5
+ export interface YamuxStreamOptions extends StreamOptions {
6
+ /**
7
+ * Used to control the initial window size that we allow for a stream.
8
+ *
9
+ * measured in bytes
10
+ */
11
+ initialStreamWindowSize?: number
12
+
13
+ /**
14
+ * Used to control the maximum window size that we allow for a stream.
15
+ */
16
+ maxStreamWindowSize?: number
17
+ }
18
+
19
+ // TODO use config items or delete them
20
+ export interface Config extends StreamMuxerOptions<YamuxStreamOptions> {
21
+ /**
22
+ * Used to do periodic keep alive messages using a ping
23
+ */
24
+ enableKeepAlive?: boolean
25
+
26
+ /**
27
+ * How often to perform the keep alive
28
+ *
29
+ * measured in milliseconds
30
+ */
31
+ keepAliveInterval?: number
32
+ }
33
+
34
+ export const defaultConfig: Required<Config> & { streamOptions: Required<YamuxStreamOptions> } = {
35
+ enableKeepAlive: true,
36
+ keepAliveInterval: 30_000,
37
+ maxInboundStreams: 1_000,
38
+ maxOutboundStreams: 1_000,
39
+ maxMessageSize: 64 * 1024,
40
+ maxEarlyStreams: 10,
41
+ streamOptions: {
42
+ initialStreamWindowSize: INITIAL_STREAM_WINDOW,
43
+ maxStreamWindowSize: MAX_STREAM_WINDOW,
44
+ inactivityTimeout: 120_000,
45
+ maxReadBufferLength: 4_194_304,
46
+ maxWriteBufferLength: Infinity
47
+ }
48
+ }
49
+
50
+ export function verifyConfig (config: Config): void {
51
+ if (config.keepAliveInterval != null && config.keepAliveInterval <= 0) {
52
+ throw new InvalidParametersError('keep-alive interval must be positive')
53
+ }
54
+ if (config.maxInboundStreams != null && config.maxInboundStreams < 0) {
55
+ throw new InvalidParametersError('max inbound streams must be larger or equal 0')
56
+ }
57
+ if (config.maxOutboundStreams != null && config.maxOutboundStreams < 0) {
58
+ throw new InvalidParametersError('max outbound streams must be larger or equal 0')
59
+ }
60
+ if (config.maxMessageSize != null && config.maxMessageSize < 1024) {
61
+ throw new InvalidParametersError('MaxMessageSize must be greater than a kilobyte')
62
+ }
63
+ if (config.streamOptions?.initialStreamWindowSize != null && config.streamOptions?.initialStreamWindowSize < INITIAL_STREAM_WINDOW) {
64
+ throw new InvalidParametersError('InitialStreamWindowSize must be larger or equal 256 kB')
65
+ }
66
+ if (config.streamOptions?.maxStreamWindowSize != null && config.streamOptions?.initialStreamWindowSize != null && config.streamOptions?.maxStreamWindowSize < config.streamOptions?.initialStreamWindowSize) {
67
+ throw new InvalidParametersError('MaxStreamWindowSize must be larger than the InitialStreamWindowSize')
68
+ }
69
+ if (config.streamOptions?.maxStreamWindowSize != null && config.streamOptions?.maxStreamWindowSize > 2 ** 32 - 1) {
70
+ throw new InvalidParametersError('MaxStreamWindowSize must be less than equal MAX_UINT32')
71
+ }
72
+ }
@@ -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,105 @@
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
+ // TODO: use sublist?
85
+ const header = decodeHeader(this.buffer.subarray(0, HEADER_LENGTH))
86
+
87
+ if (header.type === FrameType.Data) {
88
+ frameSize += header.length
89
+
90
+ if (this.buffer.byteLength < frameSize) {
91
+ // not enough data yet
92
+ return
93
+ }
94
+
95
+ const data = this.buffer.sublist(HEADER_LENGTH, frameSize)
96
+ this.buffer.consume(frameSize)
97
+
98
+ return { header, data }
99
+ }
100
+
101
+ this.buffer.consume(frameSize)
102
+
103
+ return { header }
104
+ }
105
+ }
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
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,89 @@
1
+ import { GoAwayCode } from './frame.ts'
2
+
3
+ export class ProtocolError extends Error {
4
+ static name = 'ProtocolError'
5
+
6
+ public reason: GoAwayCode
7
+
8
+ constructor (message: string, reason: GoAwayCode) {
9
+ super(message)
10
+ this.name = 'ProtocolError'
11
+ this.reason = reason
12
+ }
13
+ }
14
+
15
+ export function isProtocolError (err?: any): err is ProtocolError {
16
+ return err?.reason !== null
17
+ }
18
+
19
+ export class InvalidFrameError extends ProtocolError {
20
+ static name = 'InvalidFrameError'
21
+
22
+ constructor (message = 'The frame was invalid') {
23
+ super(message, GoAwayCode.ProtocolError)
24
+ this.name = 'InvalidFrameError'
25
+ }
26
+ }
27
+
28
+ export class UnRequestedPingError extends ProtocolError {
29
+ static name = 'UnRequestedPingError'
30
+
31
+ constructor (message = 'Un-requested ping error') {
32
+ super(message, GoAwayCode.ProtocolError)
33
+ this.name = 'UnRequestedPingError'
34
+ }
35
+ }
36
+
37
+ export class NotMatchingPingError extends ProtocolError {
38
+ static name = 'NotMatchingPingError'
39
+
40
+ constructor (message = 'Not matching ping error') {
41
+ super(message, GoAwayCode.ProtocolError)
42
+ this.name = 'NotMatchingPingError'
43
+ }
44
+ }
45
+
46
+ export class InvalidStateError extends Error {
47
+ static name = 'InvalidStateError'
48
+
49
+ constructor (message = 'Invalid state') {
50
+ super(message)
51
+ this.name = 'InvalidStateError'
52
+ }
53
+ }
54
+
55
+ export class StreamAlreadyExistsError extends ProtocolError {
56
+ static name = 'StreamAlreadyExistsError'
57
+
58
+ constructor (message = 'Stream already exists') {
59
+ super(message, GoAwayCode.ProtocolError)
60
+ this.name = 'StreamAlreadyExistsError'
61
+ }
62
+ }
63
+
64
+ export class DecodeInvalidVersionError extends ProtocolError {
65
+ static name = 'DecodeInvalidVersionError'
66
+
67
+ constructor (message = 'Decode invalid version') {
68
+ super(message, GoAwayCode.ProtocolError)
69
+ this.name = 'DecodeInvalidVersionError'
70
+ }
71
+ }
72
+
73
+ export class BothClientsError extends ProtocolError {
74
+ static name = 'BothClientsError'
75
+
76
+ constructor (message = 'Both clients') {
77
+ super(message, GoAwayCode.ProtocolError)
78
+ this.name = 'BothClientsError'
79
+ }
80
+ }
81
+
82
+ export class ReceiveWindowExceededError extends ProtocolError {
83
+ static name = 'ReceiveWindowExceededError'
84
+
85
+ constructor (message = 'Receive window exceeded') {
86
+ super(message, GoAwayCode.ProtocolError)
87
+ this.name = 'ReceiveWindowExceededError'
88
+ }
89
+ }