@libp2p/webrtc 5.2.23 → 5.2.24-0f07e3df5

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.
Files changed (90) hide show
  1. package/README.md +10 -20
  2. package/dist/index.min.js +18 -18
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/constants.d.ts +0 -23
  5. package/dist/src/constants.d.ts.map +1 -1
  6. package/dist/src/constants.js +0 -23
  7. package/dist/src/constants.js.map +1 -1
  8. package/dist/src/index.d.ts +12 -22
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/index.js +12 -22
  11. package/dist/src/index.js.map +1 -1
  12. package/dist/src/muxer.d.ts +14 -46
  13. package/dist/src/muxer.d.ts.map +1 -1
  14. package/dist/src/muxer.js +32 -135
  15. package/dist/src/muxer.js.map +1 -1
  16. package/dist/src/private-to-private/initiate-connection.d.ts +2 -3
  17. package/dist/src/private-to-private/initiate-connection.d.ts.map +1 -1
  18. package/dist/src/private-to-private/initiate-connection.js +23 -5
  19. package/dist/src/private-to-private/initiate-connection.js.map +1 -1
  20. package/dist/src/private-to-private/signaling-stream-handler.d.ts +4 -4
  21. package/dist/src/private-to-private/signaling-stream-handler.d.ts.map +1 -1
  22. package/dist/src/private-to-private/signaling-stream-handler.js +10 -6
  23. package/dist/src/private-to-private/signaling-stream-handler.js.map +1 -1
  24. package/dist/src/private-to-private/transport.d.ts +2 -2
  25. package/dist/src/private-to-private/transport.d.ts.map +1 -1
  26. package/dist/src/private-to-private/transport.js +30 -15
  27. package/dist/src/private-to-private/transport.js.map +1 -1
  28. package/dist/src/private-to-private/util.d.ts +3 -1
  29. package/dist/src/private-to-private/util.d.ts.map +1 -1
  30. package/dist/src/private-to-private/util.js +15 -3
  31. package/dist/src/private-to-private/util.js.map +1 -1
  32. package/dist/src/private-to-public/listener.d.ts.map +1 -1
  33. package/dist/src/private-to-public/listener.js +21 -15
  34. package/dist/src/private-to-public/listener.js.map +1 -1
  35. package/dist/src/private-to-public/transport.d.ts.map +1 -1
  36. package/dist/src/private-to-public/transport.js +3 -2
  37. package/dist/src/private-to-public/transport.js.map +1 -1
  38. package/dist/src/private-to-public/utils/connect.d.ts +1 -1
  39. package/dist/src/private-to-public/utils/connect.d.ts.map +1 -1
  40. package/dist/src/private-to-public/utils/connect.js +18 -15
  41. package/dist/src/private-to-public/utils/connect.js.map +1 -1
  42. package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts +4 -4
  43. package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts.map +1 -1
  44. package/dist/src/private-to-public/utils/get-rtcpeerconnection.js +13 -2
  45. package/dist/src/private-to-public/utils/get-rtcpeerconnection.js.map +1 -1
  46. package/dist/src/private-to-public/utils/sdp.d.ts.map +1 -1
  47. package/dist/src/private-to-public/utils/sdp.js +25 -13
  48. package/dist/src/private-to-public/utils/sdp.js.map +1 -1
  49. package/dist/src/private-to-public/utils/stun-listener.js +1 -1
  50. package/dist/src/private-to-public/utils/stun-listener.js.map +1 -1
  51. package/dist/src/rtcpeerconnection-to-conn.d.ts +12 -0
  52. package/dist/src/rtcpeerconnection-to-conn.d.ts.map +1 -0
  53. package/dist/src/rtcpeerconnection-to-conn.js +43 -0
  54. package/dist/src/rtcpeerconnection-to-conn.js.map +1 -0
  55. package/dist/src/stream.d.ts +16 -26
  56. package/dist/src/stream.d.ts.map +1 -1
  57. package/dist/src/stream.js +65 -167
  58. package/dist/src/stream.js.map +1 -1
  59. package/dist/src/util.d.ts +3 -1
  60. package/dist/src/util.d.ts.map +1 -1
  61. package/dist/src/util.js +19 -0
  62. package/dist/src/util.js.map +1 -1
  63. package/dist/src/webrtc/index.d.ts +1 -1
  64. package/dist/src/webrtc/index.d.ts.map +1 -1
  65. package/dist/src/webrtc/index.js +1 -1
  66. package/dist/src/webrtc/index.js.map +1 -1
  67. package/package.json +26 -29
  68. package/src/constants.ts +0 -28
  69. package/src/index.ts +12 -22
  70. package/src/muxer.ts +43 -166
  71. package/src/private-to-private/initiate-connection.ts +30 -8
  72. package/src/private-to-private/signaling-stream-handler.ts +12 -9
  73. package/src/private-to-private/transport.ts +33 -17
  74. package/src/private-to-private/util.ts +21 -4
  75. package/src/private-to-public/listener.ts +22 -15
  76. package/src/private-to-public/transport.ts +3 -2
  77. package/src/private-to-public/utils/connect.ts +19 -16
  78. package/src/private-to-public/utils/get-rtcpeerconnection.ts +16 -4
  79. package/src/private-to-public/utils/sdp.ts +29 -13
  80. package/src/private-to-public/utils/stun-listener.ts +1 -1
  81. package/src/rtcpeerconnection-to-conn.ts +62 -0
  82. package/src/stream.ts +78 -195
  83. package/src/util.ts +22 -1
  84. package/src/webrtc/index.ts +1 -1
  85. package/dist/src/maconn.d.ts +0 -58
  86. package/dist/src/maconn.d.ts.map +0 -1
  87. package/dist/src/maconn.js +0 -56
  88. package/dist/src/maconn.js.map +0 -1
  89. package/dist/typedoc-urls.json +0 -14
  90. package/src/maconn.ts +0 -101
package/src/index.ts CHANGED
@@ -26,8 +26,8 @@
26
26
  * WebRTC requires use of a relay to connect two nodes. The listener first discovers a relay server and makes a reservation, then the dialer can connect via the relayed address.
27
27
  *
28
28
  * ```TypeScript
29
- * import { noise } from '@chainsafe/libp2p-noise'
30
- * import { yamux } from '@chainsafe/libp2p-yamux'
29
+ * import { noise } from '@libp2p/noise'
30
+ * import { yamux } from '@libp2p/yamux'
31
31
  * import { echo } from '@libp2p/echo'
32
32
  * import { circuitRelayTransport, circuitRelayServer } from '@libp2p/circuit-relay-v2'
33
33
  * import { identify } from '@libp2p/identify'
@@ -35,7 +35,6 @@
35
35
  * import { webSockets } from '@libp2p/websockets'
36
36
  * import { WebRTC } from '@multiformats/multiaddr-matcher'
37
37
  * import delay from 'delay'
38
- * import { pipe } from 'it-pipe'
39
38
  * import { createLibp2p } from 'libp2p'
40
39
  * import type { Multiaddr } from '@multiformats/multiaddr'
41
40
  *
@@ -134,15 +133,11 @@
134
133
  * await relay.stop()
135
134
  *
136
135
  * // send/receive some data from the remote peer via a direct connection
137
- * await pipe(
138
- * [new TextEncoder().encode('hello world')],
139
- * stream,
140
- * async source => {
141
- * for await (const buf of source) {
142
- * console.info(new TextDecoder().decode(buf.subarray()))
143
- * }
144
- * }
145
- * )
136
+ * stream.send(new TextEncoder().encode('hello world'))
137
+ *
138
+ * stream.addEventListener('message', (evt) => {
139
+ * console.info(new TextDecoder().decode(evt.data.subarray()))
140
+ * })
146
141
  * ```
147
142
  *
148
143
  * @example WebRTC Direct
@@ -167,7 +162,6 @@
167
162
  * ```TypeScript
168
163
  * import { createLibp2p } from 'libp2p'
169
164
  * import { multiaddr } from '@multiformats/multiaddr'
170
- * import { pipe } from 'it-pipe'
171
165
  * import { fromString, toString } from 'uint8arrays'
172
166
  * import { webRTCDirect } from '@libp2p/webrtc'
173
167
  *
@@ -196,15 +190,11 @@
196
190
  * signal: AbortSignal.timeout(10_000)
197
191
  * })
198
192
  *
199
- * await pipe(
200
- * [fromString(`Hello js-libp2p-webrtc\n`)],
201
- * stream,
202
- * async function (source) {
203
- * for await (const buf of source) {
204
- * console.info(toString(buf.subarray()))
205
- * }
206
- * }
207
- * )
193
+ * stream.send(new TextEncoder().encode('hello world'))
194
+ *
195
+ * stream.addEventListener('message', (evt) => {
196
+ * console.info(new TextDecoder().decode(evt.data.subarray()))
197
+ * })
208
198
  * ```
209
199
  *
210
200
  * ## WebRTC Direct certificate hashes
package/src/muxer.ts CHANGED
@@ -1,11 +1,9 @@
1
+ import { AbstractStreamMuxer } from '@libp2p/utils'
2
+ import { pEvent } from 'p-event'
1
3
  import { MUXER_PROTOCOL } from './constants.js'
2
- import { createStream } from './stream.js'
3
- import { drainAndClose, nopSink, nopSource } from './util.js'
4
+ import { createStream, WebRTCStream } from './stream.js'
4
5
  import type { DataChannelOptions } from './index.js'
5
- import type { ComponentLogger, Logger, Stream, CounterGroup, StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface'
6
- import type { AbortOptions } from '@multiformats/multiaddr'
7
- import type { Source, Sink } from 'it-stream-types'
8
- import type { Uint8ArrayList } from 'uint8arraylist'
6
+ import type { ComponentLogger, CounterGroup, StreamMuxer, StreamMuxerFactory, CreateStreamOptions, MultiaddrConnection } from '@libp2p/interface'
9
7
 
10
8
  export interface DataChannelMuxerFactoryInit {
11
9
  /**
@@ -23,6 +21,9 @@ export interface DataChannelMuxerFactoryInit {
23
21
  */
24
22
  metrics?: CounterGroup
25
23
 
24
+ /**
25
+ * Options used to create data channels
26
+ */
26
27
  dataChannelOptions?: DataChannelOptions
27
28
  }
28
29
 
@@ -30,12 +31,6 @@ export interface DataChannelMuxerFactoryComponents {
30
31
  logger: ComponentLogger
31
32
  }
32
33
 
33
- interface BufferedStream {
34
- stream: Stream
35
- channel: RTCDataChannel
36
- onEnd(err?: Error): void
37
- }
38
-
39
34
  export class DataChannelMuxerFactory implements StreamMuxerFactory {
40
35
  public readonly protocol: string
41
36
 
@@ -43,69 +38,28 @@ export class DataChannelMuxerFactory implements StreamMuxerFactory {
43
38
  * WebRTC Peer Connection
44
39
  */
45
40
  private readonly peerConnection: RTCPeerConnection
46
- private bufferedStreams: BufferedStream[] = []
47
41
  private readonly metrics?: CounterGroup
48
42
  private readonly dataChannelOptions?: DataChannelOptions
49
- private readonly components: DataChannelMuxerFactoryComponents
50
- private readonly log: Logger
51
43
 
52
- constructor (components: DataChannelMuxerFactoryComponents, init: DataChannelMuxerFactoryInit) {
53
- this.components = components
44
+ constructor (init: DataChannelMuxerFactoryInit) {
54
45
  this.peerConnection = init.peerConnection
55
46
  this.metrics = init.metrics
56
47
  this.protocol = init.protocol ?? MUXER_PROTOCOL
57
48
  this.dataChannelOptions = init.dataChannelOptions ?? {}
58
- this.log = components.logger.forComponent('libp2p:webrtc:muxerfactory')
59
-
60
- // store any data channels opened before upgrade has been completed
61
- this.peerConnection.ondatachannel = ({ channel }) => {
62
- this.log.trace('incoming early datachannel with channel id %d and label "%s"', channel.id)
63
-
64
- // 'init' channel is only used during connection establishment
65
- if (channel.label === 'init') {
66
- this.log.trace('closing early init channel')
67
- channel.close()
68
-
69
- return
70
- }
71
-
72
- // @ts-expect-error fields are set below
73
- const bufferedStream: BufferedStream = {}
74
-
75
- const stream = createStream({
76
- channel,
77
- direction: 'inbound',
78
- onEnd: (err) => {
79
- bufferedStream.onEnd(err)
80
- },
81
- logger: components.logger,
82
- ...this.dataChannelOptions
83
- })
84
-
85
- bufferedStream.stream = stream
86
- bufferedStream.channel = channel
87
- bufferedStream.onEnd = () => {
88
- this.bufferedStreams = this.bufferedStreams.filter(s => s.stream.id !== stream.id)
89
- }
90
-
91
- this.bufferedStreams.push(bufferedStream)
92
- }
93
49
  }
94
50
 
95
- createStreamMuxer (init?: StreamMuxerInit): StreamMuxer {
96
- return new DataChannelMuxer(this.components, {
97
- ...init,
51
+ createStreamMuxer (maConn: MultiaddrConnection): StreamMuxer {
52
+ return new DataChannelMuxer(maConn, {
98
53
  peerConnection: this.peerConnection,
99
54
  dataChannelOptions: this.dataChannelOptions,
100
55
  metrics: this.metrics,
101
- streams: this.bufferedStreams,
102
56
  protocol: this.protocol
103
57
  })
104
58
  }
105
59
  }
106
60
 
107
- export interface DataChannelMuxerInit extends DataChannelMuxerFactoryInit, StreamMuxerInit {
108
- streams: BufferedStream[]
61
+ export interface DataChannelMuxerInit extends DataChannelMuxerFactoryInit {
62
+ protocol: string
109
63
  }
110
64
 
111
65
  export interface DataChannelMuxerComponents {
@@ -115,26 +69,18 @@ export interface DataChannelMuxerComponents {
115
69
  /**
116
70
  * A libp2p data channel stream muxer
117
71
  */
118
- export class DataChannelMuxer implements StreamMuxer {
119
- /**
120
- * Array of streams in the data channel
121
- */
122
- public streams: Stream[]
123
- public protocol: string
124
-
125
- private readonly log: Logger
72
+ export class DataChannelMuxer extends AbstractStreamMuxer<WebRTCStream> implements StreamMuxer<WebRTCStream> {
126
73
  private readonly peerConnection: RTCPeerConnection
127
74
  private readonly dataChannelOptions: DataChannelOptions
128
- private readonly metrics?: CounterGroup
129
- private readonly logger: ComponentLogger
130
75
 
131
- constructor (components: DataChannelMuxerComponents, readonly init: DataChannelMuxerInit) {
132
- this.log = components.logger.forComponent('libp2p:webrtc:muxer')
133
- this.logger = components.logger
134
- this.streams = init.streams.map(s => s.stream)
76
+ constructor (maConn: MultiaddrConnection, init: DataChannelMuxerInit) {
77
+ super(maConn, {
78
+ ...init,
79
+ name: 'muxer'
80
+ })
81
+
135
82
  this.peerConnection = init.peerConnection
136
83
  this.protocol = init.protocol ?? MUXER_PROTOCOL
137
- this.metrics = init.metrics
138
84
  this.dataChannelOptions = init.dataChannelOptions ?? {}
139
85
 
140
86
  /**
@@ -144,7 +90,7 @@ export class DataChannelMuxer implements StreamMuxer {
144
90
  * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event}
145
91
  */
146
92
  this.peerConnection.ondatachannel = ({ channel }) => {
147
- this.log.trace('incoming datachannel with channel id %d', channel.id)
93
+ this.log.trace('incoming %s datachannel with channel id %d and status', channel.protocol, channel.id, channel.readyState)
148
94
 
149
95
  // 'init' channel is only used during connection establishment
150
96
  if (channel.label === 'init') {
@@ -154,115 +100,46 @@ export class DataChannelMuxer implements StreamMuxer {
154
100
  return
155
101
  }
156
102
 
157
- // lib-datachannel throws if `.getId` is called on a closed channel so
158
- // memoize it
159
- const id = channel.id
160
-
161
103
  const stream = createStream({
104
+ ...this.streamOptions,
105
+ ...this.dataChannelOptions,
162
106
  channel,
163
107
  direction: 'inbound',
164
- onEnd: () => {
165
- this.#onStreamEnd(stream, channel)
166
- this.log('incoming channel %s ended', id)
167
- },
168
- logger: this.logger,
169
- ...this.dataChannelOptions
170
- })
171
-
172
- this.streams.push(stream)
173
- this.metrics?.increment({ incoming_stream: true })
174
- init?.onIncomingStream?.(stream)
175
- }
176
-
177
- // the DataChannelMuxer constructor is called during set up of the
178
- // connection by the upgrader.
179
- //
180
- // If we invoke `init.onIncomingStream` immediately, the connection object
181
- // will not be set up yet so add a tiny delay before letting the
182
- // connection know about early streams
183
- if (this.init.streams.length > 0) {
184
- queueMicrotask(() => {
185
- this.init.streams.forEach(bufferedStream => {
186
- bufferedStream.onEnd = () => {
187
- this.log('incoming early channel %s ended with state %s', bufferedStream.channel.id, bufferedStream.channel.readyState)
188
- this.#onStreamEnd(bufferedStream.stream, bufferedStream.channel)
189
- }
190
-
191
- this.metrics?.increment({ incoming_stream: true })
192
- this.init?.onIncomingStream?.(bufferedStream.stream)
193
- })
194
- })
195
- }
196
- }
197
-
198
- #onStreamEnd (stream: Stream, channel: RTCDataChannel): void {
199
- this.log.trace('stream %s %s %s onEnd', stream.direction, stream.id, stream.protocol)
200
- drainAndClose(
201
- channel,
202
- `${stream.direction} ${stream.id} ${stream.protocol}`,
203
- this.dataChannelOptions.drainTimeout, {
204
108
  log: this.log
205
- }
206
- )
207
- this.streams = this.streams.filter(s => s.id !== stream.id)
208
- this.metrics?.increment({ stream_end: true })
209
- this.init?.onStreamEnd?.(stream)
210
- }
211
-
212
- /**
213
- * Gracefully close all tracked streams and stop the muxer
214
- */
215
- async close (options?: AbortOptions): Promise<void> {
216
- try {
217
- await Promise.all(
218
- this.streams.map(async stream => stream.close(options))
219
- )
220
- } catch (err: any) {
221
- this.abort(err)
222
- }
223
- }
109
+ })
224
110
 
225
- /**
226
- * Abort all tracked streams and stop the muxer
227
- */
228
- abort (err: Error): void {
229
- for (const stream of this.streams) {
230
- stream.abort(err)
111
+ this.onRemoteStream(stream)
231
112
  }
232
113
  }
233
114
 
234
- /**
235
- * The stream source, a no-op as the transport natively supports multiplexing
236
- */
237
- source: AsyncGenerator<Uint8Array, any, unknown> = nopSource()
115
+ async onCreateStream (options?: CreateStreamOptions): Promise<WebRTCStream> {
116
+ // The spec says the label MUST be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
117
+ const channel = this.peerConnection.createDataChannel('wtf', {
118
+ // TODO: pre-negotiate stream protocol
119
+ protocol: options?.protocol
120
+ })
238
121
 
239
- /**
240
- * The stream destination, a no-op as the transport natively supports multiplexing
241
- */
242
- sink: Sink<Source<Uint8Array | Uint8ArrayList>, Promise<void>> = nopSink
122
+ this.log('open channel %d for protocol %s', channel.id, options?.protocol)
243
123
 
244
- newStream (): Stream {
245
- // The spec says the label MUST be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
246
- const channel = this.peerConnection.createDataChannel('')
247
- // lib-datachannel throws if `.getId` is called on a closed channel so
248
- // memoize it
249
- const id = channel.id
124
+ if (channel.readyState !== 'open') {
125
+ this.log('channel %d state is "%s" and not "open", waiting for "open" event before sending data', channel.id, channel.readyState)
126
+ await pEvent(channel, 'open', options)
250
127
 
251
- this.log.trace('opened outgoing datachannel with channel id %s', id)
128
+ this.log('channel %d state is now "%s", sending data', channel.id, channel.readyState)
129
+ }
252
130
 
253
131
  const stream = createStream({
132
+ ...options,
133
+ ...this.dataChannelOptions,
254
134
  channel,
255
135
  direction: 'outbound',
256
- onEnd: () => {
257
- this.#onStreamEnd(stream, channel)
258
- this.log('outgoing channel %s ended', id)
259
- },
260
- logger: this.logger,
261
- ...this.dataChannelOptions
136
+ log: this.log
262
137
  })
263
- this.streams.push(stream)
264
- this.metrics?.increment({ outgoing_stream: true })
265
138
 
266
139
  return stream
267
140
  }
141
+
142
+ onData (): void {
143
+
144
+ }
268
145
  }
@@ -1,4 +1,5 @@
1
- import { pbStream } from 'it-protobuf-stream'
1
+ import { pbStream } from '@libp2p/utils'
2
+ import { pEvent } from 'p-event'
2
3
  import { CustomProgressEvent } from 'progress-events'
3
4
  import { SIGNALING_PROTOCOL } from '../constants.js'
4
5
  import { SDPHandshakeFailedError } from '../error.js'
@@ -9,15 +10,14 @@ import { splitAddr } from './transport.js'
9
10
  import { readCandidatesUntilConnected } from './util.js'
10
11
  import type { WebRTCDialEvents, WebRTCTransportMetrics } from './transport.js'
11
12
  import type { DataChannelOptions } from '../index.js'
12
- import type { LoggerOptions, Connection, ComponentLogger, IncomingStreamData } from '@libp2p/interface'
13
+ import type { LoggerOptions, Connection, ComponentLogger, AbortOptions } from '@libp2p/interface'
13
14
  import type { ConnectionManager, TransportManager } from '@libp2p/interface-internal'
14
15
  import type { Multiaddr } from '@multiformats/multiaddr'
15
16
  import type { ProgressOptions } from 'progress-events'
16
17
 
17
- export interface IncomingStreamOpts extends IncomingStreamData {
18
+ export interface IncomingStreamOptions extends AbortOptions {
18
19
  rtcConfiguration?: RTCConfiguration
19
20
  dataChannelOptions?: Partial<DataChannelOptions>
20
- signal: AbortSignal
21
21
  }
22
22
 
23
23
  export interface ConnectOptions extends LoggerOptions, ProgressOptions<WebRTCDialEvents> {
@@ -67,9 +67,21 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa
67
67
 
68
68
  const messageStream = pbStream(stream).pb(Message)
69
69
  const peerConnection = new RTCPeerConnection(rtcConfiguration)
70
+
71
+ // make sure C++ peer connection is garbage collected
72
+ // https://github.com/murat-dogan/node-datachannel/issues/366#issuecomment-3228453155
73
+ peerConnection.addEventListener('connectionstatechange', () => {
74
+ switch (peerConnection.connectionState) {
75
+ case 'closed':
76
+ peerConnection.close()
77
+ break
78
+ default:
79
+ break
80
+ }
81
+ })
82
+
70
83
  const muxerFactory = new DataChannelMuxerFactory({
71
- logger
72
- }, {
84
+ // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
73
85
  peerConnection,
74
86
  dataChannelOptions: dataChannel
75
87
  })
@@ -97,7 +109,7 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa
97
109
  signal
98
110
  })
99
111
  .catch(err => {
100
- log.error('error sending ICE candidate', err)
112
+ log.error('error sending ICE candidate - %e', err)
101
113
  })
102
114
  }
103
115
  peerConnection.onicecandidateerror = (event) => {
@@ -157,7 +169,17 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa
157
169
  onProgress
158
170
  })
159
171
 
160
- log.trace('initiator connected, closing init channel')
172
+ log.trace('initiator connected')
173
+
174
+ if (channel.readyState !== 'open') {
175
+ log.trace('wait for init channel to open')
176
+ await pEvent(channel, 'open', {
177
+ signal
178
+ })
179
+ }
180
+
181
+ log.trace('closing init channel, starting status')
182
+
161
183
  channel.close()
162
184
 
163
185
  onProgress?.(new CustomProgressEvent('webrtc:close-signaling-stream'))
@@ -1,20 +1,19 @@
1
+ import { pbStream } from '@libp2p/utils'
1
2
  import { multiaddr } from '@multiformats/multiaddr'
2
- import { pbStream } from 'it-protobuf-stream'
3
3
  import { SDPHandshakeFailedError } from '../error.js'
4
4
  import { RTCSessionDescription } from '../webrtc/index.js'
5
5
  import { Message } from './pb/message.js'
6
- import { getConnectionState, readCandidatesUntilConnected } from './util.js'
6
+ import { getConnectionState, getRemotePeer, readCandidatesUntilConnected } from './util.js'
7
7
  import type { RTCPeerConnection } from '../webrtc/index.js'
8
- import type { Logger, IncomingStreamData } from '@libp2p/interface'
8
+ import type { AbortOptions, Connection, Logger, PeerId, Stream } from '@libp2p/interface'
9
9
  import type { Multiaddr } from '@multiformats/multiaddr'
10
10
 
11
- export interface IncomingStreamOpts extends IncomingStreamData {
11
+ export interface IncomingStreamOptions extends AbortOptions {
12
12
  peerConnection: RTCPeerConnection
13
- signal: AbortSignal
14
13
  log: Logger
15
14
  }
16
15
 
17
- export async function handleIncomingStream ({ peerConnection, stream, signal, connection, log }: IncomingStreamOpts): Promise<{ remoteAddress: Multiaddr }> {
16
+ export async function handleIncomingStream (stream: Stream, connection: Connection, { peerConnection, signal, log }: IncomingStreamOptions): Promise<{ remoteAddress: Multiaddr, remotePeer: PeerId }> {
18
17
  log.trace('new inbound signaling stream')
19
18
 
20
19
  const messageStream = pbStream(stream).pb(Message)
@@ -37,7 +36,7 @@ export async function handleIncomingStream ({ peerConnection, stream, signal, co
37
36
  signal
38
37
  })
39
38
  .catch(err => {
40
- log.error('error sending ICE candidate', err)
39
+ log.error('error sending ICE candidate - %e', err)
41
40
  })
42
41
  }
43
42
 
@@ -101,9 +100,13 @@ export async function handleIncomingStream ({ peerConnection, stream, signal, co
101
100
  }
102
101
  }
103
102
 
104
- const remoteAddress = multiaddr(`/webrtc/p2p/${connection.remoteAddr.getPeerId()}`)
103
+ const remotePeer = getRemotePeer(connection.remoteAddr)
104
+ const remoteAddress = multiaddr(`/webrtc/p2p/${remotePeer}`)
105
105
 
106
106
  log.trace('recipient connected to remote address %s', remoteAddress)
107
107
 
108
- return { remoteAddress }
108
+ return {
109
+ remoteAddress,
110
+ remotePeer
111
+ }
109
112
  }
@@ -4,15 +4,16 @@ import { multiaddr } from '@multiformats/multiaddr'
4
4
  import { WebRTC } from '@multiformats/multiaddr-matcher'
5
5
  import { setMaxListeners } from 'main-event'
6
6
  import { SIGNALING_PROTOCOL } from '../constants.js'
7
- import { WebRTCMultiaddrConnection } from '../maconn.js'
8
7
  import { DataChannelMuxerFactory } from '../muxer.js'
8
+ import { toMultiaddrConnection } from '../rtcpeerconnection-to-conn.ts'
9
9
  import { getRtcConfiguration } from '../util.js'
10
10
  import { RTCPeerConnection } from '../webrtc/index.js'
11
11
  import { initiateConnection } from './initiate-connection.js'
12
12
  import { WebRTCPeerListener } from './listener.js'
13
13
  import { handleIncomingStream } from './signaling-stream-handler.js'
14
+ import { getRemotePeer } from './util.ts'
14
15
  import type { DataChannelOptions } from '../index.js'
15
- import type { OutboundConnectionUpgradeEvents, CreateListenerOptions, DialTransportOptions, Transport, Listener, Upgrader, ComponentLogger, Logger, Connection, PeerId, CounterGroup, Metrics, Startable, OpenConnectionProgressEvents, IncomingStreamData, Libp2pEvents } from '@libp2p/interface'
16
+ import type { OutboundConnectionUpgradeEvents, CreateListenerOptions, DialTransportOptions, Transport, Listener, Upgrader, ComponentLogger, Logger, Connection, PeerId, CounterGroup, Metrics, Startable, OpenConnectionProgressEvents, Libp2pEvents, MultiaddrConnection, Stream } from '@libp2p/interface'
16
17
  import type { Registrar, ConnectionManager, TransportManager } from '@libp2p/interface-internal'
17
18
  import type { Multiaddr } from '@multiformats/multiaddr'
18
19
  import type { TypedEventTarget } from 'main-event'
@@ -115,13 +116,13 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
115
116
  }
116
117
 
117
118
  async start (): Promise<void> {
118
- await this.components.registrar.handle(SIGNALING_PROTOCOL, (data: IncomingStreamData) => {
119
+ await this.components.registrar.handle(SIGNALING_PROTOCOL, (stream: Stream, connection: Connection) => {
119
120
  // ensure we don't try to upgrade forever
120
121
  const signal = this.components.upgrader.createInboundAbortSignal(this.shutdownController.signal)
121
122
 
122
- this._onProtocol(data, signal)
123
+ this._onProtocol(stream, connection, signal)
123
124
  .catch(err => {
124
- this.log.error('failed to handle incoming connect from %p', data.connection.remotePeer, err)
125
+ this.log.error('failed to handle incoming connect from %p', connection.remotePeer, err)
125
126
  })
126
127
  .finally(() => {
127
128
  signal.clear()
@@ -180,16 +181,18 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
180
181
  onProgress: options.onProgress
181
182
  })
182
183
 
183
- const webRTCConn = new WebRTCMultiaddrConnection(this.components, {
184
+ const webRTCConn = toMultiaddrConnection({
184
185
  peerConnection,
185
- timeline: { open: Date.now() },
186
186
  remoteAddr: remoteAddress,
187
- metrics: this.metrics?.dialerEvents
187
+ metrics: this.metrics?.dialerEvents,
188
+ direction: 'outbound',
189
+ log: this.components.logger.forComponent('libp2p:webrtc:connection')
188
190
  })
189
191
 
190
192
  const connection = await options.upgrader.upgradeOutbound(webRTCConn, {
191
193
  skipProtection: true,
192
194
  skipEncryption: true,
195
+ remotePeer: getRemotePeer(ma),
193
196
  muxerFactory,
194
197
  onProgress: options.onProgress,
195
198
  signal: options.signal
@@ -201,18 +204,29 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
201
204
  return connection
202
205
  }
203
206
 
204
- async _onProtocol ({ connection, stream }: IncomingStreamData, signal: AbortSignal): Promise<void> {
207
+ async _onProtocol (stream: Stream, connection: Connection, signal: AbortSignal): Promise<void> {
205
208
  const peerConnection = new RTCPeerConnection(await getRtcConfiguration(this.init.rtcConfiguration))
206
- const muxerFactory = new DataChannelMuxerFactory(this.components, {
209
+
210
+ // make sure C++ peer connection is garbage collected
211
+ // https://github.com/murat-dogan/node-datachannel/issues/366#issuecomment-3228453155
212
+ peerConnection.addEventListener('connectionstatechange', () => {
213
+ switch (peerConnection.connectionState) {
214
+ case 'closed':
215
+ peerConnection.close()
216
+ break
217
+ default:
218
+ break
219
+ }
220
+ })
221
+ const muxerFactory = new DataChannelMuxerFactory({
222
+ // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
207
223
  peerConnection,
208
224
  dataChannelOptions: this.init.dataChannel
209
225
  })
210
226
 
211
227
  try {
212
- const { remoteAddress } = await handleIncomingStream({
228
+ const { remoteAddress, remotePeer } = await handleIncomingStream(stream, connection, {
213
229
  peerConnection,
214
- connection,
215
- stream,
216
230
  signal,
217
231
  log: this.log
218
232
  })
@@ -222,16 +236,18 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
222
236
  signal
223
237
  })
224
238
 
225
- const webRTCConn = new WebRTCMultiaddrConnection(this.components, {
239
+ const webRTCConn = toMultiaddrConnection({
226
240
  peerConnection,
227
- timeline: { open: (new Date()).getTime() },
228
241
  remoteAddr: remoteAddress,
229
- metrics: this.metrics?.listenerEvents
242
+ metrics: this.metrics?.listenerEvents,
243
+ direction: 'inbound',
244
+ log: this.components.logger.forComponent('libp2p:webrtc:connection')
230
245
  })
231
246
 
232
247
  await this.components.upgrader.upgradeInbound(webRTCConn, {
233
248
  skipEncryption: true,
234
249
  skipProtection: true,
250
+ remotePeer,
235
251
  muxerFactory,
236
252
  signal
237
253
  })
@@ -247,7 +263,7 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
247
263
  }
248
264
  }
249
265
 
250
- private _closeOnShutdown (pc: RTCPeerConnection, webRTCConn: WebRTCMultiaddrConnection): void {
266
+ private _closeOnShutdown (pc: RTCPeerConnection, webRTCConn: MultiaddrConnection): void {
251
267
  // close the connection on shut down
252
268
  const shutDownListener = (): void => {
253
269
  webRTCConn.close()
@@ -1,12 +1,13 @@
1
- import { ConnectionFailedError, InvalidMessageError } from '@libp2p/interface'
2
- import pDefer from 'p-defer'
1
+ import { ConnectionFailedError, InvalidMessageError, InvalidMultiaddrError } from '@libp2p/interface'
2
+ import { peerIdFromString } from '@libp2p/peer-id'
3
3
  import { CustomProgressEvent } from 'progress-events'
4
4
  import { isFirefox } from '../util.js'
5
5
  import { RTCIceCandidate } from '../webrtc/index.js'
6
6
  import { Message } from './pb/message.js'
7
7
  import type { WebRTCDialEvents } from './transport.js'
8
8
  import type { RTCPeerConnection } from '../webrtc/index.js'
9
- import type { AbortOptions, LoggerOptions, Stream } from '@libp2p/interface'
9
+ import type { AbortOptions, LoggerOptions, PeerId, Stream } from '@libp2p/interface'
10
+ import type { Multiaddr } from '@multiformats/multiaddr'
10
11
  import type { MessageStream } from 'it-protobuf-stream'
11
12
  import type { DeferredPromise } from 'p-defer'
12
13
  import type { ProgressOptions } from 'progress-events'
@@ -17,7 +18,7 @@ export interface ReadCandidatesOptions extends AbortOptions, LoggerOptions, Prog
17
18
 
18
19
  export const readCandidatesUntilConnected = async (pc: RTCPeerConnection, stream: MessageStream<Message, Stream>, options: ReadCandidatesOptions): Promise<void> => {
19
20
  try {
20
- const connectedPromise: DeferredPromise<void> = pDefer()
21
+ const connectedPromise = Promise.withResolvers<void>()
21
22
  resolveOnConnected(pc, connectedPromise)
22
23
 
23
24
  // read candidates until we are connected or we reach the end of the stream
@@ -94,3 +95,19 @@ function resolveOnConnected (pc: RTCPeerConnection, promise: DeferredPromise<voi
94
95
  }
95
96
  }
96
97
  }
98
+
99
+ export function getRemotePeer (ma: Multiaddr): PeerId {
100
+ let remotePeer: PeerId | undefined
101
+
102
+ for (const component of ma.getComponents()) {
103
+ if (component.name === 'p2p') {
104
+ remotePeer = peerIdFromString(component.value ?? '')
105
+ }
106
+ }
107
+
108
+ if (remotePeer == null) {
109
+ throw new InvalidMultiaddrError('Remote peerId must be present in multiaddr')
110
+ }
111
+
112
+ return remotePeer
113
+ }