@libp2p/webrtc 2.0.2 → 2.0.4

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
@@ -1,71 +1,96 @@
1
- import { WebRTCStream } from './stream.js'
1
+ import { createStream } from './stream.js'
2
2
  import { nopSink, nopSource } from './util.js'
3
+ import type { DataChannelOpts } from './stream.js'
3
4
  import type { Stream } from '@libp2p/interface-connection'
4
5
  import type { CounterGroup } from '@libp2p/interface-metrics'
5
6
  import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface-stream-muxer'
6
7
  import type { Source, Sink } from 'it-stream-types'
7
8
  import type { Uint8ArrayList } from 'uint8arraylist'
8
9
 
10
+ const PROTOCOL = '/webrtc'
11
+
9
12
  export interface DataChannelMuxerFactoryInit {
13
+ /**
14
+ * WebRTC Peer Connection
15
+ */
10
16
  peerConnection: RTCPeerConnection
17
+
18
+ /**
19
+ * Optional metrics for this data channel muxer
20
+ */
11
21
  metrics?: CounterGroup
22
+
23
+ /**
24
+ * Data channel options
25
+ */
26
+ dataChannelOptions?: Partial<DataChannelOpts>
27
+
28
+ /**
29
+ * The protocol to use
30
+ */
31
+ protocol?: string
12
32
  }
13
33
 
14
34
  export class DataChannelMuxerFactory implements StreamMuxerFactory {
35
+ public readonly protocol: string
36
+
15
37
  /**
16
38
  * WebRTC Peer Connection
17
39
  */
18
40
  private readonly peerConnection: RTCPeerConnection
19
- private streamBuffer: WebRTCStream[] = []
41
+ private streamBuffer: Stream[] = []
20
42
  private readonly metrics?: CounterGroup
43
+ private readonly dataChannelOptions?: Partial<DataChannelOpts>
44
+
45
+ constructor (init: DataChannelMuxerFactoryInit) {
46
+ this.peerConnection = init.peerConnection
47
+ this.metrics = init.metrics
48
+ this.protocol = init.protocol ?? PROTOCOL
49
+ this.dataChannelOptions = init.dataChannelOptions
21
50
 
22
- constructor (peerConnection: RTCPeerConnection, metrics?: CounterGroup, readonly protocol = '/webrtc') {
23
- this.peerConnection = peerConnection
24
51
  // store any datachannels opened before upgrade has been completed
25
52
  this.peerConnection.ondatachannel = ({ channel }) => {
26
- const stream = new WebRTCStream({
53
+ const stream = createStream({
27
54
  channel,
28
- stat: {
29
- direction: 'inbound',
30
- timeline: { open: 0 }
31
- },
32
- closeCb: (_stream) => {
33
- this.streamBuffer = this.streamBuffer.filter(s => !_stream.eq(s))
55
+ direction: 'inbound',
56
+ dataChannelOptions: init.dataChannelOptions,
57
+ onEnd: () => {
58
+ this.streamBuffer = this.streamBuffer.filter(s => s.id !== stream.id)
34
59
  }
35
60
  })
36
61
  this.streamBuffer.push(stream)
37
62
  }
38
- this.metrics = metrics
39
63
  }
40
64
 
41
- createStreamMuxer (init?: StreamMuxerInit | undefined): StreamMuxer {
42
- return new DataChannelMuxer(this.peerConnection, this.streamBuffer, this.protocol, init, this.metrics)
65
+ createStreamMuxer (init?: StreamMuxerInit): StreamMuxer {
66
+ return new DataChannelMuxer({
67
+ ...init,
68
+ peerConnection: this.peerConnection,
69
+ dataChannelOptions: this.dataChannelOptions,
70
+ metrics: this.metrics,
71
+ streams: this.streamBuffer,
72
+ protocol: this.protocol
73
+ })
43
74
  }
44
75
  }
45
76
 
77
+ export interface DataChannelMuxerInit extends DataChannelMuxerFactoryInit, StreamMuxerInit {
78
+ streams: Stream[]
79
+ }
80
+
46
81
  /**
47
82
  * A libp2p data channel stream muxer
48
83
  */
49
84
  export class DataChannelMuxer implements StreamMuxer {
50
- /**
51
- * WebRTC Peer Connection
52
- */
53
- private readonly peerConnection: RTCPeerConnection
54
-
55
- /**
56
- * Optional metrics for this data channel muxer
57
- */
58
- private readonly metrics?: CounterGroup
59
-
60
85
  /**
61
86
  * Array of streams in the data channel
62
87
  */
63
- streams: Stream[] = []
88
+ public streams: Stream[]
89
+ public protocol: string
64
90
 
65
- /**
66
- * Initialized stream muxer
67
- */
68
- init?: StreamMuxerInit
91
+ private readonly peerConnection: RTCPeerConnection
92
+ private readonly dataChannelOptions?: DataChannelOpts
93
+ private readonly metrics?: CounterGroup
69
94
 
70
95
  /**
71
96
  * Close or abort all tracked streams and stop the muxer
@@ -82,16 +107,11 @@ export class DataChannelMuxer implements StreamMuxer {
82
107
  */
83
108
  sink: Sink<Source<Uint8Array | Uint8ArrayList>, Promise<void>> = nopSink
84
109
 
85
- constructor (peerConnection: RTCPeerConnection, streams: Stream[], readonly protocol: string = '/webrtc', init?: StreamMuxerInit, metrics?: CounterGroup) {
86
- /**
87
- * Initialized stream muxer
88
- */
89
- this.init = init
90
-
91
- /**
92
- * WebRTC Peer Connection
93
- */
94
- this.peerConnection = peerConnection
110
+ constructor (readonly init: DataChannelMuxerInit) {
111
+ this.streams = init.streams
112
+ this.peerConnection = init.peerConnection
113
+ this.protocol = init.protocol ?? PROTOCOL
114
+ this.metrics = init.metrics
95
115
 
96
116
  /**
97
117
  * Fired when a data channel has been added to the connection has been
@@ -100,15 +120,15 @@ export class DataChannelMuxer implements StreamMuxer {
100
120
  * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event}
101
121
  */
102
122
  this.peerConnection.ondatachannel = ({ channel }) => {
103
- const stream = new WebRTCStream({
123
+ const stream = createStream({
104
124
  channel,
105
- stat: {
106
- direction: 'inbound',
107
- timeline: {
108
- open: 0
109
- }
110
- },
111
- closeCb: this.wrapStreamEnd(init?.onIncomingStream)
125
+ direction: 'inbound',
126
+ dataChannelOptions: this.dataChannelOptions,
127
+ onEnd: () => {
128
+ this.streams = this.streams.filter(s => s.id !== stream.id)
129
+ this.metrics?.increment({ stream_end: true })
130
+ init?.onStreamEnd?.(stream)
131
+ }
112
132
  })
113
133
 
114
134
  this.streams.push(stream)
@@ -118,13 +138,6 @@ export class DataChannelMuxer implements StreamMuxer {
118
138
  }
119
139
  }
120
140
 
121
- // wrap open streams with the onStreamEnd callback
122
- this.streams = streams
123
- .filter(stream => stream.stat.timeline.close == null)
124
- .map(stream => {
125
- (stream as WebRTCStream).closeCb = this.wrapStreamEnd(init?.onStreamEnd)
126
- return stream
127
- })
128
141
  const onIncomingStream = init?.onIncomingStream
129
142
  if (onIncomingStream != null) {
130
143
  this.streams.forEach(s => { onIncomingStream(s) })
@@ -134,33 +147,19 @@ export class DataChannelMuxer implements StreamMuxer {
134
147
  newStream (): Stream {
135
148
  // The spec says the label SHOULD be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
136
149
  const channel = this.peerConnection.createDataChannel('')
137
- const closeCb = (stream: Stream): void => {
138
- this.metrics?.increment({ stream_end: true })
139
- this.init?.onStreamEnd?.(stream)
140
- }
141
- const stream = new WebRTCStream({
150
+ const stream = createStream({
142
151
  channel,
143
- stat: {
144
- direction: 'outbound',
145
- timeline: {
146
- open: 0
147
- }
148
- },
149
- closeCb: this.wrapStreamEnd(closeCb)
152
+ direction: 'outbound',
153
+ dataChannelOptions: this.dataChannelOptions,
154
+ onEnd: () => {
155
+ this.streams = this.streams.filter(s => s.id !== stream.id)
156
+ this.metrics?.increment({ stream_end: true })
157
+ this.init?.onStreamEnd?.(stream)
158
+ }
150
159
  })
151
160
  this.streams.push(stream)
152
161
  this.metrics?.increment({ outgoing_stream: true })
153
162
 
154
163
  return stream
155
164
  }
156
-
157
- private wrapStreamEnd (onStreamEnd?: (s: Stream) => void): (stream: Stream) => void {
158
- const self = this
159
- return (_stream) => {
160
- self.streams = self.streams.filter(s => !(_stream instanceof WebRTCStream && (_stream).eq(s)))
161
- if (onStreamEnd != null) {
162
- onStreamEnd(_stream)
163
- }
164
- }
165
- }
166
165
  }
@@ -5,6 +5,7 @@ import pDefer, { type DeferredPromise } from 'p-defer'
5
5
  import { DataChannelMuxerFactory } from '../muxer.js'
6
6
  import { Message } from './pb/message.js'
7
7
  import { readCandidatesUntilConnected, resolveOnConnected } from './util.js'
8
+ import type { DataChannelOpts } from '../stream.js'
8
9
  import type { Stream } from '@libp2p/interface-connection'
9
10
  import type { IncomingStreamData } from '@libp2p/interface-registrar'
10
11
  import type { StreamMuxerFactory } from '@libp2p/interface-stream-muxer'
@@ -13,14 +14,13 @@ const DEFAULT_TIMEOUT = 30 * 1000
13
14
 
14
15
  const log = logger('libp2p:webrtc:peer')
15
16
 
16
- export type IncomingStreamOpts = { rtcConfiguration?: RTCConfiguration } & IncomingStreamData
17
+ export type IncomingStreamOpts = { rtcConfiguration?: RTCConfiguration, dataChannelOptions?: Partial<DataChannelOpts> } & IncomingStreamData
17
18
 
18
- export async function handleIncomingStream ({ rtcConfiguration, stream: rawStream }: IncomingStreamOpts): Promise<{ pc: RTCPeerConnection, muxerFactory: StreamMuxerFactory, remoteAddress: string }> {
19
+ export async function handleIncomingStream ({ rtcConfiguration, dataChannelOptions, stream: rawStream }: IncomingStreamOpts): Promise<{ pc: RTCPeerConnection, muxerFactory: StreamMuxerFactory, remoteAddress: string }> {
19
20
  const signal = AbortSignal.timeout(DEFAULT_TIMEOUT)
20
21
  const stream = pbStream(abortableDuplex(rawStream, signal)).pb(Message)
21
22
  const pc = new RTCPeerConnection(rtcConfiguration)
22
- const muxerFactory = new DataChannelMuxerFactory(pc)
23
-
23
+ const muxerFactory = new DataChannelMuxerFactory({ peerConnection: pc, dataChannelOptions })
24
24
  const connectedPromise: DeferredPromise<void> = pDefer()
25
25
  const answerSentPromise: DeferredPromise<void> = pDefer()
26
26
 
@@ -86,13 +86,14 @@ export interface ConnectOptions {
86
86
  stream: Stream
87
87
  signal: AbortSignal
88
88
  rtcConfiguration?: RTCConfiguration
89
+ dataChannelOptions?: Partial<DataChannelOpts>
89
90
  }
90
91
 
91
- export async function initiateConnection ({ rtcConfiguration, signal, stream: rawStream }: ConnectOptions): Promise<{ pc: RTCPeerConnection, muxerFactory: StreamMuxerFactory, remoteAddress: string }> {
92
+ export async function initiateConnection ({ rtcConfiguration, dataChannelOptions, signal, stream: rawStream }: ConnectOptions): Promise<{ pc: RTCPeerConnection, muxerFactory: StreamMuxerFactory, remoteAddress: string }> {
92
93
  const stream = pbStream(abortableDuplex(rawStream, signal)).pb(Message)
93
94
  // setup peer connection
94
95
  const pc = new RTCPeerConnection(rtcConfiguration)
95
- const muxerFactory = new DataChannelMuxerFactory(pc)
96
+ const muxerFactory = new DataChannelMuxerFactory({ peerConnection: pc, dataChannelOptions })
96
97
 
97
98
  const connectedPromise: DeferredPromise<void> = pDefer()
98
99
  resolveOnConnected(pc, connectedPromise)
@@ -7,6 +7,7 @@ import { codes } from '../error.js'
7
7
  import { WebRTCMultiaddrConnection } from '../maconn.js'
8
8
  import { initiateConnection, handleIncomingStream } from './handler.js'
9
9
  import { WebRTCPeerListener } from './listener.js'
10
+ import type { DataChannelOpts } from '../stream.js'
10
11
  import type { Connection } from '@libp2p/interface-connection'
11
12
  import type { PeerId } from '@libp2p/interface-peer-id'
12
13
  import type { IncomingStreamData, Registrar } from '@libp2p/interface-registrar'
@@ -21,6 +22,7 @@ const WEBRTC_CODE = protocols('webrtc').code
21
22
 
22
23
  export interface WebRTCTransportInit {
23
24
  rtcConfiguration?: RTCConfiguration
25
+ dataChannel?: Partial<DataChannelOpts>
24
26
  }
25
27
 
26
28
  export interface WebRTCTransportComponents {
@@ -35,7 +37,7 @@ export class WebRTCTransport implements Transport, Startable {
35
37
 
36
38
  constructor (
37
39
  private readonly components: WebRTCTransportComponents,
38
- private readonly init: WebRTCTransportInit
40
+ private readonly init: WebRTCTransportInit = {}
39
41
  ) {
40
42
  }
41
43
 
@@ -123,6 +125,7 @@ export class WebRTCTransport implements Transport, Startable {
123
125
  const { pc, muxerFactory, remoteAddress } = await initiateConnection({
124
126
  stream: signalingStream,
125
127
  rtcConfiguration: this.init.rtcConfiguration,
128
+ dataChannelOptions: this.init.dataChannel,
126
129
  signal: options.signal
127
130
  })
128
131
 
@@ -154,7 +157,8 @@ export class WebRTCTransport implements Transport, Startable {
154
157
  const { pc, muxerFactory, remoteAddress } = await handleIncomingStream({
155
158
  rtcConfiguration: this.init.rtcConfiguration,
156
159
  connection,
157
- stream
160
+ stream,
161
+ dataChannelOptions: this.init.dataChannel
158
162
  })
159
163
 
160
164
  await this.components.upgrader.upgradeInbound(new WebRTCMultiaddrConnection({
@@ -9,11 +9,12 @@ import { fromString as uint8arrayFromString } from 'uint8arrays/from-string'
9
9
  import { dataChannelError, inappropriateMultiaddr, unimplemented, invalidArgument } from '../error.js'
10
10
  import { WebRTCMultiaddrConnection } from '../maconn.js'
11
11
  import { DataChannelMuxerFactory } from '../muxer.js'
12
- import { WebRTCStream } from '../stream.js'
12
+ import { createStream } from '../stream.js'
13
13
  import { isFirefox } from '../util.js'
14
14
  import * as sdp from './sdp.js'
15
15
  import { genUfrag } from './util.js'
16
16
  import type { WebRTCDialOptions } from './options.js'
17
+ import type { DataChannelOpts } from '../stream.js'
17
18
  import type { Connection } from '@libp2p/interface-connection'
18
19
  import type { CounterGroup, Metrics } from '@libp2p/interface-metrics'
19
20
  import type { PeerId } from '@libp2p/interface-peer-id'
@@ -52,12 +53,17 @@ export interface WebRTCMetrics {
52
53
  dialerEvents: CounterGroup
53
54
  }
54
55
 
56
+ export interface WebRTCTransportDirectInit {
57
+ dataChannel?: Partial<DataChannelOpts>
58
+ }
59
+
55
60
  export class WebRTCDirectTransport implements Transport {
56
61
  private readonly metrics?: WebRTCMetrics
57
62
  private readonly components: WebRTCDirectTransportComponents
58
-
59
- constructor (components: WebRTCDirectTransportComponents) {
63
+ private readonly init: WebRTCTransportDirectInit
64
+ constructor (components: WebRTCDirectTransportComponents, init: WebRTCTransportDirectInit = {}) {
60
65
  this.components = components
66
+ this.init = init
61
67
  if (components.metrics != null) {
62
68
  this.metrics = {
63
69
  dialerEvents: components.metrics.registerCounterGroup('libp2p_webrtc_dialer_events_total', {
@@ -185,7 +191,7 @@ export class WebRTCDirectTransport implements Transport {
185
191
  // we pass in undefined for these parameters.
186
192
  const noise = Noise({ prologueBytes: fingerprintsPrologue })()
187
193
 
188
- const wrappedChannel = new WebRTCStream({ channel: handshakeDataChannel, stat: { direction: 'inbound', timeline: { open: 1 } } })
194
+ const wrappedChannel = createStream({ channel: handshakeDataChannel, direction: 'inbound', dataChannelOptions: this.init.dataChannel })
189
195
  const wrappedDuplex = {
190
196
  ...wrappedChannel,
191
197
  sink: wrappedChannel.sink.bind(wrappedChannel),
@@ -231,7 +237,7 @@ export class WebRTCDirectTransport implements Transport {
231
237
  // Track opened peer connection
232
238
  this.metrics?.dialerEvents.increment({ peer_connection: true })
233
239
 
234
- const muxerFactory = new DataChannelMuxerFactory(peerConnection, this.metrics?.dialerEvents)
240
+ const muxerFactory = new DataChannelMuxerFactory({ peerConnection, metrics: this.metrics?.dialerEvents, dataChannelOptions: this.init.dataChannel })
235
241
 
236
242
  // For outbound connections, the remote is expected to start the noise handshake.
237
243
  // Therefore, we need to secure an inbound noise connection from the remote.