@libp2p/utils 6.7.2 → 7.0.0-55b7e5fea
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 +16 -1
- package/dist/index.min.js +7 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/abstract-message-stream.d.ts +129 -0
- package/dist/src/abstract-message-stream.d.ts.map +1 -0
- package/dist/src/abstract-message-stream.js +393 -0
- package/dist/src/abstract-message-stream.js.map +1 -0
- package/dist/src/abstract-multiaddr-connection.d.ts +26 -0
- package/dist/src/abstract-multiaddr-connection.d.ts.map +1 -0
- package/dist/src/abstract-multiaddr-connection.js +66 -0
- package/dist/src/abstract-multiaddr-connection.js.map +1 -0
- package/dist/src/abstract-stream-muxer.d.ts +53 -0
- package/dist/src/abstract-stream-muxer.d.ts.map +1 -0
- package/dist/src/abstract-stream-muxer.js +169 -0
- package/dist/src/abstract-stream-muxer.js.map +1 -0
- package/dist/src/abstract-stream.d.ts +14 -130
- package/dist/src/abstract-stream.d.ts.map +1 -1
- package/dist/src/abstract-stream.js +39 -321
- package/dist/src/abstract-stream.js.map +1 -1
- package/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +8 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/get-thin-waist-addresses.browser.d.ts +1 -1
- package/dist/src/get-thin-waist-addresses.browser.d.ts.map +1 -1
- package/dist/src/get-thin-waist-addresses.browser.js +4 -3
- package/dist/src/get-thin-waist-addresses.browser.js.map +1 -1
- package/dist/src/get-thin-waist-addresses.d.ts +1 -1
- package/dist/src/get-thin-waist-addresses.d.ts.map +1 -1
- package/dist/src/get-thin-waist-addresses.js +7 -9
- package/dist/src/get-thin-waist-addresses.js.map +1 -1
- package/dist/src/index.d.ts +31 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +31 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/length-prefixed-decoder.d.ts +37 -0
- package/dist/src/length-prefixed-decoder.d.ts.map +1 -0
- package/dist/src/length-prefixed-decoder.js +64 -0
- package/dist/src/length-prefixed-decoder.js.map +1 -0
- package/dist/src/message-queue.d.ts +61 -0
- package/dist/src/message-queue.d.ts.map +1 -0
- package/dist/src/message-queue.js +93 -0
- package/dist/src/message-queue.js.map +1 -0
- package/dist/src/mock-muxer.d.ts +57 -0
- package/dist/src/mock-muxer.d.ts.map +1 -0
- package/dist/src/mock-muxer.js +204 -0
- package/dist/src/mock-muxer.js.map +1 -0
- package/dist/src/mock-stream.d.ts +31 -0
- package/dist/src/mock-stream.d.ts.map +1 -0
- package/dist/src/mock-stream.js +69 -0
- package/dist/src/mock-stream.js.map +1 -0
- package/dist/src/multiaddr/get-net-config.d.ts +55 -0
- package/dist/src/multiaddr/get-net-config.d.ts.map +1 -0
- package/dist/src/multiaddr/get-net-config.js +54 -0
- package/dist/src/multiaddr/get-net-config.js.map +1 -0
- package/dist/src/multiaddr/index.d.ts +7 -0
- package/dist/src/multiaddr/index.d.ts.map +1 -0
- package/dist/src/multiaddr/index.js +7 -0
- package/dist/src/multiaddr/index.js.map +1 -0
- package/dist/src/multiaddr/is-global-unicast.d.ts.map +1 -1
- package/dist/src/multiaddr/is-global-unicast.js +8 -9
- package/dist/src/multiaddr/is-global-unicast.js.map +1 -1
- package/dist/src/multiaddr/is-link-local.d.ts.map +1 -1
- package/dist/src/multiaddr/is-link-local.js +11 -16
- package/dist/src/multiaddr/is-link-local.js.map +1 -1
- package/dist/src/multiaddr/is-loopback.d.ts.map +1 -1
- package/dist/src/multiaddr/is-loopback.js +12 -5
- package/dist/src/multiaddr/is-loopback.js.map +1 -1
- package/dist/src/multiaddr/is-network-address.d.ts.map +1 -1
- package/dist/src/multiaddr/is-network-address.js +4 -16
- package/dist/src/multiaddr/is-network-address.js.map +1 -1
- package/dist/src/multiaddr/is-private.d.ts.map +1 -1
- package/dist/src/multiaddr/is-private.js +9 -10
- package/dist/src/multiaddr/is-private.js.map +1 -1
- package/dist/src/multiaddr/utils.d.ts +5 -0
- package/dist/src/multiaddr/utils.d.ts.map +1 -0
- package/dist/src/multiaddr/utils.js +32 -0
- package/dist/src/multiaddr/utils.js.map +1 -0
- package/dist/src/multiaddr-connection-pair.d.ts +25 -0
- package/dist/src/multiaddr-connection-pair.d.ts.map +1 -0
- package/dist/src/multiaddr-connection-pair.js +103 -0
- package/dist/src/multiaddr-connection-pair.js.map +1 -0
- package/dist/src/queue/index.d.ts +3 -6
- package/dist/src/queue/index.d.ts.map +1 -1
- package/dist/src/queue/index.js +20 -4
- package/dist/src/queue/index.js.map +1 -1
- package/dist/src/rate-limiter.d.ts +1 -15
- package/dist/src/rate-limiter.d.ts.map +1 -1
- package/dist/src/rate-limiter.js +1 -14
- package/dist/src/rate-limiter.js.map +1 -1
- package/dist/src/stream-pair.d.ts +42 -0
- package/dist/src/stream-pair.d.ts.map +1 -0
- package/dist/src/stream-pair.js +40 -0
- package/dist/src/stream-pair.js.map +1 -0
- package/dist/src/stream-utils.d.ts +191 -0
- package/dist/src/stream-utils.d.ts.map +1 -0
- package/dist/src/stream-utils.js +371 -0
- package/dist/src/stream-utils.js.map +1 -0
- package/package.json +15 -162
- package/src/abstract-message-stream.ts +553 -0
- package/src/abstract-multiaddr-connection.ts +93 -0
- package/src/abstract-stream-muxer.ts +239 -0
- package/src/abstract-stream.ts +51 -464
- package/src/errors.ts +10 -0
- package/src/get-thin-waist-addresses.browser.ts +5 -4
- package/src/get-thin-waist-addresses.ts +8 -12
- package/src/index.ts +31 -1
- package/src/length-prefixed-decoder.ts +98 -0
- package/src/message-queue.ts +156 -0
- package/src/mock-muxer.ts +304 -0
- package/src/mock-stream.ts +101 -0
- package/src/multiaddr/get-net-config.ts +112 -0
- package/src/multiaddr/index.ts +6 -0
- package/src/multiaddr/is-global-unicast.ts +8 -11
- package/src/multiaddr/is-link-local.ts +11 -20
- package/src/multiaddr/is-loopback.ts +12 -7
- package/src/multiaddr/is-network-address.ts +4 -19
- package/src/multiaddr/is-private.ts +9 -14
- package/src/multiaddr/utils.ts +46 -0
- package/src/multiaddr-connection-pair.ts +147 -0
- package/src/queue/index.ts +24 -11
- package/src/rate-limiter.ts +3 -30
- package/src/stream-pair.ts +90 -0
- package/src/stream-utils.ts +866 -0
- package/dist/src/abort-options.d.ts +0 -7
- package/dist/src/abort-options.d.ts.map +0 -1
- package/dist/src/abort-options.js +0 -14
- package/dist/src/abort-options.js.map +0 -1
- package/dist/src/array-equals.d.ts +0 -24
- package/dist/src/array-equals.d.ts.map +0 -1
- package/dist/src/array-equals.js +0 -31
- package/dist/src/array-equals.js.map +0 -1
- package/dist/src/close-source.d.ts +0 -4
- package/dist/src/close-source.d.ts.map +0 -1
- package/dist/src/close-source.js +0 -11
- package/dist/src/close-source.js.map +0 -1
- package/dist/src/close.d.ts +0 -21
- package/dist/src/close.d.ts.map +0 -1
- package/dist/src/close.js +0 -49
- package/dist/src/close.js.map +0 -1
- package/dist/src/merge-options.d.ts +0 -7
- package/dist/src/merge-options.d.ts.map +0 -1
- package/dist/src/merge-options.js +0 -128
- package/dist/src/merge-options.js.map +0 -1
- package/dist/src/multiaddr/is-ip-based.d.ts +0 -6
- package/dist/src/multiaddr/is-ip-based.d.ts.map +0 -1
- package/dist/src/multiaddr/is-ip-based.js +0 -18
- package/dist/src/multiaddr/is-ip-based.js.map +0 -1
- package/dist/src/stream-to-ma-conn.d.ts +0 -23
- package/dist/src/stream-to-ma-conn.d.ts.map +0 -1
- package/dist/src/stream-to-ma-conn.js +0 -75
- package/dist/src/stream-to-ma-conn.js.map +0 -1
- package/dist/typedoc-urls.json +0 -147
- package/src/abort-options.ts +0 -20
- package/src/array-equals.ts +0 -34
- package/src/close-source.ts +0 -14
- package/src/close.ts +0 -65
- package/src/merge-options.ts +0 -161
- package/src/multiaddr/is-ip-based.ts +0 -21
- package/src/stream-to-ma-conn.ts +0 -106
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { InvalidParametersError } from '@libp2p/interface'
|
|
2
|
+
import * as varint from 'uint8-varint'
|
|
3
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
4
|
+
import { InvalidMessageLengthError } from './stream-utils.ts'
|
|
5
|
+
|
|
6
|
+
const DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 * 4
|
|
7
|
+
const DEFAULT_MAX_DATA_LENGTH = 1024 * 1024 * 4
|
|
8
|
+
|
|
9
|
+
export interface LengthPrefixedDecoderInit {
|
|
10
|
+
/**
|
|
11
|
+
* How large the internal buffer is allowed to grow - attempting to store more
|
|
12
|
+
* data than this will throw
|
|
13
|
+
*/
|
|
14
|
+
maxBufferSize?: number
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Throw an error if the message that would be read from the buffer is larger
|
|
18
|
+
* than this value
|
|
19
|
+
*/
|
|
20
|
+
maxDataLength?: number
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Read a varint from the buffer
|
|
24
|
+
*/
|
|
25
|
+
lengthDecoder?(data: Uint8ArrayList | Uint8Array): number
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Return how many bytes it takes to encode the passed value
|
|
29
|
+
*/
|
|
30
|
+
encodingLength?(length: number): number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Decode length-prefixed data from a buffer
|
|
35
|
+
*/
|
|
36
|
+
export class LengthPrefixedDecoder {
|
|
37
|
+
private readonly buffer: Uint8ArrayList
|
|
38
|
+
private readonly maxBufferSize: number
|
|
39
|
+
private readonly lengthDecoder: (data: Uint8ArrayList | Uint8Array) => number
|
|
40
|
+
private readonly maxDataLength: number
|
|
41
|
+
private readonly encodingLength: (length: number) => number
|
|
42
|
+
|
|
43
|
+
constructor (init: LengthPrefixedDecoderInit = {}) {
|
|
44
|
+
this.buffer = new Uint8ArrayList()
|
|
45
|
+
this.maxBufferSize = init.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE
|
|
46
|
+
this.maxDataLength = init.maxDataLength ?? DEFAULT_MAX_DATA_LENGTH
|
|
47
|
+
this.lengthDecoder = init.lengthDecoder ?? varint.decode
|
|
48
|
+
this.encodingLength = init.encodingLength ?? varint.encodingLength
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Decodes length-prefixed data
|
|
53
|
+
*/
|
|
54
|
+
* decode (buf: Uint8Array | Uint8ArrayList): Generator<Uint8ArrayList> {
|
|
55
|
+
this.buffer.append(buf)
|
|
56
|
+
|
|
57
|
+
if (this.buffer.byteLength > this.maxBufferSize) {
|
|
58
|
+
throw new InvalidParametersError(`Buffer length limit exceeded - ${this.buffer.byteLength}/${this.maxBufferSize}`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Loop to consume as many bytes from the buffer as possible
|
|
62
|
+
// Eg: when a single chunk contains several frames
|
|
63
|
+
while (true) {
|
|
64
|
+
let dataLength: number
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
dataLength = this.lengthDecoder(this.buffer)
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (err instanceof RangeError) {
|
|
70
|
+
// ignore errors where we don't have enough data to read the length
|
|
71
|
+
// prefix
|
|
72
|
+
break
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
throw err
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (dataLength < 0 || dataLength > this.maxDataLength) {
|
|
79
|
+
throw new InvalidMessageLengthError('Invalid message length')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const lengthLength = this.encodingLength(dataLength)
|
|
83
|
+
const chunkLength = lengthLength + dataLength
|
|
84
|
+
|
|
85
|
+
if (this.buffer.byteLength >= chunkLength) {
|
|
86
|
+
const buf = this.buffer.sublist(lengthLength, chunkLength)
|
|
87
|
+
|
|
88
|
+
this.buffer.consume(chunkLength)
|
|
89
|
+
|
|
90
|
+
if (buf.byteLength > 0) {
|
|
91
|
+
yield buf
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { StreamMessageEvent } from '@libp2p/interface'
|
|
2
|
+
import delay from 'delay'
|
|
3
|
+
import { TypedEventEmitter } from 'main-event'
|
|
4
|
+
import { raceSignal } from 'race-signal'
|
|
5
|
+
import { isUint8ArrayList, Uint8ArrayList } from 'uint8arraylist'
|
|
6
|
+
import { Queue } from './queue/index.js'
|
|
7
|
+
import type { AbortOptions, Logger } from '@libp2p/interface'
|
|
8
|
+
|
|
9
|
+
const DEFAULT_CHUNK_SIZE = 1024 * 64
|
|
10
|
+
|
|
11
|
+
export interface MessageQueueEvents {
|
|
12
|
+
/**
|
|
13
|
+
* Message data
|
|
14
|
+
*/
|
|
15
|
+
message: StreamMessageEvent
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Emitted when the queue is empty
|
|
19
|
+
*/
|
|
20
|
+
drain: Event
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The remote closed the connection abruptly
|
|
24
|
+
*/
|
|
25
|
+
reset: Event
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface MessageQueueInit {
|
|
29
|
+
/**
|
|
30
|
+
* How much delay there should be between each message send in ms (note that
|
|
31
|
+
* even 0 introduces a small delay)
|
|
32
|
+
*
|
|
33
|
+
* @default 0
|
|
34
|
+
*/
|
|
35
|
+
delay?: number
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* How many messages to hold in the send queue before applying backpressure to
|
|
39
|
+
* the sender
|
|
40
|
+
*/
|
|
41
|
+
capacity?: number
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Data messages larger than this size will be chunked into smaller messages.
|
|
45
|
+
*
|
|
46
|
+
* Defaults to the maximum TCP package size.
|
|
47
|
+
*
|
|
48
|
+
* @default 65_536
|
|
49
|
+
*/
|
|
50
|
+
chunkSize?: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface MessageQueueJobOptions extends AbortOptions {
|
|
54
|
+
evt: Event
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Accepts events to emit after a short delay, and with a configurable maximum
|
|
59
|
+
* queue capacity after which the send method will return false to let us
|
|
60
|
+
* simulate write backpressure.
|
|
61
|
+
*/
|
|
62
|
+
export class MessageQueue<Events> extends TypedEventEmitter<Events & MessageQueueEvents> {
|
|
63
|
+
public needsDrain: boolean
|
|
64
|
+
|
|
65
|
+
private queue: Queue<void, MessageQueueJobOptions>
|
|
66
|
+
private capacity: number
|
|
67
|
+
private delay: number
|
|
68
|
+
private log: Logger
|
|
69
|
+
private chunkSize: number
|
|
70
|
+
|
|
71
|
+
constructor (init: MessageQueueInit & { log: Logger }) {
|
|
72
|
+
super()
|
|
73
|
+
|
|
74
|
+
this.needsDrain = false
|
|
75
|
+
this.queue = new Queue({
|
|
76
|
+
concurrency: 1
|
|
77
|
+
})
|
|
78
|
+
this.capacity = init.capacity ?? 5
|
|
79
|
+
this.delay = init.delay ?? 0
|
|
80
|
+
this.log = init.log
|
|
81
|
+
this.chunkSize = init.chunkSize ?? DEFAULT_CHUNK_SIZE
|
|
82
|
+
|
|
83
|
+
this.queue.addEventListener('idle', () => {
|
|
84
|
+
if (this.needsDrain) {
|
|
85
|
+
this.log('network send queue drained')
|
|
86
|
+
this.needsDrain = false
|
|
87
|
+
this.safeDispatchEvent('drain')
|
|
88
|
+
} else {
|
|
89
|
+
this.log('network send queue idle')
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
send (evt: Event): boolean {
|
|
95
|
+
if (isMessageEvent(evt)) {
|
|
96
|
+
// chunk outgoing messages to match TCP packet sizes
|
|
97
|
+
const data = new Uint8ArrayList(evt.data)
|
|
98
|
+
|
|
99
|
+
while (data.byteLength > 0) {
|
|
100
|
+
const end = Math.min(this.chunkSize, data.byteLength)
|
|
101
|
+
const chunk = data.sublist(0, end)
|
|
102
|
+
data.consume(chunk.byteLength)
|
|
103
|
+
|
|
104
|
+
const chunkEvent = new StreamMessageEvent(chunk)
|
|
105
|
+
|
|
106
|
+
this.queue.add(async (opts) => {
|
|
107
|
+
if (this.delay > 0) {
|
|
108
|
+
await raceSignal(delay(this.delay), opts.signal)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.dispatchEvent(opts.evt)
|
|
112
|
+
}, {
|
|
113
|
+
evt: chunkEvent
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
this.queue.add(async (opts) => {
|
|
118
|
+
if (this.delay > 0) {
|
|
119
|
+
await raceSignal(delay(this.delay), opts.signal)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.dispatchEvent(opts.evt)
|
|
123
|
+
}, {
|
|
124
|
+
evt
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (this.queue.size >= this.capacity) {
|
|
129
|
+
this.log('network send queue full')
|
|
130
|
+
this.needsDrain = true
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return true
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
pause (): void {
|
|
138
|
+
this.queue.pause()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
resume (): void {
|
|
142
|
+
this.queue.resume()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
onIdle (): Promise<void> {
|
|
146
|
+
return this.queue.onIdle()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
size (): number {
|
|
150
|
+
return this.queue.size
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function isMessageEvent (evt?: any): evt is StreamMessageEvent {
|
|
155
|
+
return evt?.data instanceof Uint8Array || isUint8ArrayList(evt?.data)
|
|
156
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import * as cborg from 'cborg'
|
|
2
|
+
import * as lp from 'it-length-prefixed'
|
|
3
|
+
import { pushable } from 'it-pushable'
|
|
4
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
5
|
+
import { AbstractStreamMuxer } from './abstract-stream-muxer.ts'
|
|
6
|
+
import { AbstractStream } from './abstract-stream.ts'
|
|
7
|
+
import { Queue } from './queue/index.js'
|
|
8
|
+
import type { SendResult } from './abstract-message-stream.ts'
|
|
9
|
+
import type { AbstractStreamInit } from './abstract-stream.ts'
|
|
10
|
+
import type { AbortOptions, MessageStreamDirection, CreateStreamOptions, StreamMuxerFactory, StreamMuxer, MultiaddrConnection, StreamMuxerOptions } from '@libp2p/interface'
|
|
11
|
+
import type { Pushable } from 'it-pushable'
|
|
12
|
+
import type { SupportedEncodings } from 'uint8arrays/from-string'
|
|
13
|
+
|
|
14
|
+
interface DataMessage {
|
|
15
|
+
id: string
|
|
16
|
+
type: 'data'
|
|
17
|
+
chunk: Uint8Array
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ResetMessage {
|
|
21
|
+
id: string
|
|
22
|
+
type: 'reset'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface CloseWriteMessage {
|
|
26
|
+
id: string
|
|
27
|
+
type: 'closeWrite'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface CloseReadMessage {
|
|
31
|
+
id: string
|
|
32
|
+
type: 'closeRead'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface CreateMessage {
|
|
36
|
+
id: string
|
|
37
|
+
type: 'create'
|
|
38
|
+
protocol?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface PauseMessage {
|
|
42
|
+
id: string
|
|
43
|
+
type: 'pause'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface ResumeMessage {
|
|
47
|
+
id: string
|
|
48
|
+
type: 'resume'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type StreamMessage = DataMessage | ResetMessage | CloseWriteMessage | CloseReadMessage | CreateMessage | PauseMessage | ResumeMessage
|
|
52
|
+
|
|
53
|
+
export interface MockMuxedStreamInit extends AbstractStreamInit {
|
|
54
|
+
sendMessage(message: StreamMessage): boolean
|
|
55
|
+
encoding: SupportedEncodings
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class MockMuxedStream extends AbstractStream {
|
|
59
|
+
private readonly sendMessage: (message: StreamMessage) => boolean
|
|
60
|
+
private dataQueue: Queue
|
|
61
|
+
private encoding: SupportedEncodings
|
|
62
|
+
|
|
63
|
+
constructor (init: MockMuxedStreamInit) {
|
|
64
|
+
super(init)
|
|
65
|
+
|
|
66
|
+
this.sendMessage = init.sendMessage
|
|
67
|
+
this.encoding = init.encoding
|
|
68
|
+
this.dataQueue = new Queue({
|
|
69
|
+
concurrency: 1
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
if (this.direction === 'outbound') {
|
|
73
|
+
this.sendMessage({
|
|
74
|
+
id: this.id,
|
|
75
|
+
type: 'create',
|
|
76
|
+
protocol: this.protocol
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
sendData (data: Uint8ArrayList): SendResult {
|
|
82
|
+
const canSendMore = this.sendMessage({
|
|
83
|
+
id: this.id,
|
|
84
|
+
type: 'data',
|
|
85
|
+
chunk: data.subarray()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
sentBytes: data.byteLength,
|
|
90
|
+
canSendMore
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
sendReset (): void {
|
|
95
|
+
this.sendMessage({
|
|
96
|
+
id: this.id,
|
|
97
|
+
type: 'reset'
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async sendCloseWrite (options?: AbortOptions): Promise<void> {
|
|
102
|
+
this.sendMessage({
|
|
103
|
+
id: this.id,
|
|
104
|
+
type: 'closeWrite'
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
options?.signal?.throwIfAborted()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async sendCloseRead (options?: AbortOptions): Promise<void> {
|
|
111
|
+
this.sendMessage({
|
|
112
|
+
id: this.id,
|
|
113
|
+
type: 'closeRead'
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
options?.signal?.throwIfAborted()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
sendPause (): void {
|
|
120
|
+
this.sendMessage({
|
|
121
|
+
id: this.id,
|
|
122
|
+
type: 'pause'
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
sendResume (): void {
|
|
127
|
+
this.sendMessage({
|
|
128
|
+
id: this.id,
|
|
129
|
+
type: 'resume'
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
onRemotePaused (): void {
|
|
134
|
+
this.dataQueue.pause()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
onRemoteResumed (): void {
|
|
138
|
+
this.dataQueue.resume()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
interface MockMuxerInit extends StreamMuxerOptions {
|
|
143
|
+
/**
|
|
144
|
+
* How long the input queue can grow
|
|
145
|
+
*/
|
|
146
|
+
maxInputQueueSize?: number
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* How to encode data message
|
|
150
|
+
*
|
|
151
|
+
* @default base64
|
|
152
|
+
*/
|
|
153
|
+
encoding?: SupportedEncodings
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* How large muxer messages are allowed to be
|
|
157
|
+
*/
|
|
158
|
+
maxMessageSize?: number
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// CBOR encoding of non-message data fields
|
|
162
|
+
const MESSAGE_OVERHEAD = 30
|
|
163
|
+
|
|
164
|
+
class MockMuxer extends AbstractStreamMuxer<MockMuxedStream> {
|
|
165
|
+
private input: Pushable<Uint8Array | Uint8ArrayList>
|
|
166
|
+
private maxInputQueueSize: number
|
|
167
|
+
private encoding: SupportedEncodings
|
|
168
|
+
private maxMessageSize: number
|
|
169
|
+
private nextStreamId: number
|
|
170
|
+
|
|
171
|
+
constructor (maConn: MultiaddrConnection, init: MockMuxerInit) {
|
|
172
|
+
super(maConn, {
|
|
173
|
+
...init,
|
|
174
|
+
protocol: '/mock-muxer/1.0.0',
|
|
175
|
+
name: 'mock-muxer'
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
this.maxInputQueueSize = init.maxInputQueueSize ?? 1024 * 1024 * 10
|
|
179
|
+
this.maxMessageSize = (init.maxMessageSize ?? 1024 * 1024 * 4) + MESSAGE_OVERHEAD
|
|
180
|
+
this.encoding = init.encoding ?? 'base64'
|
|
181
|
+
this.input = pushable()
|
|
182
|
+
this.sendMessage = this.sendMessage.bind(this)
|
|
183
|
+
this.nextStreamId = this.maConn.direction === 'outbound' ? 0 : 1
|
|
184
|
+
|
|
185
|
+
Promise.resolve()
|
|
186
|
+
.then(async () => {
|
|
187
|
+
for await (const buf of lp.decode(this.input, {
|
|
188
|
+
maxDataLength: this.maxMessageSize
|
|
189
|
+
})) {
|
|
190
|
+
this.onMessage(cborg.decode(buf.subarray()))
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
.catch(err => {
|
|
194
|
+
this.abort(err)
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
onData (data: Uint8Array | Uint8ArrayList): void {
|
|
199
|
+
if (this.input.readableLength >= this.maxInputQueueSize) {
|
|
200
|
+
this.abort(new Error(`Input queue exceeded maximum size ${this.input.readableLength} >= ${this.maxInputQueueSize}`))
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.input.push(data)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
sendMessage (message: StreamMessage): boolean {
|
|
208
|
+
if (message.type === 'data') {
|
|
209
|
+
this.log.trace('send message %o', { ...message, chunk: `[ ${message.chunk.byteLength} bytes ]` })
|
|
210
|
+
} else {
|
|
211
|
+
this.log.trace('send message %o', message)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const buf = cborg.encode(message)
|
|
215
|
+
const encoded = lp.encode.single(buf, {
|
|
216
|
+
maxDataLength: this.maxMessageSize
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
return this.send(encoded)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
onMessage (message: StreamMessage): void {
|
|
223
|
+
if (message.type === 'data') {
|
|
224
|
+
this.log.trace('incoming message %o', { ...message, chunk: `[ ${message.chunk.byteLength} bytes ]` })
|
|
225
|
+
} else {
|
|
226
|
+
this.log.trace('incoming message %o', message)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let stream: MockMuxedStream | undefined = this.streams.find(s => s.id === message.id)
|
|
230
|
+
|
|
231
|
+
if (message.type === 'create') {
|
|
232
|
+
if (stream != null) {
|
|
233
|
+
throw new Error(`Already had stream for ${message.id}`)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.log.trace('create stream inbound %s', message.id)
|
|
237
|
+
stream = this._createStream(message.id, 'inbound', {
|
|
238
|
+
protocol: message.protocol
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
this.onRemoteStream(stream)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (stream == null) {
|
|
245
|
+
this.log.error(`No stream found for ${message.id}`)
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (message.type === 'data') {
|
|
250
|
+
stream.onData(message.chunk)
|
|
251
|
+
} else if (message.type === 'reset') {
|
|
252
|
+
stream.onRemoteReset()
|
|
253
|
+
} else if (message.type === 'closeWrite') {
|
|
254
|
+
stream.onRemoteCloseWrite()
|
|
255
|
+
} else if (message.type === 'closeRead') {
|
|
256
|
+
stream.onRemoteCloseRead()
|
|
257
|
+
} else if (message.type === 'pause') {
|
|
258
|
+
stream.onRemotePaused()
|
|
259
|
+
} else if (message.type === 'resume') {
|
|
260
|
+
stream.onRemoteResumed()
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async onCreateStream (options: CreateStreamOptions): Promise<MockMuxedStream> {
|
|
265
|
+
this.nextStreamId += 2
|
|
266
|
+
|
|
267
|
+
return this._createStream(`${this.nextStreamId}`, 'outbound', options)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
_createStream (id: string, direction: MessageStreamDirection, options: CreateStreamOptions): MockMuxedStream {
|
|
271
|
+
this.log.trace('createStream %s %s', direction, id)
|
|
272
|
+
|
|
273
|
+
return new MockMuxedStream({
|
|
274
|
+
...this.streamOptions,
|
|
275
|
+
...options,
|
|
276
|
+
id,
|
|
277
|
+
direction,
|
|
278
|
+
log: this.log.newScope(`stream:${direction}:${id}`),
|
|
279
|
+
sendMessage: this.sendMessage,
|
|
280
|
+
encoding: this.encoding,
|
|
281
|
+
maxMessageSize: this.maxMessageSize - MESSAGE_OVERHEAD,
|
|
282
|
+
protocol: ''
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
class MockMuxerFactory implements StreamMuxerFactory {
|
|
288
|
+
public protocol: string = '/mock-muxer/1.0.0'
|
|
289
|
+
private init: MockMuxerInit
|
|
290
|
+
|
|
291
|
+
constructor (init: MockMuxerInit) {
|
|
292
|
+
this.init = init
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
createStreamMuxer (maConn: MultiaddrConnection): StreamMuxer {
|
|
296
|
+
return new MockMuxer(maConn, {
|
|
297
|
+
...this.init
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function mockMuxer (init: MockMuxerInit = {}): StreamMuxerFactory {
|
|
303
|
+
return new MockMuxerFactory(init)
|
|
304
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { StreamMessageEvent } from '@libp2p/interface'
|
|
2
|
+
import { defaultLogger } from '@libp2p/logger'
|
|
3
|
+
import { raceSignal } from 'race-signal'
|
|
4
|
+
import { AbstractStream } from './abstract-stream.ts'
|
|
5
|
+
import type { SendResult } from './abstract-message-stream.ts'
|
|
6
|
+
import type { MessageQueue, MessageQueueEvents } from './message-queue.ts'
|
|
7
|
+
import type { AbortOptions, MessageStreamDirection, TypedEventTarget } from '@libp2p/interface'
|
|
8
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
9
|
+
|
|
10
|
+
interface MockStreamMessages extends MessageQueueEvents {
|
|
11
|
+
closeWrite: Event
|
|
12
|
+
closeRead: Event
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface MockStreamInit {
|
|
16
|
+
delay?: number
|
|
17
|
+
direction: MessageStreamDirection
|
|
18
|
+
local: MessageQueue<MockStreamMessages>
|
|
19
|
+
remote: TypedEventTarget<MockStreamMessages>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let streamId = 0
|
|
23
|
+
|
|
24
|
+
export class MockStream extends AbstractStream {
|
|
25
|
+
private local: MessageQueue<MockStreamMessages>
|
|
26
|
+
private remote: TypedEventTarget<MockStreamMessages>
|
|
27
|
+
|
|
28
|
+
constructor (init: MockStreamInit) {
|
|
29
|
+
const id = `${streamId++}`
|
|
30
|
+
|
|
31
|
+
super({
|
|
32
|
+
...init,
|
|
33
|
+
id,
|
|
34
|
+
log: defaultLogger().forComponent(`libp2p:stream-pair:${init.direction}:${id}`)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
this.local = init.local
|
|
38
|
+
this.remote = init.remote
|
|
39
|
+
|
|
40
|
+
this.local.addEventListener('drain', () => {
|
|
41
|
+
this.safeDispatchEvent('drain')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
this.remote.addEventListener('message', (evt) => {
|
|
45
|
+
this.onData(evt.data)
|
|
46
|
+
})
|
|
47
|
+
this.remote.addEventListener('reset', (evt) => {
|
|
48
|
+
this.onRemoteReset()
|
|
49
|
+
})
|
|
50
|
+
this.remote.addEventListener('closeWrite', (evt) => {
|
|
51
|
+
this.onRemoteCloseWrite()
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
sendData (data: Uint8ArrayList): SendResult {
|
|
56
|
+
const canSendMore = this.local.send(new StreamMessageEvent(data))
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
sentBytes: data.byteLength,
|
|
60
|
+
canSendMore
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
sendReset (): void {
|
|
65
|
+
this.local.send(new Event('reset'))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async sendCloseWrite (options?: AbortOptions): Promise<void> {
|
|
69
|
+
return raceSignal(new Promise<void>((resolve, reject) => {
|
|
70
|
+
this.local.send(new Event('closeWrite'))
|
|
71
|
+
this.local.onIdle().then(resolve, reject)
|
|
72
|
+
}), options?.signal)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async sendCloseRead (options?: AbortOptions): Promise<void> {
|
|
76
|
+
return raceSignal(new Promise<void>((resolve, reject) => {
|
|
77
|
+
this.local.send(new Event('closeRead'))
|
|
78
|
+
this.local.onIdle().then(resolve, reject)
|
|
79
|
+
}), options?.signal)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
sendPause (): void {
|
|
83
|
+
this.local.send(new Event('pause'))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
sendResume (): void {
|
|
87
|
+
this.local.send(new Event('resume'))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
onRemotePaused (): void {
|
|
91
|
+
this.local.pause()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onRemoteResumed (): void {
|
|
95
|
+
this.local.resume()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onMuxerDrain (): void {
|
|
99
|
+
this.local.resume()
|
|
100
|
+
}
|
|
101
|
+
}
|