@libp2p/webrtc 2.0.10-5eee70a4 → 2.0.10-69c93ac5

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/muxer.ts CHANGED
@@ -4,6 +4,7 @@ import type { DataChannelOpts } from './stream.js'
4
4
  import type { Stream } from '@libp2p/interface/connection'
5
5
  import type { CounterGroup } from '@libp2p/interface/metrics'
6
6
  import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface/stream-muxer'
7
+ import type { AbortOptions } from '@multiformats/multiaddr'
7
8
  import type { Source, Sink } from 'it-stream-types'
8
9
  import type { Uint8ArrayList } from 'uint8arraylist'
9
10
 
@@ -93,9 +94,14 @@ export class DataChannelMuxer implements StreamMuxer {
93
94
  private readonly metrics?: CounterGroup
94
95
 
95
96
  /**
96
- * Close or abort all tracked streams and stop the muxer
97
+ * Gracefully close all tracked streams and stop the muxer
97
98
  */
98
- close: (err?: Error | undefined) => void = () => { }
99
+ close: (options?: AbortOptions) => Promise<void> = async () => { }
100
+
101
+ /**
102
+ * Abort all tracked streams and stop the muxer
103
+ */
104
+ abort: (err: Error) => void = () => { }
99
105
 
100
106
  /**
101
107
  * The stream source, a no-op as the transport natively supports multiplexing
@@ -1,6 +1,6 @@
1
1
  import { logger } from '@libp2p/logger'
2
2
  import { abortableDuplex } from 'abortable-iterator'
3
- import { pbStream } from 'it-pb-stream'
3
+ import { pbStream } from 'it-protobuf-stream'
4
4
  import pDefer, { type DeferredPromise } from 'p-defer'
5
5
  import { DataChannelMuxerFactory } from '../muxer.js'
6
6
  import { Message } from './pb/message.js'
@@ -28,8 +28,8 @@ export async function handleIncomingStream ({ rtcConfiguration, dataChannelOptio
28
28
  // candidate callbacks
29
29
  pc.onicecandidate = ({ candidate }) => {
30
30
  answerSentPromise.promise.then(
31
- () => {
32
- stream.write({
31
+ async () => {
32
+ await stream.write({
33
33
  type: Message.Type.ICE_CANDIDATE,
34
34
  data: (candidate != null) ? JSON.stringify(candidate.toJSON()) : ''
35
35
  })
@@ -64,7 +64,7 @@ export async function handleIncomingStream ({ rtcConfiguration, dataChannelOptio
64
64
  throw new Error('Failed to create answer')
65
65
  })
66
66
  // write the answer to the remote
67
- stream.write({ type: Message.Type.SDP_ANSWER, data: answer.sdp })
67
+ await stream.write({ type: Message.Type.SDP_ANSWER, data: answer.sdp })
68
68
 
69
69
  await pc.setLocalDescription(answer).catch(err => {
70
70
  log.error('could not execute setLocalDescription', err)
@@ -107,15 +107,18 @@ export async function initiateConnection ({ rtcConfiguration, dataChannelOptions
107
107
  // setup callback to write ICE candidates to the remote
108
108
  // peer
109
109
  pc.onicecandidate = ({ candidate }) => {
110
- stream.write({
110
+ void stream.write({
111
111
  type: Message.Type.ICE_CANDIDATE,
112
112
  data: (candidate != null) ? JSON.stringify(candidate.toJSON()) : ''
113
113
  })
114
+ .catch(err => {
115
+ log.error('error sending ICE candidate', err)
116
+ })
114
117
  }
115
118
  // create an offer
116
119
  const offerSdp = await pc.createOffer()
117
120
  // write the offer to the stream
118
- stream.write({ type: Message.Type.SDP_OFFER, data: offerSdp.sdp })
121
+ await stream.write({ type: Message.Type.SDP_OFFER, data: offerSdp.sdp })
119
122
  // set offer as local description
120
123
  await pc.setLocalDescription(offerSdp).catch(err => {
121
124
  log.error('could not execute setLocalDescription', err)
@@ -49,6 +49,8 @@ export class WebRTCTransport implements Transport, Startable {
49
49
  async start (): Promise<void> {
50
50
  await this.components.registrar.handle(SIGNALING_PROTO_ID, (data: IncomingStreamData) => {
51
51
  this._onProtocol(data).catch(err => { log.error('failed to handle incoming connect from %p', data.connection.remotePeer, err) })
52
+ }, {
53
+ runOnTransientConnection: true
52
54
  })
53
55
  this._started = true
54
56
  }
@@ -90,7 +92,10 @@ export class WebRTCTransport implements Transport, Startable {
90
92
  }
91
93
 
92
94
  const connection = await this.components.transportManager.dial(baseAddr, options)
93
- const signalingStream = await connection.newStream([SIGNALING_PROTO_ID], options)
95
+ const signalingStream = await connection.newStream(SIGNALING_PROTO_ID, {
96
+ ...options,
97
+ runOnTransientConnection: true
98
+ })
94
99
 
95
100
  try {
96
101
  const { pc, muxerFactory, remoteAddress } = await initiateConnection({
@@ -114,11 +119,11 @@ export class WebRTCTransport implements Transport, Startable {
114
119
  )
115
120
 
116
121
  // close the stream if SDP has been exchanged successfully
117
- signalingStream.close()
122
+ await signalingStream.close()
118
123
  return result
119
- } catch (err) {
124
+ } catch (err: any) {
120
125
  // reset the stream in case of any error
121
- signalingStream.reset()
126
+ signalingStream.abort(err)
122
127
  throw err
123
128
  } finally {
124
129
  // Close the signaling connection
@@ -144,8 +149,8 @@ export class WebRTCTransport implements Transport, Startable {
144
149
  skipProtection: true,
145
150
  muxerFactory
146
151
  })
147
- } catch (err) {
148
- stream.reset()
152
+ } catch (err: any) {
153
+ stream.abort(err)
149
154
  throw err
150
155
  } finally {
151
156
  // Close the signaling connection
@@ -1,3 +1,2 @@
1
-
2
1
  const charset = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
3
2
  export const genUfrag = (len: number): string => [...Array(len)].map(() => charset.at(Math.floor(Math.random() * charset.length))).join('')
package/src/stream.ts CHANGED
@@ -6,7 +6,7 @@ import { type Pushable, pushable } from 'it-pushable'
6
6
  import { pEvent, TimeoutError } from 'p-event'
7
7
  import { Uint8ArrayList } from 'uint8arraylist'
8
8
  import { Message } from './pb/message.js'
9
- import type { Direction, Stream } from '@libp2p/interface/connection'
9
+ import type { Direction } from '@libp2p/interface/connection'
10
10
 
11
11
  const log = logger('libp2p:webrtc:stream')
12
12
 
@@ -26,6 +26,8 @@ export interface WebRTCStreamInit extends AbstractStreamInit {
26
26
  channel: RTCDataChannel
27
27
 
28
28
  dataChannelOptions?: Partial<DataChannelOpts>
29
+
30
+ maxDataSize: number
29
31
  }
30
32
 
31
33
  // Max message size that can be sent to the DataChannel
@@ -40,7 +42,7 @@ const BUFFERED_AMOUNT_LOW_TIMEOUT = 30 * 1000
40
42
  // protobuf field definition overhead
41
43
  const PROTOBUF_OVERHEAD = 3
42
44
 
43
- class WebRTCStream extends AbstractStream {
45
+ export class WebRTCStream extends AbstractStream {
44
46
  /**
45
47
  * The data channel used to send and receive data
46
48
  */
@@ -58,6 +60,7 @@ class WebRTCStream extends AbstractStream {
58
60
  private readonly incomingData: Pushable<Uint8Array>
59
61
 
60
62
  private messageQueue?: Uint8ArrayList
63
+ private readonly maxDataSize: number
61
64
 
62
65
  constructor (init: WebRTCStreamInit) {
63
66
  super(init)
@@ -71,6 +74,7 @@ class WebRTCStream extends AbstractStream {
71
74
  maxBufferedAmount: init.dataChannelOptions?.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT,
72
75
  maxMessageSize: init.dataChannelOptions?.maxMessageSize ?? MAX_MESSAGE_SIZE
73
76
  }
77
+ this.maxDataSize = init.maxDataSize
74
78
 
75
79
  // set up initial state
76
80
  switch (this.channel.readyState) {
@@ -79,8 +83,8 @@ class WebRTCStream extends AbstractStream {
79
83
 
80
84
  case 'closed':
81
85
  case 'closing':
82
- if (this.stat.timeline.close === undefined || this.stat.timeline.close === 0) {
83
- this.stat.timeline.close = Date.now()
86
+ if (this.timeline.close === undefined || this.timeline.close === 0) {
87
+ this.timeline.close = Date.now()
84
88
  }
85
89
  break
86
90
  case 'connecting':
@@ -94,7 +98,7 @@ class WebRTCStream extends AbstractStream {
94
98
 
95
99
  // handle RTCDataChannel events
96
100
  this.channel.onopen = (_evt) => {
97
- this.stat.timeline.open = new Date().getTime()
101
+ this.timeline.open = new Date().getTime()
98
102
 
99
103
  if (this.messageQueue != null) {
100
104
  // send any queued messages
@@ -107,7 +111,9 @@ class WebRTCStream extends AbstractStream {
107
111
  }
108
112
 
109
113
  this.channel.onclose = (_evt) => {
110
- this.close()
114
+ void this.close().catch(err => {
115
+ log.error('error closing stream after channel closed', err)
116
+ })
111
117
  }
112
118
 
113
119
  this.channel.onerror = (evt) => {
@@ -153,7 +159,6 @@ class WebRTCStream extends AbstractStream {
153
159
  await pEvent(this.channel, 'bufferedamountlow', { timeout: this.dataChannelOptions.bufferedAmountLowEventTimeout })
154
160
  } catch (err: any) {
155
161
  if (err instanceof TimeoutError) {
156
- this.abort(err)
157
162
  throw new Error('Timed out waiting for DataChannel buffer to clear')
158
163
  }
159
164
 
@@ -184,10 +189,17 @@ class WebRTCStream extends AbstractStream {
184
189
  }
185
190
 
186
191
  async sendData (data: Uint8ArrayList): Promise<void> {
187
- const msgbuf = Message.encode({ message: data.subarray() })
188
- const sendbuf = lengthPrefixed.encode.single(msgbuf)
192
+ data = data.sublist()
189
193
 
190
- await this._sendMessage(sendbuf)
194
+ while (data.byteLength > 0) {
195
+ const toSend = Math.min(data.byteLength, this.maxDataSize)
196
+ const buf = data.subarray(0, toSend)
197
+ const msgbuf = Message.encode({ message: buf })
198
+ const sendbuf = lengthPrefixed.encode.single(msgbuf)
199
+ await this._sendMessage(sendbuf)
200
+
201
+ data.consume(toSend)
202
+ }
191
203
  }
192
204
 
193
205
  async sendReset (): Promise<void> {
@@ -212,7 +224,7 @@ class WebRTCStream extends AbstractStream {
212
224
  if (message.flag === Message.Flag.FIN) {
213
225
  // We should expect no more data from the remote, stop reading
214
226
  this.incomingData.end()
215
- this.closeRead()
227
+ this.remoteCloseWrite()
216
228
  }
217
229
 
218
230
  if (message.flag === Message.Flag.RESET) {
@@ -222,7 +234,7 @@ class WebRTCStream extends AbstractStream {
222
234
 
223
235
  if (message.flag === Message.Flag.STOP_SENDING) {
224
236
  // The remote has stopped reading
225
- this.closeWrite()
237
+ this.remoteCloseRead()
226
238
  }
227
239
  }
228
240
 
@@ -259,7 +271,7 @@ export interface WebRTCStreamOptions {
259
271
  onEnd?: (err?: Error | undefined) => void
260
272
  }
261
273
 
262
- export function createStream (options: WebRTCStreamOptions): Stream {
274
+ export function createStream (options: WebRTCStreamOptions): WebRTCStream {
263
275
  const { channel, direction, onEnd, dataChannelOptions } = options
264
276
 
265
277
  return new WebRTCStream({
@@ -268,6 +280,7 @@ export function createStream (options: WebRTCStreamOptions): Stream {
268
280
  maxDataSize: (dataChannelOptions?.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD,
269
281
  dataChannelOptions,
270
282
  onEnd,
271
- channel
283
+ channel,
284
+ log: logger(`libp2p:mplex:stream:${direction}:${channel.id}`)
272
285
  })
273
286
  }