@libp2p/webrtc 3.2.1 → 3.2.2-77e3cbc3

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 (67) hide show
  1. package/dist/index.min.js +13 -13
  2. package/dist/src/index.d.ts +29 -0
  3. package/dist/src/index.d.ts.map +1 -1
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/maconn.d.ts.map +1 -1
  6. package/dist/src/maconn.js +5 -2
  7. package/dist/src/maconn.js.map +1 -1
  8. package/dist/src/muxer.d.ts +10 -13
  9. package/dist/src/muxer.d.ts.map +1 -1
  10. package/dist/src/muxer.js +44 -29
  11. package/dist/src/muxer.js.map +1 -1
  12. package/dist/src/pb/message.d.ts +2 -1
  13. package/dist/src/pb/message.d.ts.map +1 -1
  14. package/dist/src/pb/message.js +2 -0
  15. package/dist/src/pb/message.js.map +1 -1
  16. package/dist/src/private-to-private/initiate-connection.d.ts +25 -0
  17. package/dist/src/private-to-private/initiate-connection.d.ts.map +1 -0
  18. package/dist/src/private-to-private/initiate-connection.js +145 -0
  19. package/dist/src/private-to-private/initiate-connection.js.map +1 -0
  20. package/dist/src/private-to-private/listener.d.ts +6 -2
  21. package/dist/src/private-to-private/listener.d.ts.map +1 -1
  22. package/dist/src/private-to-private/listener.js +6 -3
  23. package/dist/src/private-to-private/listener.js.map +1 -1
  24. package/dist/src/private-to-private/signaling-stream-handler.d.ts +10 -0
  25. package/dist/src/private-to-private/signaling-stream-handler.d.ts.map +1 -0
  26. package/dist/src/private-to-private/signaling-stream-handler.js +97 -0
  27. package/dist/src/private-to-private/signaling-stream-handler.js.map +1 -0
  28. package/dist/src/private-to-private/transport.d.ts +12 -2
  29. package/dist/src/private-to-private/transport.d.ts.map +1 -1
  30. package/dist/src/private-to-private/transport.js +67 -56
  31. package/dist/src/private-to-private/transport.js.map +1 -1
  32. package/dist/src/private-to-private/util.d.ts +6 -5
  33. package/dist/src/private-to-private/util.d.ts.map +1 -1
  34. package/dist/src/private-to-private/util.js +72 -21
  35. package/dist/src/private-to-private/util.js.map +1 -1
  36. package/dist/src/private-to-public/transport.d.ts +2 -2
  37. package/dist/src/private-to-public/transport.d.ts.map +1 -1
  38. package/dist/src/private-to-public/transport.js +2 -2
  39. package/dist/src/private-to-public/transport.js.map +1 -1
  40. package/dist/src/stream.d.ts +39 -19
  41. package/dist/src/stream.d.ts.map +1 -1
  42. package/dist/src/stream.js +135 -39
  43. package/dist/src/stream.js.map +1 -1
  44. package/dist/src/util.d.ts +6 -0
  45. package/dist/src/util.d.ts.map +1 -1
  46. package/dist/src/util.js +46 -0
  47. package/dist/src/util.js.map +1 -1
  48. package/package.json +17 -11
  49. package/src/index.ts +34 -0
  50. package/src/maconn.ts +7 -2
  51. package/src/muxer.ts +58 -44
  52. package/src/pb/message.proto +6 -1
  53. package/src/pb/message.ts +4 -2
  54. package/src/private-to-private/initiate-connection.ts +191 -0
  55. package/src/private-to-private/listener.ts +12 -4
  56. package/src/private-to-private/signaling-stream-handler.ts +129 -0
  57. package/src/private-to-private/transport.ts +87 -59
  58. package/src/private-to-private/util.ts +89 -24
  59. package/src/private-to-public/transport.ts +4 -4
  60. package/src/stream.ts +163 -61
  61. package/src/util.ts +60 -0
  62. package/dist/src/private-to-private/handler.d.ts +0 -26
  63. package/dist/src/private-to-private/handler.d.ts.map +0 -1
  64. package/dist/src/private-to-private/handler.js +0 -137
  65. package/dist/src/private-to-private/handler.js.map +0 -1
  66. package/dist/typedoc-urls.json +0 -6
  67. package/src/private-to-private/handler.ts +0 -177
package/src/stream.ts CHANGED
@@ -3,18 +3,18 @@ import { AbstractStream, type AbstractStreamInit } from '@libp2p/interface/strea
3
3
  import { logger } from '@libp2p/logger'
4
4
  import * as lengthPrefixed from 'it-length-prefixed'
5
5
  import { type Pushable, pushable } from 'it-pushable'
6
+ import pDefer from 'p-defer'
6
7
  import { pEvent, TimeoutError } from 'p-event'
8
+ import pTimeout from 'p-timeout'
9
+ import { raceSignal } from 'race-signal'
7
10
  import { Uint8ArrayList } from 'uint8arraylist'
8
11
  import { Message } from './pb/message.js'
12
+ import type { DataChannelOptions } from './index.js'
13
+ import type { AbortOptions } from '@libp2p/interface'
9
14
  import type { Direction } from '@libp2p/interface/connection'
15
+ import type { DeferredPromise } from 'p-defer'
10
16
 
11
- export interface DataChannelOpts {
12
- maxMessageSize: number
13
- maxBufferedAmount: number
14
- bufferedAmountLowEventTimeout: number
15
- }
16
-
17
- export interface WebRTCStreamInit extends AbstractStreamInit {
17
+ export interface WebRTCStreamInit extends AbstractStreamInit, DataChannelOptions {
18
18
  /**
19
19
  * The network channel used for bidirectional peer-to-peer transfers of
20
20
  * arbitrary data
@@ -22,38 +22,46 @@ export interface WebRTCStreamInit extends AbstractStreamInit {
22
22
  * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel}
23
23
  */
24
24
  channel: RTCDataChannel
25
-
26
- dataChannelOptions?: Partial<DataChannelOpts>
27
-
28
- maxDataSize: number
29
25
  }
30
26
 
31
- // Max message size that can be sent to the DataChannel
32
- export const MAX_MESSAGE_SIZE = 16 * 1024
33
-
34
- // How much can be buffered to the DataChannel at once
27
+ /**
28
+ * How much can be buffered to the DataChannel at once
29
+ */
35
30
  export const MAX_BUFFERED_AMOUNT = 16 * 1024 * 1024
36
31
 
37
- // How long time we wait for the 'bufferedamountlow' event to be emitted
32
+ /**
33
+ * How long time we wait for the 'bufferedamountlow' event to be emitted
34
+ */
38
35
  export const BUFFERED_AMOUNT_LOW_TIMEOUT = 30 * 1000
39
36
 
40
- // protobuf field definition overhead
37
+ /**
38
+ * protobuf field definition overhead
39
+ */
41
40
  export const PROTOBUF_OVERHEAD = 5
42
41
 
43
- // Length of varint, in bytes.
42
+ /**
43
+ * Length of varint, in bytes
44
+ */
44
45
  export const VARINT_LENGTH = 2
45
46
 
47
+ /**
48
+ * Max message size that can be sent to the DataChannel
49
+ */
50
+ export const MAX_MESSAGE_SIZE = 16 * 1024
51
+
52
+ /**
53
+ * When closing streams we send a FIN then wait for the remote to
54
+ * reply with a FIN_ACK. If that does not happen within this timeout
55
+ * we close the stream anyway.
56
+ */
57
+ export const FIN_ACK_TIMEOUT = 5000
58
+
46
59
  export class WebRTCStream extends AbstractStream {
47
60
  /**
48
61
  * The data channel used to send and receive data
49
62
  */
50
63
  private readonly channel: RTCDataChannel
51
64
 
52
- /**
53
- * Data channel options
54
- */
55
- private readonly dataChannelOptions: DataChannelOpts
56
-
57
65
  /**
58
66
  * push data from the underlying datachannel to the length prefix decoder
59
67
  * and then the protobuf decoder.
@@ -62,24 +70,65 @@ export class WebRTCStream extends AbstractStream {
62
70
 
63
71
  private messageQueue?: Uint8ArrayList
64
72
 
73
+ private readonly maxBufferedAmount: number
74
+
75
+ private readonly bufferedAmountLowEventTimeout: number
76
+
65
77
  /**
66
78
  * The maximum size of a message in bytes
67
79
  */
68
- private readonly maxDataSize: number
80
+ private readonly maxMessageSize: number
81
+
82
+ /**
83
+ * When this promise is resolved, the remote has sent us a FIN flag
84
+ */
85
+ private readonly receiveFinAck: DeferredPromise<void>
86
+ private readonly finAckTimeout: number
87
+ // private sentFinAck: boolean
69
88
 
70
89
  constructor (init: WebRTCStreamInit) {
90
+ // override onEnd to send/receive FIN_ACK before closing the stream
91
+ const originalOnEnd = init.onEnd
92
+ init.onEnd = (err?: Error): void => {
93
+ this.log.trace('readable and writeable ends closed', this.status)
94
+
95
+ void Promise.resolve(async () => {
96
+ if (this.timeline.abort != null || this.timeline.reset !== null) {
97
+ return
98
+ }
99
+
100
+ // wait for FIN_ACK if we haven't received it already
101
+ try {
102
+ await pTimeout(this.receiveFinAck.promise, {
103
+ milliseconds: this.finAckTimeout
104
+ })
105
+ } catch (err) {
106
+ this.log.error('error receiving FIN_ACK', err)
107
+ }
108
+ })
109
+ .then(() => {
110
+ // stop processing incoming messages
111
+ this.incomingData.end()
112
+
113
+ // final cleanup
114
+ originalOnEnd?.(err)
115
+ })
116
+ .catch(err => {
117
+ this.log.error('error ending stream', err)
118
+ })
119
+ }
120
+
71
121
  super(init)
72
122
 
73
123
  this.channel = init.channel
74
124
  this.channel.binaryType = 'arraybuffer'
75
125
  this.incomingData = pushable()
76
126
  this.messageQueue = new Uint8ArrayList()
77
- this.dataChannelOptions = {
78
- bufferedAmountLowEventTimeout: init.dataChannelOptions?.bufferedAmountLowEventTimeout ?? BUFFERED_AMOUNT_LOW_TIMEOUT,
79
- maxBufferedAmount: init.dataChannelOptions?.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT,
80
- maxMessageSize: init.dataChannelOptions?.maxMessageSize ?? init.maxDataSize
81
- }
82
- this.maxDataSize = init.maxDataSize
127
+ this.bufferedAmountLowEventTimeout = init.bufferedAmountLowEventTimeout ?? BUFFERED_AMOUNT_LOW_TIMEOUT
128
+ this.maxBufferedAmount = init.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT
129
+ this.maxMessageSize = (init.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD - VARINT_LENGTH
130
+ this.receiveFinAck = pDefer()
131
+ this.finAckTimeout = init.closeTimeout ?? FIN_ACK_TIMEOUT
83
132
 
84
133
  // set up initial state
85
134
  switch (this.channel.readyState) {
@@ -105,17 +154,25 @@ export class WebRTCStream extends AbstractStream {
105
154
  this.channel.onopen = (_evt) => {
106
155
  this.timeline.open = new Date().getTime()
107
156
 
108
- if (this.messageQueue != null) {
157
+ if (this.messageQueue != null && this.messageQueue.byteLength > 0) {
158
+ this.log.trace('dataChannel opened, sending queued messages', this.messageQueue.byteLength, this.channel.readyState)
159
+
109
160
  // send any queued messages
110
161
  this._sendMessage(this.messageQueue)
111
162
  .catch(err => {
163
+ this.log.error('error sending queued messages', err)
112
164
  this.abort(err)
113
165
  })
114
- this.messageQueue = undefined
115
166
  }
167
+
168
+ this.messageQueue = undefined
116
169
  }
117
170
 
118
171
  this.channel.onclose = (_evt) => {
172
+ // if the channel has closed we'll never receive a FIN_ACK so resolve the
173
+ // promise so we don't try to wait later
174
+ this.receiveFinAck.resolve()
175
+
119
176
  void this.close().catch(err => {
120
177
  this.log.error('error closing stream after channel closed', err)
121
178
  })
@@ -126,8 +183,6 @@ export class WebRTCStream extends AbstractStream {
126
183
  this.abort(err)
127
184
  }
128
185
 
129
- const self = this
130
-
131
186
  this.channel.onmessage = async (event: MessageEvent<ArrayBuffer>) => {
132
187
  const { data } = event
133
188
 
@@ -138,11 +193,13 @@ export class WebRTCStream extends AbstractStream {
138
193
  this.incomingData.push(new Uint8Array(data, 0, data.byteLength))
139
194
  }
140
195
 
196
+ const self = this
197
+
141
198
  // pipe framed protobuf messages through a length prefixed decoder, and
142
199
  // surface data from the `Message.message` field through a source.
143
200
  Promise.resolve().then(async () => {
144
201
  for await (const buf of lengthPrefixed.decode(this.incomingData)) {
145
- const message = self.processIncomingProtobuf(buf.subarray())
202
+ const message = self.processIncomingProtobuf(buf)
146
203
 
147
204
  if (message != null) {
148
205
  self.sourcePush(new Uint8ArrayList(message))
@@ -159,12 +216,12 @@ export class WebRTCStream extends AbstractStream {
159
216
  }
160
217
 
161
218
  async _sendMessage (data: Uint8ArrayList, checkBuffer: boolean = true): Promise<void> {
162
- if (checkBuffer && this.channel.bufferedAmount > this.dataChannelOptions.maxBufferedAmount) {
219
+ if (checkBuffer && this.channel.bufferedAmount > this.maxBufferedAmount) {
163
220
  try {
164
- await pEvent(this.channel, 'bufferedamountlow', { timeout: this.dataChannelOptions.bufferedAmountLowEventTimeout })
221
+ await pEvent(this.channel, 'bufferedamountlow', { timeout: this.bufferedAmountLowEventTimeout })
165
222
  } catch (err: any) {
166
223
  if (err instanceof TimeoutError) {
167
- throw new Error('Timed out waiting for DataChannel buffer to clear')
224
+ throw new CodeError(`Timed out waiting for DataChannel buffer to clear after ${this.bufferedAmountLowEventTimeout}ms`, 'ERR_BUFFER_CLEAR_TIMEOUT')
168
225
  }
169
226
 
170
227
  throw err
@@ -172,7 +229,7 @@ export class WebRTCStream extends AbstractStream {
172
229
  }
173
230
 
174
231
  if (this.channel.readyState === 'closed' || this.channel.readyState === 'closing') {
175
- throw new CodeError('Invalid datachannel state - closed or closing', 'ERR_INVALID_STATE')
232
+ throw new CodeError(`Invalid datachannel state - ${this.channel.readyState}`, 'ERR_INVALID_STATE')
176
233
  }
177
234
 
178
235
  if (this.channel.readyState === 'open') {
@@ -194,10 +251,12 @@ export class WebRTCStream extends AbstractStream {
194
251
  }
195
252
 
196
253
  async sendData (data: Uint8ArrayList): Promise<void> {
254
+ // sending messages is an async operation so use a copy of the list as it
255
+ // may be changed beneath us
197
256
  data = data.sublist()
198
257
 
199
258
  while (data.byteLength > 0) {
200
- const toSend = Math.min(data.byteLength, this.maxDataSize)
259
+ const toSend = Math.min(data.byteLength, this.maxMessageSize)
201
260
  const buf = data.subarray(0, toSend)
202
261
  const msgbuf = Message.encode({ message: buf })
203
262
  const sendbuf = lengthPrefixed.encode.single(msgbuf)
@@ -211,8 +270,25 @@ export class WebRTCStream extends AbstractStream {
211
270
  await this._sendFlag(Message.Flag.RESET)
212
271
  }
213
272
 
214
- async sendCloseWrite (): Promise<void> {
215
- await this._sendFlag(Message.Flag.FIN)
273
+ async sendCloseWrite (options: AbortOptions): Promise<void> {
274
+ const sent = await this._sendFlag(Message.Flag.FIN)
275
+
276
+ if (sent) {
277
+ this.log.trace('awaiting FIN_ACK')
278
+ try {
279
+ await raceSignal(this.receiveFinAck.promise, options?.signal, {
280
+ errorMessage: 'sending close-write was aborted before FIN_ACK was received',
281
+ errorCode: 'ERR_FIN_ACK_NOT_RECEIVED'
282
+ })
283
+ } catch (err) {
284
+ this.log.error('failed to await FIN_ACK', err)
285
+ }
286
+ } else {
287
+ this.log.trace('sending FIN failed, not awaiting FIN_ACK')
288
+ }
289
+
290
+ // if we've attempted to receive a FIN_ACK, do not try again
291
+ this.receiveFinAck.resolve()
216
292
  }
217
293
 
218
294
  async sendCloseRead (): Promise<void> {
@@ -222,14 +298,21 @@ export class WebRTCStream extends AbstractStream {
222
298
  /**
223
299
  * Handle incoming
224
300
  */
225
- private processIncomingProtobuf (buffer: Uint8Array): Uint8Array | undefined {
301
+ private processIncomingProtobuf (buffer: Uint8ArrayList): Uint8Array | undefined {
226
302
  const message = Message.decode(buffer)
227
303
 
228
304
  if (message.flag !== undefined) {
305
+ this.log.trace('incoming flag %s, write status "%s", read status "%s"', message.flag, this.writeStatus, this.readStatus)
306
+
229
307
  if (message.flag === Message.Flag.FIN) {
230
308
  // We should expect no more data from the remote, stop reading
231
- this.incomingData.end()
232
309
  this.remoteCloseWrite()
310
+
311
+ this.log.trace('sending FIN_ACK')
312
+ void this._sendFlag(Message.Flag.FIN_ACK)
313
+ .catch(err => {
314
+ this.log.error('error sending FIN_ACK immediately', err)
315
+ })
233
316
  }
234
317
 
235
318
  if (message.flag === Message.Flag.RESET) {
@@ -241,21 +324,45 @@ export class WebRTCStream extends AbstractStream {
241
324
  // The remote has stopped reading
242
325
  this.remoteCloseRead()
243
326
  }
327
+
328
+ if (message.flag === Message.Flag.FIN_ACK) {
329
+ this.log.trace('received FIN_ACK')
330
+ this.receiveFinAck.resolve()
331
+ }
244
332
  }
245
333
 
246
- return message.message
334
+ // ignore data messages if we've closed the readable end already
335
+ if (this.readStatus === 'ready') {
336
+ return message.message
337
+ }
247
338
  }
248
339
 
249
- private async _sendFlag (flag: Message.Flag): Promise<void> {
250
- this.log.trace('Sending flag: %s', flag.toString())
340
+ private async _sendFlag (flag: Message.Flag): Promise<boolean> {
341
+ if (this.channel.readyState !== 'open') {
342
+ // flags can be sent while we or the remote are closing the datachannel so
343
+ // if the channel isn't open, don't try to send it but return false to let
344
+ // the caller know and act if they need to
345
+ this.log.trace('not sending flag %s because channel is not open', flag.toString())
346
+ return false
347
+ }
348
+
349
+ this.log.trace('sending flag %s', flag.toString())
251
350
  const msgbuf = Message.encode({ flag })
252
351
  const prefixedBuf = lengthPrefixed.encode.single(msgbuf)
253
352
 
254
- await this._sendMessage(prefixedBuf, false)
353
+ try {
354
+ await this._sendMessage(prefixedBuf, false)
355
+
356
+ return true
357
+ } catch (err: any) {
358
+ this.log.error('could not send flag %s', flag.toString(), err)
359
+ }
360
+
361
+ return false
255
362
  }
256
363
  }
257
364
 
258
- export interface WebRTCStreamOptions {
365
+ export interface WebRTCStreamOptions extends DataChannelOptions {
259
366
  /**
260
367
  * The network channel used for bidirectional peer-to-peer transfers of
261
368
  * arbitrary data
@@ -269,23 +376,18 @@ export interface WebRTCStreamOptions {
269
376
  */
270
377
  direction: Direction
271
378
 
272
- dataChannelOptions?: Partial<DataChannelOpts>
273
-
274
- maxMsgSize?: number
275
-
276
- onEnd?: (err?: Error | undefined) => void
379
+ /**
380
+ * A callback invoked when the channel ends
381
+ */
382
+ onEnd?(err?: Error | undefined): void
277
383
  }
278
384
 
279
385
  export function createStream (options: WebRTCStreamOptions): WebRTCStream {
280
- const { channel, direction, onEnd, dataChannelOptions } = options
386
+ const { channel, direction } = options
281
387
 
282
388
  return new WebRTCStream({
283
389
  id: direction === 'inbound' ? (`i${channel.id}`) : `r${channel.id}`,
284
- direction,
285
- maxDataSize: (dataChannelOptions?.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD - VARINT_LENGTH,
286
- dataChannelOptions,
287
- onEnd,
288
- channel,
289
- log: logger(`libp2p:webrtc:stream:${direction}:${channel.id}`)
390
+ log: logger(`libp2p:webrtc:stream:${direction}:${channel.id}`),
391
+ ...options
290
392
  })
291
393
  }
package/src/util.ts CHANGED
@@ -1,4 +1,9 @@
1
+ import { logger } from '@libp2p/logger'
1
2
  import { detect } from 'detect-browser'
3
+ import pDefer from 'p-defer'
4
+ import pTimeout from 'p-timeout'
5
+
6
+ const log = logger('libp2p:webrtc:utils')
2
7
 
3
8
  const browser = detect()
4
9
  export const isFirefox = ((browser != null) && browser.name === 'firefox')
@@ -6,3 +11,58 @@ export const isFirefox = ((browser != null) && browser.name === 'firefox')
6
11
  export const nopSource = async function * nop (): AsyncGenerator<Uint8Array, any, unknown> {}
7
12
 
8
13
  export const nopSink = async (_: any): Promise<void> => {}
14
+
15
+ export const DATA_CHANNEL_DRAIN_TIMEOUT = 30 * 1000
16
+
17
+ export function drainAndClose (channel: RTCDataChannel, direction: string, drainTimeout: number = DATA_CHANNEL_DRAIN_TIMEOUT): void {
18
+ if (channel.readyState !== 'open') {
19
+ return
20
+ }
21
+
22
+ void Promise.resolve()
23
+ .then(async () => {
24
+ // wait for bufferedAmount to become zero
25
+ if (channel.bufferedAmount > 0) {
26
+ log('%s drain channel with %d buffered bytes', direction, channel.bufferedAmount)
27
+ const deferred = pDefer()
28
+ let drained = false
29
+
30
+ channel.bufferedAmountLowThreshold = 0
31
+
32
+ const closeListener = (): void => {
33
+ if (!drained) {
34
+ log('%s drain channel closed before drain', direction)
35
+ deferred.resolve()
36
+ }
37
+ }
38
+
39
+ channel.addEventListener('close', closeListener, {
40
+ once: true
41
+ })
42
+
43
+ channel.addEventListener('bufferedamountlow', () => {
44
+ drained = true
45
+ channel.removeEventListener('close', closeListener)
46
+ deferred.resolve()
47
+ })
48
+
49
+ await pTimeout(deferred.promise, {
50
+ milliseconds: drainTimeout
51
+ })
52
+ }
53
+ })
54
+ .then(async () => {
55
+ // only close if the channel is still open
56
+ if (channel.readyState === 'open') {
57
+ channel.close()
58
+ }
59
+ })
60
+ .catch(err => {
61
+ log.error('error closing outbound stream', err)
62
+ })
63
+ }
64
+
65
+ export interface AbortPromiseOptions {
66
+ signal?: AbortSignal
67
+ message?: string
68
+ }
@@ -1,26 +0,0 @@
1
- import { RTCPeerConnection } from '../webrtc/index.js';
2
- import type { DataChannelOpts } from '../stream.js';
3
- import type { Stream } from '@libp2p/interface/connection';
4
- import type { StreamMuxerFactory } from '@libp2p/interface/stream-muxer';
5
- import type { IncomingStreamData } from '@libp2p/interface-internal/registrar';
6
- export type IncomingStreamOpts = {
7
- rtcConfiguration?: RTCConfiguration;
8
- dataChannelOptions?: Partial<DataChannelOpts>;
9
- } & IncomingStreamData;
10
- export declare function handleIncomingStream({ rtcConfiguration, dataChannelOptions, stream: rawStream }: IncomingStreamOpts): Promise<{
11
- pc: RTCPeerConnection;
12
- muxerFactory: StreamMuxerFactory;
13
- remoteAddress: string;
14
- }>;
15
- export interface ConnectOptions {
16
- stream: Stream;
17
- signal: AbortSignal;
18
- rtcConfiguration?: RTCConfiguration;
19
- dataChannelOptions?: Partial<DataChannelOpts>;
20
- }
21
- export declare function initiateConnection({ rtcConfiguration, dataChannelOptions, signal, stream: rawStream }: ConnectOptions): Promise<{
22
- pc: RTCPeerConnection;
23
- muxerFactory: StreamMuxerFactory;
24
- remoteAddress: string;
25
- }>;
26
- //# sourceMappingURL=handler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/private-to-private/handler.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAyB,MAAM,oBAAoB,CAAA;AAG7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAA;AAM9E,MAAM,MAAM,kBAAkB,GAAG;IAAE,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;CAAE,GAAG,kBAAkB,CAAA;AAE5I,wBAAsB,oBAAoB,CAAE,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,kBAAkB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAyExN;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,WAAW,CAAA;IACnB,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;CAC9C;AAED,wBAAsB,kBAAkB,CAAE,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,kBAAkB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CA6D1N"}
@@ -1,137 +0,0 @@
1
- import { CodeError } from '@libp2p/interface/errors';
2
- import { logger } from '@libp2p/logger';
3
- import { abortableDuplex } from 'abortable-iterator';
4
- import { pbStream } from 'it-protobuf-stream';
5
- import pDefer, {} from 'p-defer';
6
- import { DataChannelMuxerFactory } from '../muxer.js';
7
- import { RTCPeerConnection, RTCSessionDescription } from '../webrtc/index.js';
8
- import { Message } from './pb/message.js';
9
- import { readCandidatesUntilConnected, resolveOnConnected } from './util.js';
10
- const DEFAULT_TIMEOUT = 30 * 1000;
11
- const log = logger('libp2p:webrtc:peer');
12
- export async function handleIncomingStream({ rtcConfiguration, dataChannelOptions, stream: rawStream }) {
13
- const signal = AbortSignal.timeout(DEFAULT_TIMEOUT);
14
- const stream = pbStream(abortableDuplex(rawStream, signal)).pb(Message);
15
- const pc = new RTCPeerConnection(rtcConfiguration);
16
- try {
17
- const muxerFactory = new DataChannelMuxerFactory({ peerConnection: pc, dataChannelOptions });
18
- const connectedPromise = pDefer();
19
- const answerSentPromise = pDefer();
20
- signal.onabort = () => {
21
- connectedPromise.reject(new CodeError('Timed out while trying to connect', 'ERR_TIMEOUT'));
22
- };
23
- // candidate callbacks
24
- pc.onicecandidate = ({ candidate }) => {
25
- answerSentPromise.promise.then(async () => {
26
- await stream.write({
27
- type: Message.Type.ICE_CANDIDATE,
28
- data: (candidate != null) ? JSON.stringify(candidate.toJSON()) : ''
29
- });
30
- }, (err) => {
31
- log.error('cannot set candidate since sending answer failed', err);
32
- connectedPromise.reject(err);
33
- });
34
- };
35
- resolveOnConnected(pc, connectedPromise);
36
- // read an SDP offer
37
- const pbOffer = await stream.read();
38
- if (pbOffer.type !== Message.Type.SDP_OFFER) {
39
- throw new Error(`expected message type SDP_OFFER, received: ${pbOffer.type ?? 'undefined'} `);
40
- }
41
- const offer = new RTCSessionDescription({
42
- type: 'offer',
43
- sdp: pbOffer.data
44
- });
45
- await pc.setRemoteDescription(offer).catch(err => {
46
- log.error('could not execute setRemoteDescription', err);
47
- throw new Error('Failed to set remoteDescription');
48
- });
49
- // create and write an SDP answer
50
- const answer = await pc.createAnswer().catch(err => {
51
- log.error('could not execute createAnswer', err);
52
- answerSentPromise.reject(err);
53
- throw new Error('Failed to create answer');
54
- });
55
- // write the answer to the remote
56
- await stream.write({ type: Message.Type.SDP_ANSWER, data: answer.sdp });
57
- await pc.setLocalDescription(answer).catch(err => {
58
- log.error('could not execute setLocalDescription', err);
59
- answerSentPromise.reject(err);
60
- throw new Error('Failed to set localDescription');
61
- });
62
- answerSentPromise.resolve();
63
- // wait until candidates are connected
64
- await readCandidatesUntilConnected(connectedPromise, pc, stream);
65
- const remoteAddress = parseRemoteAddress(pc.currentRemoteDescription?.sdp ?? '');
66
- return { pc, muxerFactory, remoteAddress };
67
- }
68
- catch (err) {
69
- pc.close();
70
- throw err;
71
- }
72
- }
73
- export async function initiateConnection({ rtcConfiguration, dataChannelOptions, signal, stream: rawStream }) {
74
- const stream = pbStream(abortableDuplex(rawStream, signal)).pb(Message);
75
- // setup peer connection
76
- const pc = new RTCPeerConnection(rtcConfiguration);
77
- try {
78
- const muxerFactory = new DataChannelMuxerFactory({ peerConnection: pc, dataChannelOptions });
79
- const connectedPromise = pDefer();
80
- resolveOnConnected(pc, connectedPromise);
81
- // reject the connectedPromise if the signal aborts
82
- signal.onabort = connectedPromise.reject;
83
- // we create the channel so that the peerconnection has a component for which
84
- // to collect candidates. The label is not relevant to connection initiation
85
- // but can be useful for debugging
86
- const channel = pc.createDataChannel('init');
87
- // setup callback to write ICE candidates to the remote
88
- // peer
89
- pc.onicecandidate = ({ candidate }) => {
90
- void stream.write({
91
- type: Message.Type.ICE_CANDIDATE,
92
- data: (candidate != null) ? JSON.stringify(candidate.toJSON()) : ''
93
- })
94
- .catch(err => {
95
- log.error('error sending ICE candidate', err);
96
- });
97
- };
98
- // create an offer
99
- const offerSdp = await pc.createOffer();
100
- // write the offer to the stream
101
- await stream.write({ type: Message.Type.SDP_OFFER, data: offerSdp.sdp });
102
- // set offer as local description
103
- await pc.setLocalDescription(offerSdp).catch(err => {
104
- log.error('could not execute setLocalDescription', err);
105
- throw new Error('Failed to set localDescription');
106
- });
107
- // read answer
108
- const answerMessage = await stream.read();
109
- if (answerMessage.type !== Message.Type.SDP_ANSWER) {
110
- throw new Error('remote should send an SDP answer');
111
- }
112
- const answerSdp = new RTCSessionDescription({ type: 'answer', sdp: answerMessage.data });
113
- await pc.setRemoteDescription(answerSdp).catch(err => {
114
- log.error('could not execute setRemoteDescription', err);
115
- throw new Error('Failed to set remoteDescription');
116
- });
117
- await readCandidatesUntilConnected(connectedPromise, pc, stream);
118
- channel.close();
119
- const remoteAddress = parseRemoteAddress(pc.currentRemoteDescription?.sdp ?? '');
120
- return { pc, muxerFactory, remoteAddress };
121
- }
122
- catch (err) {
123
- pc.close();
124
- throw err;
125
- }
126
- }
127
- function parseRemoteAddress(sdp) {
128
- // 'a=candidate:1746876089 1 udp 2113937151 0614fbad-b...ocal 54882 typ host generation 0 network-cost 999'
129
- const candidateLine = sdp.split('\r\n').filter(line => line.startsWith('a=candidate')).pop();
130
- const candidateParts = candidateLine?.split(' ');
131
- if (candidateLine == null || candidateParts == null || candidateParts.length < 5) {
132
- log('could not parse remote address from', candidateLine);
133
- return '/webrtc';
134
- }
135
- return `/dnsaddr/${candidateParts[4]}/${candidateParts[2].toLowerCase()}/${candidateParts[5]}/webrtc`;
136
- }
137
- //# sourceMappingURL=handler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/private-to-private/handler.ts"],"names":[],"mappings":"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"}
@@ -1,6 +0,0 @@
1
- {
2
- "webRTC": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTC.html",
3
- ".:webRTC": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTC.html",
4
- "webRTCDirect": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTCDirect.html",
5
- ".:webRTCDirect": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTCDirect.html"
6
- }