@libp2p/webrtc 3.2.10 → 3.2.11-0b4a2ee79
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 +1 -1
- package/dist/index.min.js +12 -12
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/maconn.d.ts +6 -1
- package/dist/src/maconn.d.ts.map +1 -1
- package/dist/src/maconn.js +6 -6
- package/dist/src/maconn.js.map +1 -1
- package/dist/src/muxer.d.ts +13 -2
- package/dist/src/muxer.d.ts.map +1 -1
- package/dist/src/muxer.js +36 -7
- package/dist/src/muxer.js.map +1 -1
- package/dist/src/private-to-private/initiate-connection.d.ts +3 -2
- package/dist/src/private-to-private/initiate-connection.d.ts.map +1 -1
- package/dist/src/private-to-private/initiate-connection.js +3 -4
- package/dist/src/private-to-private/initiate-connection.js.map +1 -1
- package/dist/src/private-to-private/signaling-stream-handler.d.ts +3 -1
- package/dist/src/private-to-private/signaling-stream-handler.d.ts.map +1 -1
- package/dist/src/private-to-private/signaling-stream-handler.js +3 -4
- package/dist/src/private-to-private/signaling-stream-handler.js.map +1 -1
- package/dist/src/private-to-private/transport.d.ts +3 -0
- package/dist/src/private-to-private/transport.d.ts.map +1 -1
- package/dist/src/private-to-private/transport.js +13 -11
- package/dist/src/private-to-private/transport.js.map +1 -1
- package/dist/src/private-to-private/util.d.ts +2 -1
- package/dist/src/private-to-private/util.d.ts.map +1 -1
- package/dist/src/private-to-private/util.js +25 -40
- package/dist/src/private-to-private/util.js.map +1 -1
- package/dist/src/private-to-public/sdp.d.ts +2 -1
- package/dist/src/private-to-public/sdp.d.ts.map +1 -1
- package/dist/src/private-to-public/sdp.js +3 -6
- package/dist/src/private-to-public/sdp.js.map +1 -1
- package/dist/src/private-to-public/transport.d.ts +3 -0
- package/dist/src/private-to-public/transport.d.ts.map +1 -1
- package/dist/src/private-to-public/transport.js +21 -10
- package/dist/src/private-to-public/transport.js.map +1 -1
- package/dist/src/stream.d.ts +10 -3
- package/dist/src/stream.d.ts.map +1 -1
- package/dist/src/stream.js +18 -32
- package/dist/src/stream.js.map +1 -1
- package/dist/src/util.d.ts +2 -1
- package/dist/src/util.d.ts.map +1 -1
- package/dist/src/util.js +4 -6
- package/dist/src/util.js.map +1 -1
- package/package.json +21 -20
- package/src/index.ts +6 -0
- package/src/maconn.ts +12 -7
- package/src/muxer.ts +57 -8
- package/src/private-to-private/initiate-connection.ts +5 -6
- package/src/private-to-private/signaling-stream-handler.ts +5 -5
- package/src/private-to-private/transport.ts +15 -12
- package/src/private-to-private/util.ts +29 -44
- package/src/private-to-public/sdp.ts +4 -8
- package/src/private-to-public/transport.ts +23 -11
- package/src/stream.ts +26 -37
- package/src/util.ts +5 -7
- package/dist/typedoc-urls.json +0 -8
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import { CodeError } from '@libp2p/interface/errors'
|
|
2
|
-
import {
|
|
3
|
-
import { abortableSource } from 'abortable-iterator'
|
|
2
|
+
import { closeSource } from '@libp2p/utils/close-source'
|
|
4
3
|
import { anySignal } from 'any-signal'
|
|
5
|
-
import * as lp from 'it-length-prefixed'
|
|
6
|
-
import { AbortError, raceSignal } from 'race-signal'
|
|
7
4
|
import { isFirefox } from '../util.js'
|
|
8
5
|
import { RTCIceCandidate } from '../webrtc/index.js'
|
|
9
6
|
import { Message } from './pb/message.js'
|
|
7
|
+
import type { LoggerOptions } from '@libp2p/interface'
|
|
10
8
|
import type { Stream } from '@libp2p/interface/connection'
|
|
11
9
|
import type { AbortOptions, MessageStream } from 'it-protobuf-stream'
|
|
12
10
|
import type { DeferredPromise } from 'p-defer'
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export interface ReadCandidatesOptions extends AbortOptions {
|
|
12
|
+
export interface ReadCandidatesOptions extends AbortOptions, LoggerOptions {
|
|
17
13
|
direction: string
|
|
18
14
|
}
|
|
19
15
|
|
|
@@ -31,71 +27,60 @@ export const readCandidatesUntilConnected = async (connectedPromise: DeferredPro
|
|
|
31
27
|
options.signal
|
|
32
28
|
])
|
|
33
29
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
}
|
|
30
|
+
const abortListener = (): void => {
|
|
31
|
+
closeSource(stream.unwrap().unwrap().source, options.log)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
signal.addEventListener('abort', abortListener)
|
|
37
35
|
|
|
38
36
|
try {
|
|
39
37
|
// read candidates until we are connected or we reach the end of the stream
|
|
40
|
-
|
|
41
|
-
const message =
|
|
38
|
+
while (true) {
|
|
39
|
+
const message = await Promise.race([
|
|
40
|
+
connectedPromise.promise,
|
|
41
|
+
stream.read()
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
// stream ended or we became connected
|
|
45
|
+
if (message == null) {
|
|
46
|
+
break
|
|
47
|
+
}
|
|
42
48
|
|
|
43
49
|
if (message.type !== Message.Type.ICE_CANDIDATE) {
|
|
44
50
|
throw new CodeError('ICE candidate message expected', 'ERR_NOT_ICE_CANDIDATE')
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
const candidateInit = JSON.parse(message.data ?? 'null')
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
sdpMLineIndex: 0
|
|
55
|
-
}
|
|
56
|
-
}
|
|
55
|
+
// an empty string means this generation of candidates is complete, a null
|
|
56
|
+
// candidate means candidate gathering has finished
|
|
57
|
+
// see - https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent
|
|
58
|
+
if (candidateInit === '' || candidateInit === null) {
|
|
59
|
+
options.log.trace('end-of-candidates received')
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
log.trace('end-of-candidates received')
|
|
60
|
-
candidateInit = {
|
|
61
|
-
candidate: null,
|
|
62
|
-
sdpMid: '0',
|
|
63
|
-
sdpMLineIndex: 0
|
|
64
|
-
}
|
|
61
|
+
continue
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
// a null candidate means end-of-candidates
|
|
68
|
-
// see - https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent
|
|
69
64
|
const candidate = new RTCIceCandidate(candidateInit)
|
|
70
65
|
|
|
71
|
-
log.trace('%s received new ICE candidate', options.direction, candidate)
|
|
66
|
+
options.log.trace('%s received new ICE candidate', options.direction, candidate)
|
|
72
67
|
|
|
73
68
|
try {
|
|
74
69
|
await pc.addIceCandidate(candidate)
|
|
75
70
|
} catch (err) {
|
|
76
|
-
log.error('%s bad candidate received', options.direction, err)
|
|
71
|
+
options.log.error('%s bad candidate received', options.direction, candidateInit, err)
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
74
|
} catch (err) {
|
|
80
|
-
log.error('%s error parsing ICE candidate', options.direction, err)
|
|
75
|
+
options.log.error('%s error parsing ICE candidate', options.direction, err)
|
|
81
76
|
} finally {
|
|
77
|
+
signal.removeEventListener('abort', abortListener)
|
|
82
78
|
signal.clear()
|
|
83
79
|
}
|
|
84
|
-
|
|
85
|
-
if (options.signal?.aborted === true) {
|
|
86
|
-
throw new AbortError('Aborted while reading ICE candidates', 'ERR_ICE_CANDIDATES_READ_ABORTED')
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// read all available ICE candidates, wait for connection state change
|
|
90
|
-
await raceSignal(connectedPromise.promise, options.signal, {
|
|
91
|
-
errorMessage: 'Aborted before connected',
|
|
92
|
-
errorCode: 'ERR_ABORTED_BEFORE_CONNECTED'
|
|
93
|
-
})
|
|
94
80
|
}
|
|
95
81
|
|
|
96
82
|
export function resolveOnConnected (pc: RTCPeerConnection, promise: DeferredPromise<void>): void {
|
|
97
83
|
pc[isFirefox ? 'oniceconnectionstatechange' : 'onconnectionstatechange'] = (_) => {
|
|
98
|
-
log.trace('receiver peerConnectionState state change: %s', pc.connectionState)
|
|
99
84
|
switch (isFirefox ? pc.iceConnectionState : pc.connectionState) {
|
|
100
85
|
case 'connected':
|
|
101
86
|
promise.resolve()
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
import { logger } from '@libp2p/logger'
|
|
2
1
|
import { bases } from 'multiformats/basics'
|
|
3
2
|
import * as multihashes from 'multihashes'
|
|
4
3
|
import { inappropriateMultiaddr, invalidArgument, invalidFingerprint, unsupportedHashAlgorithm } from '../error.js'
|
|
5
4
|
import { CERTHASH_CODE } from './transport.js'
|
|
5
|
+
import type { LoggerOptions } from '@libp2p/interface'
|
|
6
6
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
7
7
|
import type { HashCode, HashName } from 'multihashes'
|
|
8
8
|
|
|
9
|
-
const log = logger('libp2p:webrtc:sdp')
|
|
10
|
-
|
|
11
9
|
/**
|
|
12
10
|
* Get base2 | identity decoders
|
|
13
11
|
*/
|
|
14
12
|
// @ts-expect-error - Not easy to combine these types.
|
|
15
13
|
export const mbdecoder: any = Object.values(bases).map(b => b.decoder).reduce((d, b) => d.or(b))
|
|
16
14
|
|
|
17
|
-
export function getLocalFingerprint (pc: RTCPeerConnection): string | undefined {
|
|
15
|
+
export function getLocalFingerprint (pc: RTCPeerConnection, options: LoggerOptions): string | undefined {
|
|
18
16
|
// try to fetch fingerprint from local certificate
|
|
19
17
|
const localCert = pc.getConfiguration().certificates?.at(0)
|
|
20
18
|
if (localCert == null || localCert.getFingerprints == null) {
|
|
21
|
-
log.trace('fetching fingerprint from local SDP')
|
|
19
|
+
options.log.trace('fetching fingerprint from local SDP')
|
|
22
20
|
const localDescription = pc.localDescription
|
|
23
21
|
if (localDescription == null) {
|
|
24
22
|
return undefined
|
|
@@ -26,7 +24,7 @@ export function getLocalFingerprint (pc: RTCPeerConnection): string | undefined
|
|
|
26
24
|
return getFingerprintFromSdp(localDescription.sdp)
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
log.trace('fetching fingerprint from local certificate')
|
|
27
|
+
options.log.trace('fetching fingerprint from local certificate')
|
|
30
28
|
|
|
31
29
|
if (localCert.getFingerprints().length === 0) {
|
|
32
30
|
return undefined
|
|
@@ -55,8 +53,6 @@ function ipv (ma: Multiaddr): string {
|
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
log('Warning: multiaddr does not appear to contain IP4 or IP6, defaulting to IP6', ma)
|
|
59
|
-
|
|
60
56
|
return 'IP6'
|
|
61
57
|
}
|
|
62
58
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { noise as Noise } from '@chainsafe/libp2p-noise'
|
|
2
2
|
import { type CreateListenerOptions, symbol, type Transport, type Listener } from '@libp2p/interface/transport'
|
|
3
|
-
import { logger } from '@libp2p/logger'
|
|
4
3
|
import * as p from '@libp2p/peer-id'
|
|
5
4
|
import { protocols } from '@multiformats/multiaddr'
|
|
6
5
|
import { WebRTCDirect } from '@multiformats/multiaddr-matcher'
|
|
@@ -17,13 +16,12 @@ import * as sdp from './sdp.js'
|
|
|
17
16
|
import { genUfrag } from './util.js'
|
|
18
17
|
import type { WebRTCDialOptions } from './options.js'
|
|
19
18
|
import type { DataChannelOptions } from '../index.js'
|
|
19
|
+
import type { ComponentLogger, Logger } from '@libp2p/interface'
|
|
20
20
|
import type { Connection } from '@libp2p/interface/connection'
|
|
21
21
|
import type { CounterGroup, Metrics } from '@libp2p/interface/metrics'
|
|
22
22
|
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
23
23
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
24
24
|
|
|
25
|
-
const log = logger('libp2p:webrtc:transport')
|
|
26
|
-
|
|
27
25
|
/**
|
|
28
26
|
* The time to wait, in milliseconds, for the data channel handshake to complete
|
|
29
27
|
*/
|
|
@@ -49,6 +47,7 @@ export const CERTHASH_CODE: number = protocols('certhash').code
|
|
|
49
47
|
export interface WebRTCDirectTransportComponents {
|
|
50
48
|
peerId: PeerId
|
|
51
49
|
metrics?: Metrics
|
|
50
|
+
logger: ComponentLogger
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
export interface WebRTCMetrics {
|
|
@@ -60,10 +59,12 @@ export interface WebRTCTransportDirectInit {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
export class WebRTCDirectTransport implements Transport {
|
|
62
|
+
private readonly log: Logger
|
|
63
63
|
private readonly metrics?: WebRTCMetrics
|
|
64
64
|
private readonly components: WebRTCDirectTransportComponents
|
|
65
65
|
private readonly init: WebRTCTransportDirectInit
|
|
66
66
|
constructor (components: WebRTCDirectTransportComponents, init: WebRTCTransportDirectInit = {}) {
|
|
67
|
+
this.log = components.logger.forComponent('libp2p:webrtc-direct')
|
|
67
68
|
this.components = components
|
|
68
69
|
this.init = init
|
|
69
70
|
if (components.metrics != null) {
|
|
@@ -81,7 +82,7 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
81
82
|
*/
|
|
82
83
|
async dial (ma: Multiaddr, options: WebRTCDialOptions): Promise<Connection> {
|
|
83
84
|
const rawConn = await this._connect(ma, options)
|
|
84
|
-
log('dialing address: %a', ma)
|
|
85
|
+
this.log('dialing address: %a', ma)
|
|
85
86
|
return rawConn
|
|
86
87
|
}
|
|
87
88
|
|
|
@@ -144,7 +145,7 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
144
145
|
const handshakeDataChannel = peerConnection.createDataChannel('', { negotiated: true, id: 0 })
|
|
145
146
|
const handshakeTimeout = setTimeout(() => {
|
|
146
147
|
const error = `Data channel was never opened: state: ${handshakeDataChannel.readyState}`
|
|
147
|
-
log.error(error)
|
|
148
|
+
this.log.error(error)
|
|
148
149
|
this.metrics?.dialerEvents.increment({ open_error: true })
|
|
149
150
|
reject(dataChannelError('data', error))
|
|
150
151
|
}, HANDSHAKE_TIMEOUT_MS)
|
|
@@ -159,7 +160,7 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
159
160
|
clearTimeout(handshakeTimeout)
|
|
160
161
|
const errorTarget = event.target?.toString() ?? 'not specified'
|
|
161
162
|
const error = `Error opening a data channel for handshaking: ${errorTarget}`
|
|
162
|
-
log.error(error)
|
|
163
|
+
this.log.error(error)
|
|
163
164
|
// NOTE: We use unknown error here but this could potentially be considered a reset by some standards.
|
|
164
165
|
this.metrics?.dialerEvents.increment({ unknown_error: true })
|
|
165
166
|
reject(dataChannelError('data', error))
|
|
@@ -194,7 +195,12 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
194
195
|
// we pass in undefined for these parameters.
|
|
195
196
|
const noise = Noise({ prologueBytes: fingerprintsPrologue })()
|
|
196
197
|
|
|
197
|
-
const wrappedChannel = createStream({
|
|
198
|
+
const wrappedChannel = createStream({
|
|
199
|
+
channel: handshakeDataChannel,
|
|
200
|
+
direction: 'inbound',
|
|
201
|
+
logger: this.components.logger,
|
|
202
|
+
...(this.init.dataChannel ?? {})
|
|
203
|
+
})
|
|
198
204
|
const wrappedDuplex = {
|
|
199
205
|
...wrappedChannel,
|
|
200
206
|
sink: wrappedChannel.sink.bind(wrappedChannel),
|
|
@@ -209,7 +215,7 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
209
215
|
|
|
210
216
|
// Creating the connection before completion of the noise
|
|
211
217
|
// handshake ensures that the stream opening callback is set up
|
|
212
|
-
const maConn = new WebRTCMultiaddrConnection({
|
|
218
|
+
const maConn = new WebRTCMultiaddrConnection(this.components, {
|
|
213
219
|
peerConnection,
|
|
214
220
|
remoteAddr: ma,
|
|
215
221
|
timeline: {
|
|
@@ -226,7 +232,7 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
226
232
|
case 'disconnected':
|
|
227
233
|
case 'closed':
|
|
228
234
|
maConn.close().catch((err) => {
|
|
229
|
-
log.error('error closing connection', err)
|
|
235
|
+
this.log.error('error closing connection', err)
|
|
230
236
|
}).finally(() => {
|
|
231
237
|
// Remove the event listener once the connection is closed
|
|
232
238
|
controller.abort()
|
|
@@ -240,7 +246,11 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
240
246
|
// Track opened peer connection
|
|
241
247
|
this.metrics?.dialerEvents.increment({ peer_connection: true })
|
|
242
248
|
|
|
243
|
-
const muxerFactory = new DataChannelMuxerFactory(
|
|
249
|
+
const muxerFactory = new DataChannelMuxerFactory(this.components, {
|
|
250
|
+
peerConnection,
|
|
251
|
+
metrics: this.metrics?.dialerEvents,
|
|
252
|
+
dataChannelOptions: this.init.dataChannel
|
|
253
|
+
})
|
|
244
254
|
|
|
245
255
|
// For outbound connections, the remote is expected to start the noise handshake.
|
|
246
256
|
// Therefore, we need to secure an inbound noise connection from the remote.
|
|
@@ -262,7 +272,9 @@ export class WebRTCDirectTransport implements Transport {
|
|
|
262
272
|
throw invalidArgument('no local certificate')
|
|
263
273
|
}
|
|
264
274
|
|
|
265
|
-
const localFingerprint = sdp.getLocalFingerprint(pc
|
|
275
|
+
const localFingerprint = sdp.getLocalFingerprint(pc, {
|
|
276
|
+
log: this.log
|
|
277
|
+
})
|
|
266
278
|
if (localFingerprint == null) {
|
|
267
279
|
throw invalidArgument('no local fingerprint found')
|
|
268
280
|
}
|
package/src/stream.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CodeError } from '@libp2p/interface/errors'
|
|
2
|
-
import { AbstractStream, type AbstractStreamInit } from '@libp2p/
|
|
3
|
-
import { logger } from '@libp2p/logger'
|
|
2
|
+
import { AbstractStream, type AbstractStreamInit } from '@libp2p/utils/abstract-stream'
|
|
4
3
|
import * as lengthPrefixed from 'it-length-prefixed'
|
|
5
4
|
import { type Pushable, pushable } from 'it-pushable'
|
|
6
5
|
import pDefer from 'p-defer'
|
|
@@ -10,7 +9,7 @@ import { raceSignal } from 'race-signal'
|
|
|
10
9
|
import { Uint8ArrayList } from 'uint8arraylist'
|
|
11
10
|
import { Message } from './pb/message.js'
|
|
12
11
|
import type { DataChannelOptions } from './index.js'
|
|
13
|
-
import type { AbortOptions } from '@libp2p/interface'
|
|
12
|
+
import type { AbortOptions, ComponentLogger } from '@libp2p/interface'
|
|
14
13
|
import type { Direction } from '@libp2p/interface/connection'
|
|
15
14
|
import type { DeferredPromise } from 'p-defer'
|
|
16
15
|
|
|
@@ -22,6 +21,8 @@ export interface WebRTCStreamInit extends AbstractStreamInit, DataChannelOptions
|
|
|
22
21
|
* {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel}
|
|
23
22
|
*/
|
|
24
23
|
channel: RTCDataChannel
|
|
24
|
+
|
|
25
|
+
logger: ComponentLogger
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -56,6 +57,12 @@ export const MAX_MESSAGE_SIZE = 16 * 1024
|
|
|
56
57
|
*/
|
|
57
58
|
export const FIN_ACK_TIMEOUT = 5000
|
|
58
59
|
|
|
60
|
+
/**
|
|
61
|
+
* When sending data messages, if the channel is not in the "open" state, wait
|
|
62
|
+
* this long for the "open" event to fire.
|
|
63
|
+
*/
|
|
64
|
+
export const OPEN_TIMEOUT = 5000
|
|
65
|
+
|
|
59
66
|
export class WebRTCStream extends AbstractStream {
|
|
60
67
|
/**
|
|
61
68
|
* The data channel used to send and receive data
|
|
@@ -68,8 +75,6 @@ export class WebRTCStream extends AbstractStream {
|
|
|
68
75
|
*/
|
|
69
76
|
private readonly incomingData: Pushable<Uint8Array>
|
|
70
77
|
|
|
71
|
-
private messageQueue?: Uint8ArrayList
|
|
72
|
-
|
|
73
78
|
private readonly maxBufferedAmount: number
|
|
74
79
|
|
|
75
80
|
private readonly bufferedAmountLowEventTimeout: number
|
|
@@ -84,6 +89,7 @@ export class WebRTCStream extends AbstractStream {
|
|
|
84
89
|
*/
|
|
85
90
|
private readonly receiveFinAck: DeferredPromise<void>
|
|
86
91
|
private readonly finAckTimeout: number
|
|
92
|
+
private readonly openTimeout: number
|
|
87
93
|
|
|
88
94
|
constructor (init: WebRTCStreamInit) {
|
|
89
95
|
// override onEnd to send/receive FIN_ACK before closing the stream
|
|
@@ -121,17 +127,18 @@ export class WebRTCStream extends AbstractStream {
|
|
|
121
127
|
|
|
122
128
|
this.channel = init.channel
|
|
123
129
|
this.channel.binaryType = 'arraybuffer'
|
|
124
|
-
this.incomingData = pushable()
|
|
125
|
-
this.messageQueue = new Uint8ArrayList()
|
|
130
|
+
this.incomingData = pushable<Uint8Array>()
|
|
126
131
|
this.bufferedAmountLowEventTimeout = init.bufferedAmountLowEventTimeout ?? BUFFERED_AMOUNT_LOW_TIMEOUT
|
|
127
132
|
this.maxBufferedAmount = init.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT
|
|
128
133
|
this.maxMessageSize = (init.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD - VARINT_LENGTH
|
|
129
134
|
this.receiveFinAck = pDefer()
|
|
130
135
|
this.finAckTimeout = init.closeTimeout ?? FIN_ACK_TIMEOUT
|
|
136
|
+
this.openTimeout = init.openTimeout ?? OPEN_TIMEOUT
|
|
131
137
|
|
|
132
138
|
// set up initial state
|
|
133
139
|
switch (this.channel.readyState) {
|
|
134
140
|
case 'open':
|
|
141
|
+
this.timeline.open = new Date().getTime()
|
|
135
142
|
break
|
|
136
143
|
|
|
137
144
|
case 'closed':
|
|
@@ -152,19 +159,6 @@ export class WebRTCStream extends AbstractStream {
|
|
|
152
159
|
// handle RTCDataChannel events
|
|
153
160
|
this.channel.onopen = (_evt) => {
|
|
154
161
|
this.timeline.open = new Date().getTime()
|
|
155
|
-
|
|
156
|
-
if (this.messageQueue != null && this.messageQueue.byteLength > 0) {
|
|
157
|
-
this.log.trace('dataChannel opened, sending queued messages', this.messageQueue.byteLength, this.channel.readyState)
|
|
158
|
-
|
|
159
|
-
// send any queued messages
|
|
160
|
-
this._sendMessage(this.messageQueue)
|
|
161
|
-
.catch(err => {
|
|
162
|
-
this.log.error('error sending queued messages', err)
|
|
163
|
-
this.abort(err)
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
this.messageQueue = undefined
|
|
168
162
|
}
|
|
169
163
|
|
|
170
164
|
this.channel.onclose = (_evt) => {
|
|
@@ -217,6 +211,7 @@ export class WebRTCStream extends AbstractStream {
|
|
|
217
211
|
async _sendMessage (data: Uint8ArrayList, checkBuffer: boolean = true): Promise<void> {
|
|
218
212
|
if (checkBuffer && this.channel.bufferedAmount > this.maxBufferedAmount) {
|
|
219
213
|
try {
|
|
214
|
+
this.log('channel buffer is %d, wait for "bufferedamountlow" event', this.channel.bufferedAmount)
|
|
220
215
|
await pEvent(this.channel, 'bufferedamountlow', { timeout: this.bufferedAmountLowEventTimeout })
|
|
221
216
|
} catch (err: any) {
|
|
222
217
|
if (err instanceof TimeoutError) {
|
|
@@ -231,22 +226,14 @@ export class WebRTCStream extends AbstractStream {
|
|
|
231
226
|
throw new CodeError(`Invalid datachannel state - ${this.channel.readyState}`, 'ERR_INVALID_STATE')
|
|
232
227
|
}
|
|
233
228
|
|
|
234
|
-
if (this.channel.readyState
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
} else if (this.channel.readyState === 'connecting') {
|
|
240
|
-
// queue message for when we are open
|
|
241
|
-
if (this.messageQueue == null) {
|
|
242
|
-
this.messageQueue = new Uint8ArrayList()
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
this.messageQueue.append(data)
|
|
246
|
-
} else {
|
|
247
|
-
this.log.error('unknown datachannel state %s', this.channel.readyState)
|
|
248
|
-
throw new CodeError('Unknown datachannel state', 'ERR_INVALID_STATE')
|
|
229
|
+
if (this.channel.readyState !== 'open') {
|
|
230
|
+
this.log('channel state is "%s" and not "open", waiting for "open" event before sending data', this.channel.readyState)
|
|
231
|
+
await pEvent(this.channel, 'open', { timeout: this.openTimeout })
|
|
232
|
+
this.log('channel state is now "%s", sending data', this.channel.readyState)
|
|
249
233
|
}
|
|
234
|
+
|
|
235
|
+
// send message without copying data
|
|
236
|
+
this.channel.send(data.subarray())
|
|
250
237
|
}
|
|
251
238
|
|
|
252
239
|
async sendData (data: Uint8ArrayList): Promise<void> {
|
|
@@ -341,7 +328,7 @@ export class WebRTCStream extends AbstractStream {
|
|
|
341
328
|
// flags can be sent while we or the remote are closing the datachannel so
|
|
342
329
|
// if the channel isn't open, don't try to send it but return false to let
|
|
343
330
|
// the caller know and act if they need to
|
|
344
|
-
this.log.trace('not sending flag %s because channel is not open', flag.toString())
|
|
331
|
+
this.log.trace('not sending flag %s because channel is "%s" and not "open"', this.channel.readyState, flag.toString())
|
|
345
332
|
return false
|
|
346
333
|
}
|
|
347
334
|
|
|
@@ -379,6 +366,8 @@ export interface WebRTCStreamOptions extends DataChannelOptions {
|
|
|
379
366
|
* A callback invoked when the channel ends
|
|
380
367
|
*/
|
|
381
368
|
onEnd?(err?: Error | undefined): void
|
|
369
|
+
|
|
370
|
+
logger: ComponentLogger
|
|
382
371
|
}
|
|
383
372
|
|
|
384
373
|
export function createStream (options: WebRTCStreamOptions): WebRTCStream {
|
|
@@ -386,7 +375,7 @@ export function createStream (options: WebRTCStreamOptions): WebRTCStream {
|
|
|
386
375
|
|
|
387
376
|
return new WebRTCStream({
|
|
388
377
|
id: direction === 'inbound' ? (`i${channel.id}`) : `r${channel.id}`,
|
|
389
|
-
log: logger(`libp2p:webrtc:stream:${direction}:${channel.id}`),
|
|
378
|
+
log: options.logger.forComponent(`libp2p:webrtc:stream:${direction}:${channel.id}`),
|
|
390
379
|
...options
|
|
391
380
|
})
|
|
392
381
|
}
|
package/src/util.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { logger } from '@libp2p/logger'
|
|
2
1
|
import { detect } from 'detect-browser'
|
|
3
2
|
import pDefer from 'p-defer'
|
|
4
3
|
import pTimeout from 'p-timeout'
|
|
5
|
-
|
|
6
|
-
const log = logger('libp2p:webrtc:utils')
|
|
4
|
+
import type { LoggerOptions } from '@libp2p/interface'
|
|
7
5
|
|
|
8
6
|
const browser = detect()
|
|
9
7
|
export const isFirefox = ((browser != null) && browser.name === 'firefox')
|
|
@@ -14,7 +12,7 @@ export const nopSink = async (_: any): Promise<void> => {}
|
|
|
14
12
|
|
|
15
13
|
export const DATA_CHANNEL_DRAIN_TIMEOUT = 30 * 1000
|
|
16
14
|
|
|
17
|
-
export function drainAndClose (channel: RTCDataChannel, direction: string, drainTimeout: number = DATA_CHANNEL_DRAIN_TIMEOUT): void {
|
|
15
|
+
export function drainAndClose (channel: RTCDataChannel, direction: string, drainTimeout: number = DATA_CHANNEL_DRAIN_TIMEOUT, options: LoggerOptions): void {
|
|
18
16
|
if (channel.readyState !== 'open') {
|
|
19
17
|
return
|
|
20
18
|
}
|
|
@@ -23,7 +21,7 @@ export function drainAndClose (channel: RTCDataChannel, direction: string, drain
|
|
|
23
21
|
.then(async () => {
|
|
24
22
|
// wait for bufferedAmount to become zero
|
|
25
23
|
if (channel.bufferedAmount > 0) {
|
|
26
|
-
log('%s drain channel with %d buffered bytes', direction, channel.bufferedAmount)
|
|
24
|
+
options.log('%s drain channel with %d buffered bytes', direction, channel.bufferedAmount)
|
|
27
25
|
const deferred = pDefer()
|
|
28
26
|
let drained = false
|
|
29
27
|
|
|
@@ -31,7 +29,7 @@ export function drainAndClose (channel: RTCDataChannel, direction: string, drain
|
|
|
31
29
|
|
|
32
30
|
const closeListener = (): void => {
|
|
33
31
|
if (!drained) {
|
|
34
|
-
log('%s drain channel closed before drain', direction)
|
|
32
|
+
options.log('%s drain channel closed before drain', direction)
|
|
35
33
|
deferred.resolve()
|
|
36
34
|
}
|
|
37
35
|
}
|
|
@@ -58,7 +56,7 @@ export function drainAndClose (channel: RTCDataChannel, direction: string, drain
|
|
|
58
56
|
}
|
|
59
57
|
})
|
|
60
58
|
.catch(err => {
|
|
61
|
-
log.error('error closing outbound stream', err)
|
|
59
|
+
options.log.error('error closing outbound stream', err)
|
|
62
60
|
})
|
|
63
61
|
}
|
|
64
62
|
|
package/dist/typedoc-urls.json
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"DataChannelOptions": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.DataChannelOptions.html",
|
|
3
|
-
".:DataChannelOptions": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.DataChannelOptions.html",
|
|
4
|
-
"webRTC": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTC.html",
|
|
5
|
-
".:webRTC": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTC.html",
|
|
6
|
-
"webRTCDirect": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTCDirect.html",
|
|
7
|
-
".:webRTCDirect": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTCDirect.html"
|
|
8
|
-
}
|