@libp2p/mplex 8.0.2 → 8.0.4-05abd49f

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/src/stream.ts CHANGED
@@ -1,23 +1,9 @@
1
- import { CodeError } from '@libp2p/interfaces/errors'
2
- import { logger } from '@libp2p/logger'
3
- import { abortableSource } from 'abortable-iterator'
4
- import { anySignal } from 'any-signal'
5
- import { pushable } from 'it-pushable'
1
+ import { AbstractStream, type AbstractStreamInit } from '@libp2p/interface/stream-muxer/stream'
6
2
  import { Uint8ArrayList } from 'uint8arraylist'
7
3
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
8
4
  import { MAX_MSG_SIZE } from './decode.js'
9
5
  import { InitiatorMessageTypes, ReceiverMessageTypes } from './message-types.js'
10
6
  import type { Message } from './message-types.js'
11
- import type { MplexStream } from './mplex.js'
12
- import type { StreamTimeline } from '@libp2p/interface-connection'
13
- import type { Source } from 'it-stream-types'
14
-
15
- const log = logger('libp2p:mplex:stream')
16
-
17
- const ERR_STREAM_RESET = 'ERR_STREAM_RESET'
18
- const ERR_STREAM_ABORT = 'ERR_STREAM_ABORT'
19
- const ERR_SINK_ENDED = 'ERR_SINK_ENDED'
20
- const ERR_DOUBLE_SINK = 'ERR_DOUBLE_SINK'
21
7
 
22
8
  export interface Options {
23
9
  id: number
@@ -28,226 +14,58 @@ export interface Options {
28
14
  maxMsgSize?: number
29
15
  }
30
16
 
31
- export function createStream (options: Options): MplexStream {
32
- const { id, name, send, onEnd, type = 'initiator', maxMsgSize = MAX_MSG_SIZE } = options
17
+ interface MplexStreamInit extends AbstractStreamInit {
18
+ streamId: number
19
+ name: string
20
+ send: (msg: Message) => void
21
+ }
33
22
 
34
- const abortController = new AbortController()
35
- const resetController = new AbortController()
36
- const closeController = new AbortController()
37
- const Types = type === 'initiator' ? InitiatorMessageTypes : ReceiverMessageTypes
38
- const externalId = type === 'initiator' ? (`i${id}`) : `r${id}`
39
- const streamName = `${name == null ? id : name}`
23
+ class MplexStream extends AbstractStream {
24
+ private readonly name: string
25
+ private readonly streamId: number
26
+ private readonly send: (msg: Message) => void
27
+ private readonly types: Record<string, number>
40
28
 
41
- let sourceEnded = false
42
- let sinkEnded = false
43
- let sinkSunk = false
44
- let endErr: Error | undefined
29
+ constructor (init: MplexStreamInit) {
30
+ super(init)
45
31
 
46
- const timeline: StreamTimeline = {
47
- open: Date.now()
32
+ this.types = init.direction === 'outbound' ? InitiatorMessageTypes : ReceiverMessageTypes
33
+ this.send = init.send
34
+ this.name = init.name
35
+ this.streamId = init.streamId
48
36
  }
49
37
 
50
- const onSourceEnd = (err?: Error): void => {
51
- if (sourceEnded) {
52
- return
53
- }
54
-
55
- sourceEnded = true
56
- log.trace('%s stream %s source end - err: %o', type, streamName, err)
57
-
58
- if (err != null && endErr == null) {
59
- endErr = err
60
- }
61
-
62
- if (sinkEnded) {
63
- stream.stat.timeline.close = Date.now()
64
-
65
- if (onEnd != null) {
66
- onEnd(endErr)
67
- }
68
- }
38
+ sendNewStream (): void {
39
+ this.send({ id: this.streamId, type: InitiatorMessageTypes.NEW_STREAM, data: new Uint8ArrayList(uint8ArrayFromString(this.name)) })
69
40
  }
70
41
 
71
- const onSinkEnd = (err?: Error): void => {
72
- if (sinkEnded) {
73
- return
74
- }
75
-
76
- sinkEnded = true
77
- log.trace('%s stream %s sink end - err: %o', type, streamName, err)
78
-
79
- if (err != null && endErr == null) {
80
- endErr = err
81
- }
82
-
83
- if (sourceEnded) {
84
- timeline.close = Date.now()
85
-
86
- if (onEnd != null) {
87
- onEnd(endErr)
88
- }
89
- }
42
+ sendData (data: Uint8ArrayList): void {
43
+ this.send({ id: this.streamId, type: this.types.MESSAGE, data })
90
44
  }
91
45
 
92
- const streamSource = pushable<Uint8ArrayList>({
93
- onEnd: onSourceEnd
94
- })
95
-
96
- const stream: MplexStream = {
97
- // Close for both Reading and Writing
98
- close: () => {
99
- log.trace('%s stream %s close', type, streamName)
100
-
101
- stream.closeRead()
102
- stream.closeWrite()
103
- },
104
-
105
- // Close for reading
106
- closeRead: () => {
107
- log.trace('%s stream %s closeRead', type, streamName)
108
-
109
- if (sourceEnded) {
110
- return
111
- }
112
-
113
- streamSource.end()
114
- },
115
-
116
- // Close for writing
117
- closeWrite: () => {
118
- log.trace('%s stream %s closeWrite', type, streamName)
119
-
120
- if (sinkEnded) {
121
- return
122
- }
123
-
124
- closeController.abort()
125
-
126
- try {
127
- send({ id, type: Types.CLOSE })
128
- } catch (err) {
129
- log.trace('%s stream %s error sending close', type, name, err)
130
- }
131
-
132
- onSinkEnd()
133
- },
134
-
135
- // Close for reading and writing (local error)
136
- abort: (err: Error) => {
137
- log.trace('%s stream %s abort', type, streamName, err)
138
- // End the source with the passed error
139
- streamSource.end(err)
140
- abortController.abort()
141
- onSinkEnd(err)
142
- },
143
-
144
- // Close immediately for reading and writing (remote error)
145
- reset: () => {
146
- const err = new CodeError('stream reset', ERR_STREAM_RESET)
147
- resetController.abort()
148
- streamSource.end(err)
149
- onSinkEnd(err)
150
- },
151
-
152
- sink: async (source: Source<Uint8ArrayList | Uint8Array>) => {
153
- if (sinkSunk) {
154
- throw new CodeError('sink already called on stream', ERR_DOUBLE_SINK)
155
- }
156
-
157
- sinkSunk = true
158
-
159
- if (sinkEnded) {
160
- throw new CodeError('stream closed for writing', ERR_SINK_ENDED)
161
- }
162
-
163
- const signal = anySignal([
164
- abortController.signal,
165
- resetController.signal,
166
- closeController.signal
167
- ])
168
-
169
- try {
170
- source = abortableSource(source, signal)
171
-
172
- if (type === 'initiator') { // If initiator, open a new stream
173
- send({ id, type: InitiatorMessageTypes.NEW_STREAM, data: new Uint8ArrayList(uint8ArrayFromString(streamName)) })
174
- }
175
-
176
- for await (let data of source) {
177
- while (data.length > 0) {
178
- if (data.length <= maxMsgSize) {
179
- send({ id, type: Types.MESSAGE, data: data instanceof Uint8Array ? new Uint8ArrayList(data) : data })
180
- break
181
- }
182
- data = data instanceof Uint8Array ? new Uint8ArrayList(data) : data
183
- send({ id, type: Types.MESSAGE, data: data.sublist(0, maxMsgSize) })
184
- data.consume(maxMsgSize)
185
- }
186
- }
187
- } catch (err: any) {
188
- if (err.type === 'aborted' && err.message === 'The operation was aborted') {
189
- if (closeController.signal.aborted) {
190
- return
191
- }
192
-
193
- if (resetController.signal.aborted) {
194
- err.message = 'stream reset'
195
- err.code = ERR_STREAM_RESET
196
- }
197
-
198
- if (abortController.signal.aborted) {
199
- err.message = 'stream aborted'
200
- err.code = ERR_STREAM_ABORT
201
- }
202
- }
203
-
204
- // Send no more data if this stream was remotely reset
205
- if (err.code === ERR_STREAM_RESET) {
206
- log.trace('%s stream %s reset', type, name)
207
- } else {
208
- log.trace('%s stream %s error', type, name, err)
209
- try {
210
- send({ id, type: Types.RESET })
211
- } catch (err) {
212
- log.trace('%s stream %s error sending reset', type, name, err)
213
- }
214
- }
215
-
216
- streamSource.end(err)
217
- onSinkEnd(err)
218
- return
219
- } finally {
220
- signal.clear()
221
- }
222
-
223
- try {
224
- send({ id, type: Types.CLOSE })
225
- } catch (err) {
226
- log.trace('%s stream %s error sending close', type, name, err)
227
- }
228
-
229
- onSinkEnd()
230
- },
231
-
232
- source: streamSource,
233
-
234
- sourcePush: (data: Uint8ArrayList) => {
235
- streamSource.push(data)
236
- },
237
-
238
- sourceReadableLength () {
239
- return streamSource.readableLength
240
- },
241
-
242
- stat: {
243
- direction: type === 'initiator' ? 'outbound' : 'inbound',
244
- timeline
245
- },
46
+ sendReset (): void {
47
+ this.send({ id: this.streamId, type: this.types.RESET })
48
+ }
246
49
 
247
- metadata: {},
50
+ sendCloseWrite (): void {
51
+ this.send({ id: this.streamId, type: this.types.CLOSE })
52
+ }
248
53
 
249
- id: externalId
54
+ sendCloseRead (): void {
55
+ // mplex does not support close read, only close write
250
56
  }
57
+ }
58
+
59
+ export function createStream (options: Options): MplexStream {
60
+ const { id, name, send, onEnd, type = 'initiator', maxMsgSize = MAX_MSG_SIZE } = options
251
61
 
252
- return stream
62
+ return new MplexStream({
63
+ id: type === 'initiator' ? (`i${id}`) : `r${id}`,
64
+ streamId: id,
65
+ name: `${name == null ? id : name}`,
66
+ direction: type === 'initiator' ? 'outbound' : 'inbound',
67
+ maxDataSize: maxMsgSize,
68
+ onEnd,
69
+ send
70
+ })
253
71
  }
@@ -1,4 +0,0 @@
1
- {
2
- "MplexInit": "https://libp2p.github.io/js-libp2p-mplex/interfaces/MplexInit.html",
3
- "mplex": "https://libp2p.github.io/js-libp2p-mplex/functions/mplex.html"
4
- }