@libp2p/webrtc 5.2.24-87bc8d4fb → 5.2.24-9a9b11fd4
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/README.md +10 -20
- package/dist/index.min.js +18 -18
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +4 -23
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +4 -23
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +20 -22
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -22
- package/dist/src/index.js.map +1 -1
- package/dist/src/muxer.d.ts +14 -46
- package/dist/src/muxer.d.ts.map +1 -1
- package/dist/src/muxer.js +30 -138
- package/dist/src/muxer.js.map +1 -1
- package/dist/src/private-to-private/initiate-connection.d.ts +2 -3
- package/dist/src/private-to-private/initiate-connection.d.ts.map +1 -1
- package/dist/src/private-to-private/initiate-connection.js +37 -5
- package/dist/src/private-to-private/initiate-connection.js.map +1 -1
- package/dist/src/private-to-private/signaling-stream-handler.d.ts +4 -4
- package/dist/src/private-to-private/signaling-stream-handler.d.ts.map +1 -1
- package/dist/src/private-to-private/signaling-stream-handler.js +19 -7
- package/dist/src/private-to-private/signaling-stream-handler.js.map +1 -1
- package/dist/src/private-to-private/transport.d.ts +2 -9
- package/dist/src/private-to-private/transport.d.ts.map +1 -1
- package/dist/src/private-to-private/transport.js +30 -15
- package/dist/src/private-to-private/transport.js.map +1 -1
- package/dist/src/private-to-private/util.d.ts +3 -2
- package/dist/src/private-to-private/util.d.ts.map +1 -1
- package/dist/src/private-to-private/util.js +26 -14
- package/dist/src/private-to-private/util.js.map +1 -1
- package/dist/src/private-to-public/listener.d.ts.map +1 -1
- package/dist/src/private-to-public/listener.js +21 -15
- package/dist/src/private-to-public/listener.js.map +1 -1
- package/dist/src/private-to-public/transport.d.ts +0 -8
- package/dist/src/private-to-public/transport.d.ts.map +1 -1
- package/dist/src/private-to-public/transport.js +3 -2
- package/dist/src/private-to-public/transport.js.map +1 -1
- package/dist/src/private-to-public/utils/connect.d.ts +1 -1
- package/dist/src/private-to-public/utils/connect.d.ts.map +1 -1
- package/dist/src/private-to-public/utils/connect.js +17 -14
- package/dist/src/private-to-public/utils/connect.js.map +1 -1
- package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts +4 -4
- package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts.map +1 -1
- package/dist/src/private-to-public/utils/get-rtcpeerconnection.js +13 -2
- package/dist/src/private-to-public/utils/get-rtcpeerconnection.js.map +1 -1
- package/dist/src/private-to-public/utils/sdp.d.ts.map +1 -1
- package/dist/src/private-to-public/utils/sdp.js +25 -13
- package/dist/src/private-to-public/utils/sdp.js.map +1 -1
- package/dist/src/private-to-public/utils/stun-listener.js +1 -1
- package/dist/src/private-to-public/utils/stun-listener.js.map +1 -1
- package/dist/src/rtcpeerconnection-to-conn.d.ts +12 -0
- package/dist/src/rtcpeerconnection-to-conn.d.ts.map +1 -0
- package/dist/src/rtcpeerconnection-to-conn.js +46 -0
- package/dist/src/rtcpeerconnection-to-conn.js.map +1 -0
- package/dist/src/stream.d.ts +14 -26
- package/dist/src/stream.d.ts.map +1 -1
- package/dist/src/stream.js +134 -204
- package/dist/src/stream.js.map +1 -1
- package/dist/src/util.d.ts +3 -1
- package/dist/src/util.d.ts.map +1 -1
- package/dist/src/util.js +19 -0
- package/dist/src/util.js.map +1 -1
- package/dist/src/webrtc/index.d.ts +1 -1
- package/dist/src/webrtc/index.d.ts.map +1 -1
- package/dist/src/webrtc/index.js +1 -1
- package/dist/src/webrtc/index.js.map +1 -1
- package/package.json +26 -29
- package/src/constants.ts +5 -28
- package/src/index.ts +21 -22
- package/src/muxer.ts +39 -169
- package/src/private-to-private/initiate-connection.ts +46 -8
- package/src/private-to-private/signaling-stream-handler.ts +23 -10
- package/src/private-to-private/transport.ts +33 -25
- package/src/private-to-private/util.ts +33 -16
- package/src/private-to-public/listener.ts +22 -15
- package/src/private-to-public/transport.ts +3 -12
- package/src/private-to-public/utils/connect.ts +18 -15
- package/src/private-to-public/utils/get-rtcpeerconnection.ts +16 -4
- package/src/private-to-public/utils/sdp.ts +29 -13
- package/src/private-to-public/utils/stun-listener.ts +1 -1
- package/src/rtcpeerconnection-to-conn.ts +66 -0
- package/src/stream.ts +153 -237
- package/src/util.ts +22 -1
- package/src/webrtc/index.ts +1 -1
- package/dist/src/maconn.d.ts +0 -58
- package/dist/src/maconn.d.ts.map +0 -1
- package/dist/src/maconn.js +0 -56
- package/dist/src/maconn.js.map +0 -1
- package/src/maconn.ts +0 -101
@@ -1,4 +1,5 @@
|
|
1
|
-
import { pbStream } from '
|
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,
|
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
|
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
|
-
|
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
|
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 {
|
6
|
+
import { getRemotePeer, readCandidatesUntilConnected } from './util.js'
|
7
7
|
import type { RTCPeerConnection } from '../webrtc/index.js'
|
8
|
-
import type { Logger,
|
8
|
+
import type { AbortOptions, Connection, Logger, PeerId, Stream } from '@libp2p/interface'
|
9
9
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
10
10
|
|
11
|
-
export interface
|
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 (
|
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 (
|
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
|
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 {
|
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,
|
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, (
|
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(
|
115
|
+
this._onProtocol(stream, connection, signal)
|
123
116
|
.catch(err => {
|
124
|
-
this.log.error('failed to handle incoming connect from %p',
|
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 =
|
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 (
|
199
|
+
async _onProtocol (stream: Stream, connection: Connection, signal: AbortSignal): Promise<void> {
|
205
200
|
const peerConnection = new RTCPeerConnection(await getRtcConfiguration(this.init.rtcConfiguration))
|
206
|
-
|
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 =
|
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:
|
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
|
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
|
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
|
-
})
|
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 &&
|
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
|
83
|
-
|
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(
|
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
|
4
|
-
import {
|
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,
|
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 &&
|
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,
|
125
|
+
udpMuxServer = this.startUDPMuxServer(host, port, type === 'ip4' ? 4 : 6)
|
123
126
|
UDP_MUX_LISTENERS.push(udpMuxServer)
|
124
127
|
}
|
125
128
|
|
126
|
-
if (
|
129
|
+
if (type === 'ip4') {
|
127
130
|
udpMuxServer.isIPv4 = true
|
128
|
-
} else if (
|
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
|
244
|
+
const components = ma.getComponents()
|
242
245
|
|
243
|
-
for (let j = 0; j <
|
244
|
-
if (
|
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 (
|
251
|
-
|
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 =
|
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.
|
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
|
-
|
180
|
+
remotePeer: theirPeerId,
|
190
181
|
privateKey: this.components.privateKey
|
191
182
|
})
|
192
183
|
} catch (err) {
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import { noise } from '@
|
2
|
-
import {
|
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
|
-
|
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
|
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
|
-
|
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 =
|
126
|
+
const maConn = toMultiaddrConnection({
|
127
127
|
peerConnection,
|
128
128
|
remoteAddr: options.remoteAddr,
|
129
|
-
|
130
|
-
|
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(
|
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.
|
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.
|
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
|
}
|