@libp2p/mplex 11.0.46-cf9aab5c8 → 11.0.47-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.
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/encode.d.ts +1 -2
- package/dist/src/encode.d.ts.map +1 -1
- package/dist/src/encode.js +4 -6
- package/dist/src/encode.js.map +1 -1
- package/dist/src/index.d.ts +5 -31
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/mplex.d.ts +11 -53
- package/dist/src/mplex.d.ts.map +1 -1
- package/dist/src/mplex.js +56 -222
- package/dist/src/mplex.js.map +1 -1
- package/dist/src/stream.d.ts +17 -23
- package/dist/src/stream.d.ts.map +1 -1
- package/dist/src/stream.js +56 -28
- package/dist/src/stream.js.map +1 -1
- package/package.json +12 -15
- package/src/encode.ts +5 -7
- package/src/index.ts +10 -43
- package/src/mplex.ts +58 -273
- package/src/stream.ts +78 -46
package/dist/src/encode.d.ts
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
import { Uint8ArrayList } from 'uint8arraylist';
|
2
2
|
import type { Message } from './message-types.js';
|
3
|
-
import type { Source } from 'it-stream-types';
|
4
3
|
/**
|
5
4
|
* Encode and yield one or more messages
|
6
5
|
*/
|
7
|
-
export declare function encode(
|
6
|
+
export declare function encode(message: Message): Uint8ArrayList;
|
8
7
|
//# sourceMappingURL=encode.d.ts.map
|
package/dist/src/encode.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/encode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAG/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;
|
1
|
+
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/encode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAG/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAkDjD;;GAEG;AACH,wBAAgB,MAAM,CAAE,OAAO,EAAE,OAAO,GAAG,cAAc,CAKxD"}
|
package/dist/src/encode.js
CHANGED
@@ -44,11 +44,9 @@ const encoder = new Encoder();
|
|
44
44
|
/**
|
45
45
|
* Encode and yield one or more messages
|
46
46
|
*/
|
47
|
-
export
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
yield list;
|
52
|
-
}
|
47
|
+
export function encode(message) {
|
48
|
+
const list = new Uint8ArrayList();
|
49
|
+
encoder.write(message, list);
|
50
|
+
return list;
|
53
51
|
}
|
54
52
|
//# sourceMappingURL=encode.js.map
|
package/dist/src/encode.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../../src/encode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;
|
1
|
+
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../../src/encode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,CAAA;AAE3B,MAAM,OAAO;IACH,KAAK,CAAY;IACjB,WAAW,CAAQ;IAE3B;QACE,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAA;QACnC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAA;IACtB,CAAC;IAED;;OAEG;IACH,KAAK,CAAE,GAAY,EAAE,IAAoB;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAA;QACvB,IAAI,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA;QAE7B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;QACnD,MAAM,IAAI,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAA;QAEvD,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,iBAAiB,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;YAC5J,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;YAC5C,MAAM,IAAI,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAClD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;YAC9B,MAAM,IAAI,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAA;QACpC,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;QAEtD,IAAI,SAAS,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;YAC7B,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAA;YACnC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAA;QACtB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,WAAW,GAAG,MAAM,CAAA;QAC3B,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAEnB,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,iBAAiB,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;YAC5J,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACvB,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B;;GAEG;AACH,MAAM,UAAU,MAAM,CAAE,OAAgB;IACtC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAA;IACjC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAE5B,OAAO,IAAI,CAAA;AACb,CAAC"}
|
package/dist/src/index.d.ts
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
*
|
4
4
|
* This is a [simple stream multiplexer(https://docs.libp2p.io/concepts/multiplex/mplex/) that has been deprecated.
|
5
5
|
*
|
6
|
-
* Please use [@
|
6
|
+
* Please use [@libp2p/yamux](https://www.npmjs.com/package/@libp2p/yamux) instead.
|
7
7
|
*
|
8
8
|
* @example
|
9
9
|
*
|
@@ -30,9 +30,7 @@
|
|
30
30
|
* pipe([1, 2, 3], stream)
|
31
31
|
* ```
|
32
32
|
*/
|
33
|
-
import type { MplexComponents } from './mplex.js';
|
34
33
|
import type { StreamMuxerFactory } from '@libp2p/interface';
|
35
|
-
export type { MplexComponents };
|
36
34
|
export interface MplexInit {
|
37
35
|
/**
|
38
36
|
* The maximum size of message that can be sent in one go in bytes.
|
@@ -40,42 +38,18 @@ export interface MplexInit {
|
|
40
38
|
* messages. If we receive a message larger than this an error will
|
41
39
|
* be thrown and the connection closed.
|
42
40
|
*
|
43
|
-
* @default
|
41
|
+
* @default 1_048_576
|
44
42
|
*/
|
45
|
-
|
43
|
+
maxMessageSize?: number;
|
46
44
|
/**
|
47
45
|
* Constrains the size of the unprocessed message queue buffer.
|
48
46
|
* Before messages are deserialized, the raw bytes are buffered to ensure
|
49
47
|
* we have the complete message to deserialized. If the queue gets longer
|
50
48
|
* than this value an error will be thrown and the connection closed.
|
51
49
|
*
|
52
|
-
* @default
|
50
|
+
* @default 4_194_304
|
53
51
|
*/
|
54
52
|
maxUnprocessedMessageQueueSize?: number;
|
55
|
-
/**
|
56
|
-
* The maximum number of multiplexed streams that can be open at any
|
57
|
-
* one time. A request to open more than this will have a stream
|
58
|
-
* reset message sent immediately as a response for the newly opened
|
59
|
-
* stream id
|
60
|
-
*
|
61
|
-
* @default 1024
|
62
|
-
*/
|
63
|
-
maxInboundStreams?: number;
|
64
|
-
/**
|
65
|
-
* The maximum number of multiplexed streams that can be open at any
|
66
|
-
* one time. An attempt to open more than this will throw
|
67
|
-
*
|
68
|
-
* @default 1024
|
69
|
-
*/
|
70
|
-
maxOutboundStreams?: number;
|
71
|
-
/**
|
72
|
-
* Incoming stream messages are buffered until processed by the stream
|
73
|
-
* handler. If the buffer reaches this size in bytes the stream will
|
74
|
-
* be reset
|
75
|
-
*
|
76
|
-
* @default 4194304
|
77
|
-
*/
|
78
|
-
maxStreamBufferSize?: number;
|
79
53
|
/**
|
80
54
|
* When `maxInboundStreams` is hit, if the remote continues try to open
|
81
55
|
* more than this many new multiplexed streams per second the connection
|
@@ -88,5 +62,5 @@ export interface MplexInit {
|
|
88
62
|
/**
|
89
63
|
* @deprecated mplex is deprecated as it has no flow control. Please use yamux instead.
|
90
64
|
*/
|
91
|
-
export declare function mplex(init?: MplexInit): (
|
65
|
+
export declare function mplex(init?: MplexInit): () => StreamMuxerFactory;
|
92
66
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,KAAK,
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,KAAK,EAAoC,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE7F,MAAM,WAAW,SAAS;IACxB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;;;;;;OAOG;IACH,8BAA8B,CAAC,EAAE,MAAM,CAAA;IAEvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAuBD;;GAEG;AACH,wBAAgB,KAAK,CAAE,IAAI,GAAE,SAAc,GAAG,MAAM,kBAAkB,CAErE"}
|
package/dist/src/index.js
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
*
|
4
4
|
* This is a [simple stream multiplexer(https://docs.libp2p.io/concepts/multiplex/mplex/) that has been deprecated.
|
5
5
|
*
|
6
|
-
* Please use [@
|
6
|
+
* Please use [@libp2p/yamux](https://www.npmjs.com/package/@libp2p/yamux) instead.
|
7
7
|
*
|
8
8
|
* @example
|
9
9
|
*
|
@@ -35,18 +35,15 @@ import { MplexStreamMuxer } from './mplex.js';
|
|
35
35
|
class Mplex {
|
36
36
|
protocol = '/mplex/6.7.0';
|
37
37
|
_init;
|
38
|
-
|
39
|
-
constructor(components, init = {}) {
|
40
|
-
this.components = components;
|
38
|
+
constructor(init = {}) {
|
41
39
|
this._init = init;
|
42
40
|
}
|
43
41
|
[Symbol.toStringTag] = '@libp2p/mplex';
|
44
42
|
[serviceCapabilities] = [
|
45
43
|
'@libp2p/stream-multiplexing'
|
46
44
|
];
|
47
|
-
createStreamMuxer(
|
48
|
-
return new MplexStreamMuxer(
|
49
|
-
...init,
|
45
|
+
createStreamMuxer(maConn) {
|
46
|
+
return new MplexStreamMuxer(maConn, {
|
50
47
|
...this._init
|
51
48
|
});
|
52
49
|
}
|
@@ -55,6 +52,6 @@ class Mplex {
|
|
55
52
|
* @deprecated mplex is deprecated as it has no flow control. Please use yamux instead.
|
56
53
|
*/
|
57
54
|
export function mplex(init = {}) {
|
58
|
-
return (
|
55
|
+
return () => new Mplex(init);
|
59
56
|
}
|
60
57
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAkC7C,MAAM,KAAK;IACF,QAAQ,GAAG,cAAc,CAAA;IACf,KAAK,CAAW;IAEjC,YAAa,OAAkB,EAAE;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,CAAC;IAEQ,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,eAAe,CAAA;IAEtC,CAAC,mBAAmB,CAAC,GAAa;QACzC,6BAA6B;KAC9B,CAAA;IAED,iBAAiB,CAAE,MAA2B;QAC5C,OAAO,IAAI,gBAAgB,CAAC,MAAM,EAAE;YAClC,GAAG,IAAI,CAAC,KAAK;SACd,CAAC,CAAA;IACJ,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,KAAK,CAAE,OAAkB,EAAE;IACzC,OAAO,GAAG,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAA;AAC9B,CAAC"}
|
package/dist/src/mplex.d.ts
CHANGED
@@ -1,65 +1,23 @@
|
|
1
|
+
import { AbstractStreamMuxer } from '@libp2p/utils';
|
1
2
|
import type { MplexInit } from './index.js';
|
2
3
|
import type { Message } from './message-types.js';
|
3
4
|
import type { MplexStream } from './stream.js';
|
4
|
-
import type {
|
5
|
-
import type { Sink, Source } from 'it-stream-types';
|
5
|
+
import type { CreateStreamOptions, MultiaddrConnection, MessageStreamDirection } from '@libp2p/interface';
|
6
6
|
import type { Uint8ArrayList } from 'uint8arraylist';
|
7
|
-
export
|
8
|
-
logger: ComponentLogger;
|
9
|
-
}
|
10
|
-
interface MplexStreamMuxerInit extends MplexInit, StreamMuxerInit {
|
11
|
-
/**
|
12
|
-
* The default timeout to use in ms when shutting down the muxer.
|
13
|
-
*/
|
14
|
-
closeTimeout?: number;
|
15
|
-
}
|
16
|
-
export declare class MplexStreamMuxer implements StreamMuxer {
|
17
|
-
protocol: string;
|
18
|
-
sink: Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>>;
|
19
|
-
source: AsyncGenerator<Uint8ArrayList | Uint8Array>;
|
20
|
-
private readonly log;
|
7
|
+
export declare class MplexStreamMuxer extends AbstractStreamMuxer<MplexStream> {
|
21
8
|
private _streamId;
|
22
|
-
private readonly _streams;
|
23
|
-
private readonly _init;
|
24
|
-
private readonly _source;
|
25
|
-
private readonly closeController;
|
26
9
|
private readonly rateLimiter;
|
27
|
-
private readonly
|
28
|
-
private readonly
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
*/
|
33
|
-
get streams(): Stream[];
|
10
|
+
private readonly maxMessageSize;
|
11
|
+
private readonly maxUnprocessedMessageQueueSize;
|
12
|
+
private readonly decoder;
|
13
|
+
constructor(maConn: MultiaddrConnection, init: MplexInit);
|
14
|
+
onData(data: Uint8Array | Uint8ArrayList): void;
|
34
15
|
/**
|
35
16
|
* Initiate a new stream with the given name. If no name is
|
36
17
|
* provided, the id of the stream will be used.
|
37
18
|
*/
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
*/
|
42
|
-
close(options?: AbortOptions): Promise<void>;
|
43
|
-
abort(err: Error): void;
|
44
|
-
/**
|
45
|
-
* Called whenever an inbound stream is created
|
46
|
-
*/
|
47
|
-
_newReceiverStream(options: {
|
48
|
-
id: number;
|
49
|
-
name: string;
|
50
|
-
}): MplexStream;
|
51
|
-
_newStream(options: {
|
52
|
-
id: number;
|
53
|
-
name: string;
|
54
|
-
type: 'initiator' | 'receiver';
|
55
|
-
registry: Map<number, MplexStream>;
|
56
|
-
}): MplexStream;
|
57
|
-
/**
|
58
|
-
* Creates a sink with an abortable source. Incoming messages will
|
59
|
-
* also have their size restricted. All messages will be varint decoded.
|
60
|
-
*/
|
61
|
-
_createSink(): Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>>;
|
62
|
-
_handleIncoming(message: Message): Promise<void>;
|
19
|
+
onCreateStream(options: CreateStreamOptions): MplexStream;
|
20
|
+
_newStream(id: number, direction: MessageStreamDirection, options?: CreateStreamOptions): MplexStream;
|
21
|
+
handleMessage(message: Message): void;
|
63
22
|
}
|
64
|
-
export {};
|
65
23
|
//# sourceMappingURL=mplex.d.ts.map
|
package/dist/src/mplex.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"mplex.d.ts","sourceRoot":"","sources":["../../src/mplex.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"mplex.d.ts","sourceRoot":"","sources":["../../src/mplex.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAKhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AACzG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAqBpD,qBAAa,gBAAiB,SAAQ,mBAAmB,CAAC,WAAW,CAAC;IACpE,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAQ;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAEpB,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS;IAkBzD,MAAM,CAAE,IAAI,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI;IAMhD;;;OAGG;IACH,cAAc,CAAE,OAAO,EAAE,mBAAmB,GAAG,WAAW;IAU1D,UAAU,CAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,sBAAsB,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,WAAW;IAetG,aAAa,CAAE,OAAO,EAAE,OAAO,GAAG,IAAI;CAqEvC"}
|
package/dist/src/mplex.js
CHANGED
@@ -1,257 +1,101 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import { RateLimiter } from '@libp2p/utils/rate-limiter';
|
4
|
-
import { pipe } from 'it-pipe';
|
5
|
-
import { pushable } from 'it-pushable';
|
1
|
+
import { MuxerClosedError } from '@libp2p/interface';
|
2
|
+
import { RateLimiter, AbstractStreamMuxer } from '@libp2p/utils';
|
6
3
|
import { toString as uint8ArrayToString } from 'uint8arrays';
|
7
|
-
import { Decoder } from './decode.js';
|
8
|
-
import { encode } from './encode.js';
|
9
|
-
import { StreamInputBufferError } from './errors.js';
|
4
|
+
import { Decoder, MAX_MSG_QUEUE_SIZE, MAX_MSG_SIZE } from './decode.js';
|
10
5
|
import { MessageTypes, MessageTypeNames } from './message-types.js';
|
11
6
|
import { createStream } from './stream.js';
|
12
|
-
const MAX_STREAMS_INBOUND_STREAMS_PER_CONNECTION = 1024;
|
13
|
-
const MAX_STREAMS_OUTBOUND_STREAMS_PER_CONNECTION = 1024;
|
14
|
-
const MAX_STREAM_BUFFER_SIZE = 1024 * 1024 * 4; // 4MB
|
15
7
|
const DISCONNECT_THRESHOLD = 5;
|
16
|
-
const CLOSE_TIMEOUT = 500;
|
17
8
|
function printMessage(msg) {
|
18
9
|
const output = {
|
19
10
|
...msg,
|
20
11
|
type: `${MessageTypeNames[msg.type]} (${msg.type})`
|
21
12
|
};
|
22
13
|
if (msg.type === MessageTypes.NEW_STREAM) {
|
23
|
-
output.data = uint8ArrayToString(msg.data
|
14
|
+
output.data = uint8ArrayToString(msg.data.subarray());
|
24
15
|
}
|
25
16
|
if (msg.type === MessageTypes.MESSAGE_INITIATOR || msg.type === MessageTypes.MESSAGE_RECEIVER) {
|
26
|
-
output.data = uint8ArrayToString(msg.data
|
17
|
+
output.data = uint8ArrayToString(msg.data.subarray(), 'base16');
|
27
18
|
}
|
28
19
|
return output;
|
29
20
|
}
|
30
|
-
export class MplexStreamMuxer {
|
31
|
-
protocol = '/mplex/6.7.0';
|
32
|
-
sink;
|
33
|
-
source;
|
34
|
-
log;
|
21
|
+
export class MplexStreamMuxer extends AbstractStreamMuxer {
|
35
22
|
_streamId;
|
36
|
-
_streams;
|
37
|
-
_init;
|
38
|
-
_source;
|
39
|
-
closeController;
|
40
23
|
rateLimiter;
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
/**
|
50
|
-
* Stream to ids map
|
51
|
-
*/
|
52
|
-
initiators: new Map(),
|
53
|
-
/**
|
54
|
-
* Stream to ids map
|
55
|
-
*/
|
56
|
-
receivers: new Map()
|
57
|
-
};
|
58
|
-
this._init = init;
|
59
|
-
this.closeTimeout = init.closeTimeout ?? CLOSE_TIMEOUT;
|
60
|
-
/**
|
61
|
-
* An iterable sink
|
62
|
-
*/
|
63
|
-
this.sink = this._createSink();
|
64
|
-
/**
|
65
|
-
* An iterable source
|
66
|
-
*/
|
67
|
-
this._source = pushable({
|
68
|
-
objectMode: true,
|
69
|
-
onEnd: () => {
|
70
|
-
// the source has ended, we can't write any more messages to gracefully
|
71
|
-
// close streams so all we can do is destroy them
|
72
|
-
for (const stream of this._streams.initiators.values()) {
|
73
|
-
stream.destroy();
|
74
|
-
}
|
75
|
-
for (const stream of this._streams.receivers.values()) {
|
76
|
-
stream.destroy();
|
77
|
-
}
|
78
|
-
}
|
24
|
+
maxMessageSize;
|
25
|
+
maxUnprocessedMessageQueueSize;
|
26
|
+
decoder;
|
27
|
+
constructor(maConn, init) {
|
28
|
+
super(maConn, {
|
29
|
+
...init,
|
30
|
+
protocol: '/mplex/6.7.0',
|
31
|
+
name: 'mplex'
|
79
32
|
});
|
80
|
-
this.
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
this.closeController = new AbortController();
|
33
|
+
this._streamId = 0;
|
34
|
+
this.maxMessageSize = init.maxMessageSize ?? MAX_MSG_SIZE;
|
35
|
+
this.maxUnprocessedMessageQueueSize = init.maxUnprocessedMessageQueueSize ?? MAX_MSG_QUEUE_SIZE;
|
36
|
+
this.decoder = new Decoder(this.maxMessageSize, this.maxUnprocessedMessageQueueSize);
|
85
37
|
this.rateLimiter = new RateLimiter({
|
86
38
|
points: init.disconnectThreshold ?? DISCONNECT_THRESHOLD,
|
87
39
|
duration: 1
|
88
40
|
});
|
89
41
|
}
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
get streams() {
|
94
|
-
// Inbound and Outbound streams may have the same ids, so we need to make those unique
|
95
|
-
const streams = [];
|
96
|
-
for (const stream of this._streams.initiators.values()) {
|
97
|
-
streams.push(stream);
|
42
|
+
onData(data) {
|
43
|
+
for (const msg of this.decoder.write(data)) {
|
44
|
+
this.handleMessage(msg);
|
98
45
|
}
|
99
|
-
for (const stream of this._streams.receivers.values()) {
|
100
|
-
streams.push(stream);
|
101
|
-
}
|
102
|
-
return streams;
|
103
46
|
}
|
104
47
|
/**
|
105
48
|
* Initiate a new stream with the given name. If no name is
|
106
49
|
* provided, the id of the stream will be used.
|
107
50
|
*/
|
108
|
-
|
109
|
-
if (this.
|
51
|
+
onCreateStream(options) {
|
52
|
+
if (this.status !== 'open') {
|
110
53
|
throw new MuxerClosedError('Muxer already closed');
|
111
54
|
}
|
112
55
|
const id = this._streamId++;
|
113
|
-
|
114
|
-
const registry = this._streams.initiators;
|
115
|
-
return this._newStream({ id, name, type: 'initiator', registry });
|
116
|
-
}
|
117
|
-
/**
|
118
|
-
* Close or abort all tracked streams and stop the muxer
|
119
|
-
*/
|
120
|
-
async close(options) {
|
121
|
-
if (this.closeController.signal.aborted) {
|
122
|
-
return;
|
123
|
-
}
|
124
|
-
const signal = options?.signal ?? AbortSignal.timeout(this.closeTimeout);
|
125
|
-
try {
|
126
|
-
// try to gracefully close all streams
|
127
|
-
await Promise.all(this.streams.map(async (s) => s.close({
|
128
|
-
signal
|
129
|
-
})));
|
130
|
-
this._source.end();
|
131
|
-
// try to gracefully close the muxer
|
132
|
-
await this._source.onEmpty({
|
133
|
-
signal
|
134
|
-
});
|
135
|
-
this.closeController.abort();
|
136
|
-
}
|
137
|
-
catch (err) {
|
138
|
-
this.abort(err);
|
139
|
-
}
|
140
|
-
}
|
141
|
-
abort(err) {
|
142
|
-
if (this.closeController.signal.aborted) {
|
143
|
-
return;
|
144
|
-
}
|
145
|
-
this.streams.forEach(s => { s.abort(err); });
|
146
|
-
this.closeController.abort(err);
|
147
|
-
}
|
148
|
-
/**
|
149
|
-
* Called whenever an inbound stream is created
|
150
|
-
*/
|
151
|
-
_newReceiverStream(options) {
|
152
|
-
const { id, name } = options;
|
153
|
-
const registry = this._streams.receivers;
|
154
|
-
return this._newStream({ id, name, type: 'receiver', registry });
|
56
|
+
return this._newStream(id, 'outbound', options);
|
155
57
|
}
|
156
|
-
_newStream(options) {
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
if (this.log.enabled) {
|
167
|
-
this.log.trace('%s stream %s send', type, id, printMessage(msg));
|
168
|
-
}
|
169
|
-
this._source.push(msg);
|
170
|
-
};
|
171
|
-
const onEnd = () => {
|
172
|
-
this.log('%s stream with id %s and protocol %s ended', type, id, stream.protocol);
|
173
|
-
registry.delete(id);
|
174
|
-
if (this._init.onStreamEnd != null) {
|
175
|
-
this._init.onStreamEnd(stream);
|
176
|
-
}
|
177
|
-
};
|
178
|
-
const stream = createStream({ id, name, send, type, onEnd, maxMsgSize: this._init.maxMsgSize, log: this.log });
|
179
|
-
registry.set(id, stream);
|
58
|
+
_newStream(id, direction, options) {
|
59
|
+
this.log('new %s stream %s', direction, id);
|
60
|
+
const stream = createStream({
|
61
|
+
...options,
|
62
|
+
id,
|
63
|
+
direction,
|
64
|
+
maxMsgSize: this.maxMessageSize,
|
65
|
+
log: this.log,
|
66
|
+
muxer: this
|
67
|
+
});
|
180
68
|
return stream;
|
181
69
|
}
|
182
|
-
|
183
|
-
* Creates a sink with an abortable source. Incoming messages will
|
184
|
-
* also have their size restricted. All messages will be varint decoded.
|
185
|
-
*/
|
186
|
-
_createSink() {
|
187
|
-
const sink = async (source) => {
|
188
|
-
const abortListener = () => {
|
189
|
-
closeSource(source, this.log);
|
190
|
-
};
|
191
|
-
this.closeController.signal.addEventListener('abort', abortListener);
|
192
|
-
try {
|
193
|
-
const decoder = new Decoder(this._init.maxMsgSize, this._init.maxUnprocessedMessageQueueSize);
|
194
|
-
for await (const chunk of source) {
|
195
|
-
for (const msg of decoder.write(chunk)) {
|
196
|
-
await this._handleIncoming(msg);
|
197
|
-
}
|
198
|
-
}
|
199
|
-
this._source.end();
|
200
|
-
}
|
201
|
-
catch (err) {
|
202
|
-
this.log('error in sink', err);
|
203
|
-
this._source.end(err); // End the source with an error
|
204
|
-
}
|
205
|
-
finally {
|
206
|
-
this.closeController.signal.removeEventListener('abort', abortListener);
|
207
|
-
}
|
208
|
-
};
|
209
|
-
return sink;
|
210
|
-
}
|
211
|
-
async _handleIncoming(message) {
|
212
|
-
const { id, type } = message;
|
70
|
+
handleMessage(message) {
|
213
71
|
if (this.log.enabled) {
|
214
72
|
this.log.trace('incoming message', printMessage(message));
|
215
73
|
}
|
216
74
|
// Create a new stream?
|
217
75
|
if (message.type === MessageTypes.NEW_STREAM) {
|
218
|
-
if
|
219
|
-
|
220
|
-
|
221
|
-
// instead of setting it up just to tear it down
|
222
|
-
this._source.push({
|
223
|
-
id,
|
224
|
-
type: MessageTypes.RESET_RECEIVER
|
225
|
-
});
|
226
|
-
// if we've hit our stream limit, and the remote keeps trying to open
|
227
|
-
// more new streams, if they are doing this very quickly maybe they
|
228
|
-
// are attacking us and we should close the connection
|
229
|
-
try {
|
230
|
-
await this.rateLimiter.consume('new-stream', 1);
|
231
|
-
}
|
232
|
-
catch {
|
233
|
-
this.log('rate limit hit when opening too many new streams over the inbound stream limit - closing remote connection');
|
234
|
-
// since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
|
235
|
-
this.abort(new Error('Too many open streams'));
|
236
|
-
return;
|
237
|
-
}
|
238
|
-
return;
|
76
|
+
// close the connection if the remote opens too many streams too quickly
|
77
|
+
try {
|
78
|
+
this.rateLimiter.consume('new-stream', 1);
|
239
79
|
}
|
240
|
-
|
241
|
-
|
242
|
-
|
80
|
+
catch {
|
81
|
+
this.log('rate limit hit when opening too many new streams over the inbound stream limit - closing remote connection');
|
82
|
+
// since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
|
83
|
+
this.abort(new Error('Too many open streams'));
|
84
|
+
return;
|
243
85
|
}
|
86
|
+
const stream = this._newStream(message.id, 'inbound', this.streamOptions);
|
87
|
+
this.onRemoteStream(stream);
|
244
88
|
return;
|
245
89
|
}
|
246
|
-
const
|
247
|
-
const stream =
|
90
|
+
const id = `${(message.type & 1) === 1 ? 'i' : 'r'}${message.id}`;
|
91
|
+
const stream = this.streams.find(s => s.id === id);
|
248
92
|
if (stream == null) {
|
249
|
-
this.log('missing stream %s for message type %s', id, MessageTypeNames[type]);
|
93
|
+
this.log('missing stream %s for message type %s', id, MessageTypeNames[message.type]);
|
250
94
|
// if the remote keeps sending us messages for streams that have been
|
251
95
|
// closed or were never opened they may be attacking us so if they do
|
252
96
|
// this very quickly all we can do is close the connection
|
253
97
|
try {
|
254
|
-
|
98
|
+
this.rateLimiter.consume('missing-stream', 1);
|
255
99
|
}
|
256
100
|
catch {
|
257
101
|
this.log('rate limit hit when receiving messages for streams that do not exist - closing remote connection');
|
@@ -261,39 +105,29 @@ export class MplexStreamMuxer {
|
|
261
105
|
}
|
262
106
|
return;
|
263
107
|
}
|
264
|
-
const maxBufferSize = this._init.maxStreamBufferSize ?? MAX_STREAM_BUFFER_SIZE;
|
265
108
|
try {
|
266
|
-
switch (type) {
|
109
|
+
switch (message.type) {
|
267
110
|
case MessageTypes.MESSAGE_INITIATOR:
|
268
111
|
case MessageTypes.MESSAGE_RECEIVER:
|
269
|
-
if (stream.sourceReadableLength() > maxBufferSize) {
|
270
|
-
// Stream buffer has got too large, reset the stream
|
271
|
-
this._source.push({
|
272
|
-
id: message.id,
|
273
|
-
type: type === MessageTypes.MESSAGE_INITIATOR ? MessageTypes.RESET_RECEIVER : MessageTypes.RESET_INITIATOR
|
274
|
-
});
|
275
|
-
// Inform the stream consumer they are not fast enough
|
276
|
-
throw new StreamInputBufferError('Input buffer full - increase Mplex maxBufferSize to accommodate slow consumers');
|
277
|
-
}
|
278
112
|
// We got data from the remote, push it into our local stream
|
279
|
-
stream.
|
113
|
+
stream.onData(message.data);
|
280
114
|
break;
|
281
115
|
case MessageTypes.CLOSE_INITIATOR:
|
282
116
|
case MessageTypes.CLOSE_RECEIVER:
|
283
|
-
// The remote has stopped writing
|
284
|
-
stream.
|
117
|
+
// The remote has stopped writing
|
118
|
+
stream.onRemoteCloseWrite();
|
285
119
|
break;
|
286
120
|
case MessageTypes.RESET_INITIATOR:
|
287
121
|
case MessageTypes.RESET_RECEIVER:
|
288
122
|
// The remote has errored, stop reading and writing to the stream immediately
|
289
|
-
stream.
|
123
|
+
stream.onRemoteReset();
|
290
124
|
break;
|
291
125
|
default:
|
292
|
-
this.log('unknown message type
|
126
|
+
this.log('unknown message type');
|
293
127
|
}
|
294
128
|
}
|
295
129
|
catch (err) {
|
296
|
-
this.log.error('error while processing message', err);
|
130
|
+
this.log.error('error while processing message - %e', err);
|
297
131
|
stream.abort(err);
|
298
132
|
}
|
299
133
|
}
|