@libp2p/webrtc 5.2.24 → 6.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.
Files changed (91) hide show
  1. package/README.md +10 -20
  2. package/dist/index.min.js +28 -29
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/constants.d.ts +4 -23
  5. package/dist/src/constants.d.ts.map +1 -1
  6. package/dist/src/constants.js +4 -23
  7. package/dist/src/constants.js.map +1 -1
  8. package/dist/src/index.d.ts +20 -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 +30 -138
  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 +37 -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 +19 -7
  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 -9
  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 -2
  29. package/dist/src/private-to-private/util.d.ts.map +1 -1
  30. package/dist/src/private-to-private/util.js +26 -14
  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 +0 -8
  36. package/dist/src/private-to-public/transport.d.ts.map +1 -1
  37. package/dist/src/private-to-public/transport.js +3 -2
  38. package/dist/src/private-to-public/transport.js.map +1 -1
  39. package/dist/src/private-to-public/utils/connect.d.ts +1 -1
  40. package/dist/src/private-to-public/utils/connect.d.ts.map +1 -1
  41. package/dist/src/private-to-public/utils/connect.js +17 -14
  42. package/dist/src/private-to-public/utils/connect.js.map +1 -1
  43. package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts +4 -4
  44. package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts.map +1 -1
  45. package/dist/src/private-to-public/utils/get-rtcpeerconnection.js +13 -2
  46. package/dist/src/private-to-public/utils/get-rtcpeerconnection.js.map +1 -1
  47. package/dist/src/private-to-public/utils/sdp.d.ts.map +1 -1
  48. package/dist/src/private-to-public/utils/sdp.js +25 -13
  49. package/dist/src/private-to-public/utils/sdp.js.map +1 -1
  50. package/dist/src/private-to-public/utils/stun-listener.js +1 -1
  51. package/dist/src/private-to-public/utils/stun-listener.js.map +1 -1
  52. package/dist/src/rtcpeerconnection-to-conn.d.ts +12 -0
  53. package/dist/src/rtcpeerconnection-to-conn.d.ts.map +1 -0
  54. package/dist/src/rtcpeerconnection-to-conn.js +46 -0
  55. package/dist/src/rtcpeerconnection-to-conn.js.map +1 -0
  56. package/dist/src/stream.d.ts +14 -26
  57. package/dist/src/stream.d.ts.map +1 -1
  58. package/dist/src/stream.js +134 -204
  59. package/dist/src/stream.js.map +1 -1
  60. package/dist/src/util.d.ts +3 -1
  61. package/dist/src/util.d.ts.map +1 -1
  62. package/dist/src/util.js +19 -0
  63. package/dist/src/util.js.map +1 -1
  64. package/dist/src/webrtc/index.d.ts +1 -1
  65. package/dist/src/webrtc/index.d.ts.map +1 -1
  66. package/dist/src/webrtc/index.js +1 -1
  67. package/dist/src/webrtc/index.js.map +1 -1
  68. package/package.json +28 -31
  69. package/src/constants.ts +5 -28
  70. package/src/index.ts +21 -22
  71. package/src/muxer.ts +39 -169
  72. package/src/private-to-private/initiate-connection.ts +46 -8
  73. package/src/private-to-private/signaling-stream-handler.ts +23 -10
  74. package/src/private-to-private/transport.ts +33 -25
  75. package/src/private-to-private/util.ts +33 -16
  76. package/src/private-to-public/listener.ts +22 -15
  77. package/src/private-to-public/transport.ts +3 -12
  78. package/src/private-to-public/utils/connect.ts +18 -15
  79. package/src/private-to-public/utils/get-rtcpeerconnection.ts +16 -4
  80. package/src/private-to-public/utils/sdp.ts +29 -13
  81. package/src/private-to-public/utils/stun-listener.ts +1 -1
  82. package/src/rtcpeerconnection-to-conn.ts +66 -0
  83. package/src/stream.ts +153 -237
  84. package/src/util.ts +22 -1
  85. package/src/webrtc/index.ts +1 -1
  86. package/dist/src/maconn.d.ts +0 -58
  87. package/dist/src/maconn.d.ts.map +0 -1
  88. package/dist/src/maconn.js +0 -56
  89. package/dist/src/maconn.js.map +0 -1
  90. package/dist/typedoc-urls.json +0 -14
  91. package/src/maconn.ts +0 -101
@@ -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
  })
@@ -82,10 +94,20 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa
82
94
 
83
95
  // setup callback to write ICE candidates to the remote peer
84
96
  peerConnection.onicecandidate = ({ candidate }) => {
97
+ if (peerConnection.connectionState === 'connected') {
98
+ log.trace('ignore new ice candidate as peer connection is already connected')
99
+ return
100
+ }
101
+
85
102
  // a null candidate means end-of-candidates, an empty string candidate
86
103
  // means end-of-candidates for this generation, otherwise this should
87
104
  // be a valid candidate object
88
105
  // see - https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent
106
+ if (candidate == null || candidate?.candidate === '') {
107
+ log.trace('initiator detected end of ICE candidates')
108
+ return
109
+ }
110
+
89
111
  const data = JSON.stringify(candidate?.toJSON() ?? null)
90
112
 
91
113
  log.trace('initiator sending ICE candidate %o', candidate)
@@ -97,7 +119,7 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa
97
119
  signal
98
120
  })
99
121
  .catch(err => {
100
- log.error('error sending ICE candidate', err)
122
+ log.error('error sending ICE candidate - %e', err)
101
123
  })
102
124
  }
103
125
  peerConnection.onicecandidateerror = (event) => {
@@ -157,9 +179,25 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa
157
179
  onProgress
158
180
  })
159
181
 
160
- log.trace('initiator connected, closing init channel')
182
+ log.trace('initiator connected')
183
+
184
+ if (channel.readyState !== 'open') {
185
+ log.trace('wait for init channel to open')
186
+ await pEvent(channel, 'open', {
187
+ signal
188
+ })
189
+ }
190
+
191
+ log.trace('closing init channel')
161
192
  channel.close()
162
193
 
194
+ // wait for init channel to close before proceeding, otherwise the channel
195
+ // id can be reused before both sides have seen the channel close
196
+ log.trace('waiting for init channel to close')
197
+ await pEvent(channel, 'close', {
198
+ signal
199
+ })
200
+
163
201
  onProgress?.(new CustomProgressEvent('webrtc:close-signaling-stream'))
164
202
 
165
203
  log.trace('closing signaling channel')
@@ -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 { 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)
@@ -22,10 +21,20 @@ export async function handleIncomingStream ({ peerConnection, stream, signal, co
22
21
  try {
23
22
  // candidate callbacks
24
23
  peerConnection.onicecandidate = ({ candidate }) => {
24
+ if (peerConnection.connectionState === 'connected') {
25
+ log.trace('ignore new ice candidate as peer connection is already connected')
26
+ return
27
+ }
28
+
25
29
  // a null candidate means end-of-candidates, an empty string candidate
26
30
  // means end-of-candidates for this generation, otherwise this should
27
31
  // be a valid candidate object
28
32
  // see - https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent
33
+ if (candidate == null || candidate?.candidate === '') {
34
+ log.trace('recipient detected end of ICE candidates')
35
+ return
36
+ }
37
+
29
38
  const data = JSON.stringify(candidate?.toJSON() ?? null)
30
39
 
31
40
  log.trace('recipient sending ICE candidate %s', data)
@@ -37,7 +46,7 @@ export async function handleIncomingStream ({ peerConnection, stream, signal, co
37
46
  signal
38
47
  })
39
48
  .catch(err => {
40
- log.error('error sending ICE candidate', err)
49
+ log.error('error sending ICE candidate - %e', err)
41
50
  })
42
51
  }
43
52
 
@@ -91,7 +100,7 @@ export async function handleIncomingStream ({ peerConnection, stream, signal, co
91
100
  log
92
101
  })
93
102
  } catch (err: any) {
94
- if (getConnectionState(peerConnection) !== 'connected') {
103
+ if (peerConnection.connectionState !== 'connected') {
95
104
  log.error('error while handling signaling stream from peer %a', connection.remoteAddr, err)
96
105
 
97
106
  peerConnection.close()
@@ -101,9 +110,13 @@ export async function handleIncomingStream ({ peerConnection, stream, signal, co
101
110
  }
102
111
  }
103
112
 
104
- const remoteAddress = multiaddr(`/webrtc/p2p/${connection.remoteAddr.getPeerId()}`)
113
+ const remotePeer = getRemotePeer(connection.remoteAddr)
114
+ const remoteAddress = multiaddr(`/webrtc/p2p/${remotePeer}`)
105
115
 
106
116
  log.trace('recipient connected to remote address %s', remoteAddress)
107
117
 
108
- return { remoteAddress }
118
+ return {
119
+ remoteAddress,
120
+ remotePeer
121
+ }
109
122
  }
@@ -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'
@@ -30,14 +31,6 @@ export interface WebRTCTransportInit {
30
31
  * Any options here will be applied to any RTCDataChannels that are opened.
31
32
  */
32
33
  dataChannel?: DataChannelOptions
33
-
34
- /**
35
- * Inbound connections must complete the upgrade within this many ms
36
- *
37
- * @default 30_000
38
- * @deprecated configure `connectionManager.inboundUpgradeTimeout` instead
39
- */
40
- inboundConnectionTimeout?: number
41
34
  }
42
35
 
43
36
  export interface WebRTCTransportComponents {
@@ -115,13 +108,13 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
115
108
  }
116
109
 
117
110
  async start (): Promise<void> {
118
- await this.components.registrar.handle(SIGNALING_PROTOCOL, (data: IncomingStreamData) => {
111
+ await this.components.registrar.handle(SIGNALING_PROTOCOL, (stream: Stream, connection: Connection) => {
119
112
  // ensure we don't try to upgrade forever
120
113
  const signal = this.components.upgrader.createInboundAbortSignal(this.shutdownController.signal)
121
114
 
122
- this._onProtocol(data, signal)
115
+ this._onProtocol(stream, connection, signal)
123
116
  .catch(err => {
124
- this.log.error('failed to handle incoming connect from %p', data.connection.remotePeer, err)
117
+ this.log.error('failed to handle incoming connect from %p', connection.remotePeer, err)
125
118
  })
126
119
  .finally(() => {
127
120
  signal.clear()
@@ -180,16 +173,18 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
180
173
  onProgress: options.onProgress
181
174
  })
182
175
 
183
- const webRTCConn = new WebRTCMultiaddrConnection(this.components, {
176
+ const webRTCConn = toMultiaddrConnection({
184
177
  peerConnection,
185
- timeline: { open: Date.now() },
186
178
  remoteAddr: remoteAddress,
187
- metrics: this.metrics?.dialerEvents
179
+ metrics: this.metrics?.dialerEvents,
180
+ direction: 'outbound',
181
+ log: this.components.logger.forComponent('libp2p:webrtc:connection')
188
182
  })
189
183
 
190
184
  const connection = await options.upgrader.upgradeOutbound(webRTCConn, {
191
185
  skipProtection: true,
192
186
  skipEncryption: true,
187
+ remotePeer: getRemotePeer(ma),
193
188
  muxerFactory,
194
189
  onProgress: options.onProgress,
195
190
  signal: options.signal
@@ -201,18 +196,29 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
201
196
  return connection
202
197
  }
203
198
 
204
- async _onProtocol ({ connection, stream }: IncomingStreamData, signal: AbortSignal): Promise<void> {
199
+ async _onProtocol (stream: Stream, connection: Connection, signal: AbortSignal): Promise<void> {
205
200
  const peerConnection = new RTCPeerConnection(await getRtcConfiguration(this.init.rtcConfiguration))
206
- const muxerFactory = new DataChannelMuxerFactory(this.components, {
201
+
202
+ // make sure C++ peer connection is garbage collected
203
+ // https://github.com/murat-dogan/node-datachannel/issues/366#issuecomment-3228453155
204
+ peerConnection.addEventListener('connectionstatechange', () => {
205
+ switch (peerConnection.connectionState) {
206
+ case 'closed':
207
+ peerConnection.close()
208
+ break
209
+ default:
210
+ break
211
+ }
212
+ })
213
+ const muxerFactory = new DataChannelMuxerFactory({
214
+ // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
207
215
  peerConnection,
208
216
  dataChannelOptions: this.init.dataChannel
209
217
  })
210
218
 
211
219
  try {
212
- const { remoteAddress } = await handleIncomingStream({
220
+ const { remoteAddress, remotePeer } = await handleIncomingStream(stream, connection, {
213
221
  peerConnection,
214
- connection,
215
- stream,
216
222
  signal,
217
223
  log: this.log
218
224
  })
@@ -222,16 +228,18 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
222
228
  signal
223
229
  })
224
230
 
225
- const webRTCConn = new WebRTCMultiaddrConnection(this.components, {
231
+ const webRTCConn = toMultiaddrConnection({
226
232
  peerConnection,
227
- timeline: { open: (new Date()).getTime() },
228
233
  remoteAddr: remoteAddress,
229
- metrics: this.metrics?.listenerEvents
234
+ metrics: this.metrics?.listenerEvents,
235
+ direction: 'inbound',
236
+ log: this.components.logger.forComponent('libp2p:webrtc:connection')
230
237
  })
231
238
 
232
239
  await this.components.upgrader.upgradeInbound(webRTCConn, {
233
240
  skipEncryption: true,
234
241
  skipProtection: true,
242
+ remotePeer,
235
243
  muxerFactory,
236
244
  signal
237
245
  })
@@ -247,7 +255,7 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
247
255
  }
248
256
  }
249
257
 
250
- private _closeOnShutdown (pc: RTCPeerConnection, webRTCConn: WebRTCMultiaddrConnection): void {
258
+ private _closeOnShutdown (pc: RTCPeerConnection, webRTCConn: MultiaddrConnection): void {
251
259
  // close the connection on shut down
252
260
  const shutDownListener = (): void => {
253
261
  webRTCConn.close()
@@ -1,12 +1,12 @@
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
- import { isFirefox } from '../util.js'
5
4
  import { RTCIceCandidate } from '../webrtc/index.js'
6
5
  import { Message } from './pb/message.js'
7
6
  import type { WebRTCDialEvents } from './transport.js'
8
7
  import type { RTCPeerConnection } from '../webrtc/index.js'
9
- import type { AbortOptions, LoggerOptions, Stream } from '@libp2p/interface'
8
+ import type { AbortOptions, LoggerOptions, PeerId, Stream } from '@libp2p/interface'
9
+ import type { Multiaddr } from '@multiformats/multiaddr'
10
10
  import type { MessageStream } from 'it-protobuf-stream'
11
11
  import type { DeferredPromise } from 'p-defer'
12
12
  import type { ProgressOptions } from 'progress-events'
@@ -17,7 +17,7 @@ export interface ReadCandidatesOptions extends AbortOptions, LoggerOptions, Prog
17
17
 
18
18
  export const readCandidatesUntilConnected = async (pc: RTCPeerConnection, stream: MessageStream<Message, Stream>, options: ReadCandidatesOptions): Promise<void> => {
19
19
  try {
20
- const connectedPromise: DeferredPromise<void> = pDefer()
20
+ const connectedPromise = Promise.withResolvers<void>()
21
21
  resolveOnConnected(pc, connectedPromise)
22
22
 
23
23
  // read candidates until we are connected or we reach the end of the stream
@@ -27,7 +27,7 @@ export const readCandidatesUntilConnected = async (pc: RTCPeerConnection, stream
27
27
  connectedPromise.promise,
28
28
  stream.read({
29
29
  signal: options.signal
30
- }).catch(() => {})
30
+ })
31
31
  ])
32
32
 
33
33
  // stream ended or we became connected
@@ -62,35 +62,52 @@ export const readCandidatesUntilConnected = async (pc: RTCPeerConnection, stream
62
62
  options.onProgress?.(new CustomProgressEvent<string>('webrtc:add-ice-candidate', candidate.candidate))
63
63
  await pc.addIceCandidate(candidate)
64
64
  } catch (err) {
65
- options.log.error('%s bad candidate received', options.direction, candidateInit, err)
65
+ options.log.error('%s bad candidate received %o - %e', options.direction, candidateInit, err)
66
66
  }
67
67
  }
68
68
  } catch (err) {
69
- options.log.error('%s error parsing ICE candidate', options.direction, err)
69
+ options.log.error('%s error parsing ICE candidate - %e', options.direction, err)
70
70
 
71
- if (options.signal?.aborted === true && getConnectionState(pc) !== 'connected') {
71
+ if (options.signal?.aborted === true && pc.connectionState !== 'connected') {
72
72
  throw err
73
73
  }
74
74
  }
75
75
  }
76
76
 
77
- export function getConnectionState (pc: RTCPeerConnection): string {
78
- return isFirefox ? pc.iceConnectionState : pc.connectionState
79
- }
80
-
81
77
  function resolveOnConnected (pc: RTCPeerConnection, promise: DeferredPromise<void>): void {
82
- pc[isFirefox ? 'oniceconnectionstatechange' : 'onconnectionstatechange'] = (_) => {
83
- switch (getConnectionState(pc)) {
78
+ if (pc.connectionState === 'connected') {
79
+ promise.resolve()
80
+ return
81
+ }
82
+
83
+ pc.onconnectionstatechange = (_) => {
84
+ switch (pc.connectionState) {
84
85
  case 'connected':
85
86
  promise.resolve()
86
87
  break
87
88
  case 'failed':
88
89
  case 'disconnected':
89
90
  case 'closed':
90
- promise.reject(new ConnectionFailedError('RTCPeerConnection was closed'))
91
+ promise.reject(new ConnectionFailedError(`RTCPeerConnection connection state became "${pc.connectionState}"`))
91
92
  break
92
93
  default:
93
94
  break
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
+ }
@@ -1,12 +1,11 @@
1
1
  import { isIPv4 } from '@chainsafe/is-ip'
2
2
  import { InvalidParametersError } from '@libp2p/interface'
3
- import { getThinWaistAddresses } from '@libp2p/utils/get-thin-waist-addresses'
4
- import { multiaddr, fromStringTuples } from '@multiformats/multiaddr'
3
+ import { getNetConfig, getThinWaistAddresses } from '@libp2p/utils'
4
+ import { CODE_CERTHASH, CODE_WEBRTC_DIRECT, multiaddr } from '@multiformats/multiaddr'
5
5
  import { WebRTCDirect } from '@multiformats/multiaddr-matcher'
6
6
  import getPort from 'get-port'
7
7
  import { TypedEventEmitter, setMaxListeners } from 'main-event'
8
8
  import pWaitFor from 'p-wait-for'
9
- import { CODEC_CERTHASH, CODEC_WEBRTC_DIRECT } from '../constants.js'
10
9
  import { connect } from './utils/connect.js'
11
10
  import { createDialerRTCPeerConnection } from './utils/get-rtcpeerconnection.js'
12
11
  import { stunListener } from './utils/stun-listener.js'
@@ -94,7 +93,11 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
94
93
  }
95
94
 
96
95
  async listen (ma: Multiaddr): Promise<void> {
97
- const { host, port, family } = ma.toOptions()
96
+ const { host, port, type, protocol } = getNetConfig(ma)
97
+
98
+ if (port == null || protocol !== 'udp' || (type !== 'ip4' && type !== 'ip6')) {
99
+ throw new InvalidParametersError(`Multiaddr ${ma} was not an IPv4 or IPv6 address or was missing a UDP port`)
100
+ }
98
101
 
99
102
  let udpMuxServer: UDPMuxServer | undefined
100
103
 
@@ -106,7 +109,7 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
106
109
  udpMuxServer = UDP_MUX_LISTENERS.find(s => s.port === port)
107
110
 
108
111
  // make sure the port is free for the given family
109
- if (udpMuxServer != null && ((udpMuxServer.isIPv4 && family === 4) || (udpMuxServer.isIPv6 && family === 6))) {
112
+ if (udpMuxServer != null && ((udpMuxServer.isIPv4 && type === 'ip4') || (udpMuxServer.isIPv6 && type === 'ip6'))) {
110
113
  throw new InvalidParametersError(`There is already a listener for ${host}:${port}`)
111
114
  }
112
115
 
@@ -119,13 +122,13 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
119
122
  // start the mux server if we don't have one already
120
123
  if (udpMuxServer == null) {
121
124
  this.log('starting UDP mux server on %s:%p', host, port)
122
- udpMuxServer = this.startUDPMuxServer(host, port, family)
125
+ udpMuxServer = this.startUDPMuxServer(host, port, type === 'ip4' ? 4 : 6)
123
126
  UDP_MUX_LISTENERS.push(udpMuxServer)
124
127
  }
125
128
 
126
- if (family === 4) {
129
+ if (type === 'ip4') {
127
130
  udpMuxServer.isIPv4 = true
128
- } else if (family === 6) {
131
+ } else if (type === 'ip6') {
129
132
  udpMuxServer.isIPv6 = true
130
133
  }
131
134
 
@@ -205,7 +208,7 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
205
208
  metrics: this.components.metrics,
206
209
  events: this.metrics?.listenerEvents,
207
210
  signal,
208
- remoteAddr: multiaddr(`/ip${isIPv4(remoteHost) ? 4 : 6}/${remoteHost}/udp/${remotePort}`),
211
+ remoteAddr: multiaddr(`/ip${isIPv4(remoteHost) ? 4 : 6}/${remoteHost}/udp/${remotePort}/webrtc-direct`),
209
212
  dataChannel: this.init.dataChannel,
210
213
  upgrader: this.init.upgrader,
211
214
  peerId: this.components.peerId,
@@ -238,19 +241,23 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
238
241
  }
239
242
 
240
243
  // add the certhash if it is missing
241
- const tuples = ma.stringTuples()
244
+ const components = ma.getComponents()
242
245
 
243
- for (let j = 0; j < tuples.length; j++) {
244
- if (tuples[j][0] !== CODEC_WEBRTC_DIRECT) {
246
+ for (let j = 0; j < components.length; j++) {
247
+ if (components[j].code !== CODE_WEBRTC_DIRECT) {
245
248
  continue
246
249
  }
247
250
 
248
251
  const certhashIndex = j + 1
249
252
 
250
- if (tuples[certhashIndex] == null || tuples[certhashIndex][0] !== CODEC_CERTHASH) {
251
- tuples.splice(certhashIndex, 0, [CODEC_CERTHASH, this.certificate?.certhash])
253
+ if (components[certhashIndex] == null || components[certhashIndex].code !== CODE_CERTHASH) {
254
+ components.splice(certhashIndex, 0, {
255
+ code: CODE_CERTHASH,
256
+ name: 'certhash',
257
+ value: this.certificate?.certhash
258
+ })
252
259
 
253
- ma = fromStringTuples(tuples)
260
+ ma = multiaddr(components)
254
261
  multiaddrs[i] = ma
255
262
  }
256
263
  }
@@ -1,6 +1,7 @@
1
1
  import { generateKeyPair, privateKeyToCryptoKeyPair } from '@libp2p/crypto/keys'
2
2
  import { InvalidParametersError, NotFoundError, NotStartedError, serviceCapabilities, transportSymbol } from '@libp2p/interface'
3
3
  import { peerIdFromString } from '@libp2p/peer-id'
4
+ import { CODE_P2P } from '@multiformats/multiaddr'
4
5
  import { WebRTCDirect } from '@multiformats/multiaddr-matcher'
5
6
  import { BasicConstraintsExtension, X509Certificate, X509CertificateGenerator } from '@peculiar/x509'
6
7
  import { Key } from 'interface-datastore'
@@ -50,11 +51,6 @@ export interface WebRTCTransportDirectInit {
50
51
  */
51
52
  dataChannel?: DataChannelOptions
52
53
 
53
- /**
54
- * @deprecated use `certificate` instead - this option will be removed in a future release
55
- */
56
- certificates?: TransportCertificate[]
57
-
58
54
  /**
59
55
  * Use an existing TLS certificate to secure incoming connections or supply
60
56
  * settings to generate one.
@@ -67,11 +63,6 @@ export interface WebRTCTransportDirectInit {
67
63
  */
68
64
  certificate?: TransportCertificate
69
65
 
70
- /**
71
- * @deprecated this setting is ignored and will be removed in a future release
72
- */
73
- useLibjuice?: boolean
74
-
75
66
  /**
76
67
  * The key the certificate is stored in the datastore under
77
68
  *
@@ -164,7 +155,7 @@ export class WebRTCDirectTransport implements Transport, Startable {
164
155
  options.signal.throwIfAborted()
165
156
 
166
157
  let theirPeerId: PeerId | undefined
167
- const remotePeerString = ma.getPeerId()
158
+ const remotePeerString = ma.getComponents().findLast(c => c.code === CODE_P2P)?.value
168
159
  if (remotePeerString != null) {
169
160
  theirPeerId = peerIdFromString(remotePeerString)
170
161
  }
@@ -186,7 +177,7 @@ export class WebRTCDirectTransport implements Transport, Startable {
186
177
  dataChannel: this.init.dataChannel,
187
178
  upgrader: options.upgrader,
188
179
  peerId: this.components.peerId,
189
- remotePeerId: theirPeerId,
180
+ remotePeer: theirPeerId,
190
181
  privateKey: this.components.privateKey
191
182
  })
192
183
  } catch (err) {
@@ -1,8 +1,8 @@
1
- import { noise } from '@chainsafe/libp2p-noise'
2
- import { raceEvent } from 'race-event'
1
+ import { noise } from '@libp2p/noise'
2
+ import { pEvent } from 'p-event'
3
3
  import { WebRTCTransportError } from '../../error.js'
4
- import { WebRTCMultiaddrConnection } from '../../maconn.js'
5
4
  import { DataChannelMuxerFactory } from '../../muxer.js'
5
+ import { toMultiaddrConnection } from '../../rtcpeerconnection-to-conn.ts'
6
6
  import { createStream } from '../../stream.js'
7
7
  import { isFirefox } from '../../util.js'
8
8
  import { generateNoisePrologue } from './generate-noise-prologue.js'
@@ -22,7 +22,7 @@ export interface ConnectOptions {
22
22
  dataChannel?: DataChannelOptions
23
23
  upgrader: Upgrader
24
24
  peerId: PeerId
25
- remotePeerId?: PeerId
25
+ remotePeer?: PeerId
26
26
  signal: AbortSignal
27
27
  privateKey: PrivateKey
28
28
  }
@@ -83,7 +83,7 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
83
83
 
84
84
  if (handshakeDataChannel.readyState !== 'open') {
85
85
  options.log.trace('%s wait for handshake channel to open, starting status %s', options.role, handshakeDataChannel.readyState)
86
- await raceEvent(handshakeDataChannel, 'open', options.signal)
86
+ await pEvent(handshakeDataChannel, 'open', options)
87
87
  }
88
88
 
89
89
  options.log.trace('%s handshake channel opened', options.role)
@@ -116,20 +116,19 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
116
116
  const handshakeStream = createStream({
117
117
  channel: handshakeDataChannel,
118
118
  direction: 'outbound',
119
- handshake: true,
119
+ isHandshake: true,
120
120
  log: options.log,
121
121
  ...(options.dataChannel ?? {})
122
122
  })
123
123
 
124
124
  // Creating the connection before completion of the noise
125
125
  // handshake ensures that the stream opening callback is set up
126
- const maConn = new WebRTCMultiaddrConnection(options, {
126
+ const maConn = toMultiaddrConnection({
127
127
  peerConnection,
128
128
  remoteAddr: options.remoteAddr,
129
- timeline: {
130
- open: Date.now()
131
- },
132
- metrics: options.events
129
+ metrics: options.events,
130
+ direction: options.role === 'client' ? 'outbound' : 'inbound',
131
+ log: options.logger.forComponent('libp2p:webrtc-direct:connection')
133
132
  })
134
133
 
135
134
  peerConnection.addEventListener(CONNECTION_STATE_CHANGE_EVENT, () => {
@@ -150,7 +149,8 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
150
149
  // Track opened peer connection
151
150
  options.events?.increment({ peer_connection: true })
152
151
 
153
- const muxerFactory = new DataChannelMuxerFactory(options, {
152
+ const muxerFactory = new DataChannelMuxerFactory({
153
+ // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
154
154
  peerConnection,
155
155
  metrics: options.events,
156
156
  dataChannelOptions: options.dataChannel
@@ -161,8 +161,8 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
161
161
  // handshake. Therefore, we need to secure an inbound noise connection
162
162
  // from the server.
163
163
  options.log.trace('%s secure inbound', options.role)
164
- await connectionEncrypter.secureInbound(handshakeStream, {
165
- remotePeer: options.remotePeerId,
164
+ const result = await connectionEncrypter.secureInbound(handshakeStream, {
165
+ remotePeer: options.remotePeer,
166
166
  signal: options.signal,
167
167
  skipStreamMuxerNegotiation: true
168
168
  })
@@ -171,6 +171,7 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
171
171
  return await options.upgrader.upgradeOutbound(maConn, {
172
172
  skipProtection: true,
173
173
  skipEncryption: true,
174
+ remotePeer: result.remotePeer,
174
175
  muxerFactory,
175
176
  signal: options.signal
176
177
  })
@@ -181,7 +182,7 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
181
182
  // the client.
182
183
  options.log.trace('%s secure outbound', options.role)
183
184
  const result = await connectionEncrypter.secureOutbound(handshakeStream, {
184
- remotePeer: options.remotePeerId,
185
+ remotePeer: options.remotePeer,
185
186
  signal: options.signal,
186
187
  skipStreamMuxerNegotiation: true
187
188
  })
@@ -193,11 +194,13 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
193
194
  await options.upgrader.upgradeInbound(maConn, {
194
195
  skipProtection: true,
195
196
  skipEncryption: true,
197
+ remotePeer: result.remotePeer,
196
198
  muxerFactory,
197
199
  signal: options.signal
198
200
  })
199
201
  } catch (err) {
200
202
  handshakeDataChannel.close()
203
+ peerConnection.close()
201
204
 
202
205
  throw err
203
206
  }