@libp2p/webrtc 6.0.2 → 6.0.3-de2ad9ca5

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 (56) hide show
  1. package/dist/index.min.js +6 -6
  2. package/dist/index.min.js.map +3 -3
  3. package/dist/src/muxer.d.ts +8 -0
  4. package/dist/src/muxer.d.ts.map +1 -1
  5. package/dist/src/muxer.js +38 -15
  6. package/dist/src/muxer.js.map +1 -1
  7. package/dist/src/private-to-private/initiate-connection.d.ts +1 -2
  8. package/dist/src/private-to-private/initiate-connection.d.ts.map +1 -1
  9. package/dist/src/private-to-private/initiate-connection.js +5 -4
  10. package/dist/src/private-to-private/initiate-connection.js.map +1 -1
  11. package/dist/src/private-to-private/signaling-stream-handler.js +4 -4
  12. package/dist/src/private-to-private/signaling-stream-handler.js.map +1 -1
  13. package/dist/src/private-to-private/transport.d.ts.map +1 -1
  14. package/dist/src/private-to-private/transport.js +5 -3
  15. package/dist/src/private-to-private/transport.js.map +1 -1
  16. package/dist/src/private-to-public/listener.d.ts +1 -0
  17. package/dist/src/private-to-public/listener.d.ts.map +1 -1
  18. package/dist/src/private-to-public/listener.js +9 -4
  19. package/dist/src/private-to-public/listener.js.map +1 -1
  20. package/dist/src/private-to-public/transport.d.ts.map +1 -1
  21. package/dist/src/private-to-public/transport.js +5 -3
  22. package/dist/src/private-to-public/transport.js.map +1 -1
  23. package/dist/src/private-to-public/utils/connect.d.ts +4 -4
  24. package/dist/src/private-to-public/utils/connect.d.ts.map +1 -1
  25. package/dist/src/private-to-public/utils/connect.js +7 -9
  26. package/dist/src/private-to-public/utils/connect.js.map +1 -1
  27. package/dist/src/private-to-public/utils/get-rtcpeerconnection.browser.d.ts +6 -1
  28. package/dist/src/private-to-public/utils/get-rtcpeerconnection.browser.d.ts.map +1 -1
  29. package/dist/src/private-to-public/utils/get-rtcpeerconnection.browser.js +15 -3
  30. package/dist/src/private-to-public/utils/get-rtcpeerconnection.browser.js.map +1 -1
  31. package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts +17 -2
  32. package/dist/src/private-to-public/utils/get-rtcpeerconnection.d.ts.map +1 -1
  33. package/dist/src/private-to-public/utils/get-rtcpeerconnection.js +18 -7
  34. package/dist/src/private-to-public/utils/get-rtcpeerconnection.js.map +1 -1
  35. package/dist/src/rtcpeerconnection-to-conn.d.ts +0 -1
  36. package/dist/src/rtcpeerconnection-to-conn.d.ts.map +1 -1
  37. package/dist/src/rtcpeerconnection-to-conn.js.map +1 -1
  38. package/dist/src/stream.d.ts.map +1 -1
  39. package/dist/src/stream.js +25 -17
  40. package/dist/src/stream.js.map +1 -1
  41. package/dist/src/util.js +1 -1
  42. package/dist/src/util.js.map +1 -1
  43. package/package.json +8 -8
  44. package/src/muxer.ts +50 -15
  45. package/src/private-to-private/initiate-connection.ts +6 -5
  46. package/src/private-to-private/signaling-stream-handler.ts +4 -4
  47. package/src/private-to-private/transport.ts +6 -4
  48. package/src/private-to-public/listener.ts +10 -4
  49. package/src/private-to-public/transport.ts +8 -3
  50. package/src/private-to-public/utils/connect.ts +11 -14
  51. package/src/private-to-public/utils/get-rtcpeerconnection.browser.ts +20 -3
  52. package/src/private-to-public/utils/get-rtcpeerconnection.ts +31 -8
  53. package/src/rtcpeerconnection-to-conn.ts +0 -1
  54. package/src/stream.ts +26 -18
  55. package/src/util.ts +1 -1
  56. package/dist/typedoc-urls.json +0 -14
@@ -69,13 +69,13 @@ export async function handleIncomingStream (stream: Stream, connection: Connecti
69
69
  })
70
70
 
71
71
  await peerConnection.setRemoteDescription(offer).catch(err => {
72
- log.error('could not execute setRemoteDescription', err)
72
+ log.error('could not execute setRemoteDescription - %e', err)
73
73
  throw new SDPHandshakeFailedError('Failed to set remoteDescription')
74
74
  })
75
75
 
76
76
  // create and write an SDP answer
77
77
  const answer = await peerConnection.createAnswer().catch(err => {
78
- log.error('could not execute createAnswer', err)
78
+ log.error('could not execute createAnswer - %e', err)
79
79
  throw new SDPHandshakeFailedError('Failed to create answer')
80
80
  })
81
81
 
@@ -87,7 +87,7 @@ export async function handleIncomingStream (stream: Stream, connection: Connecti
87
87
  })
88
88
 
89
89
  await peerConnection.setLocalDescription(answer).catch(err => {
90
- log.error('could not execute setLocalDescription', err)
90
+ log.error('could not execute setLocalDescription - %e', err)
91
91
  throw new SDPHandshakeFailedError('Failed to set localDescription')
92
92
  })
93
93
 
@@ -101,7 +101,7 @@ export async function handleIncomingStream (stream: Stream, connection: Connecti
101
101
  })
102
102
  } catch (err: any) {
103
103
  if (peerConnection.connectionState !== 'connected') {
104
- log.error('error while handling signaling stream from peer %a', connection.remoteAddr, err)
104
+ log.error('error while handling signaling stream from peer %a - %e', connection.remoteAddr, err)
105
105
 
106
106
  peerConnection.close()
107
107
  throw err
@@ -114,7 +114,7 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
114
114
 
115
115
  this._onProtocol(stream, connection, signal)
116
116
  .catch(err => {
117
- this.log.error('failed to handle incoming connect from %p', connection.remotePeer, err)
117
+ this.log.error('failed to handle incoming connect from %p - %e', connection.remotePeer, err)
118
118
  })
119
119
  .finally(() => {
120
120
  signal.clear()
@@ -229,6 +229,7 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
229
229
  })
230
230
 
231
231
  const webRTCConn = toMultiaddrConnection({
232
+ // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
232
233
  peerConnection,
233
234
  remoteAddr: remoteAddress,
234
235
  metrics: this.metrics?.listenerEvents,
@@ -245,9 +246,10 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
245
246
  })
246
247
 
247
248
  // close the connection on shut down
249
+ // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
248
250
  this._closeOnShutdown(peerConnection, webRTCConn)
249
251
  } catch (err: any) {
250
- this.log.error('incoming signaling error', err)
252
+ this.log.error('incoming signaling error - %e', err)
251
253
 
252
254
  peerConnection.close()
253
255
  stream.abort(err)
@@ -255,12 +257,12 @@ export class WebRTCTransport implements Transport<WebRTCDialEvents>, Startable {
255
257
  }
256
258
  }
257
259
 
258
- private _closeOnShutdown (pc: RTCPeerConnection, webRTCConn: MultiaddrConnection): void {
260
+ private _closeOnShutdown (pc: globalThis.RTCPeerConnection, webRTCConn: MultiaddrConnection): void {
259
261
  // close the connection on shut down
260
262
  const shutDownListener = (): void => {
261
263
  webRTCConn.close()
262
264
  .catch(err => {
263
- this.log.error('could not close WebRTCMultiaddrConnection', err)
265
+ this.log.error('could not close WebRTCMultiaddrConnection - %e', err)
264
266
  })
265
267
  }
266
268
 
@@ -27,6 +27,7 @@ export interface WebRTCDirectListenerComponents {
27
27
  keychain?: Keychain
28
28
  datastore: Datastore
29
29
  metrics?: Metrics
30
+ events?: CounterGroup
30
31
  }
31
32
 
32
33
  export interface WebRTCDirectListenerInit {
@@ -159,7 +160,7 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
159
160
 
160
161
  this.incomingConnection(ufrag, remoteHost, remotePort, signal)
161
162
  .catch(err => {
162
- this.log.error('error processing incoming STUN request', err)
163
+ this.log.error('error processing incoming STUN request - %e', err)
163
164
  })
164
165
  .finally(() => {
165
166
  signal.clear()
@@ -184,7 +185,13 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
184
185
  signal.throwIfAborted()
185
186
 
186
187
  // https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md#browser-to-public-server
187
- peerConnection = await createDialerRTCPeerConnection('server', ufrag, this.init.rtcConfiguration, this.certificate)
188
+ const results = await createDialerRTCPeerConnection('server', ufrag, {
189
+ rtcConfiguration: this.init.rtcConfiguration,
190
+ certificate: this.certificate,
191
+ events: this.metrics?.listenerEvents,
192
+ dataChannel: this.init.dataChannel
193
+ })
194
+ peerConnection = results.peerConnection
188
195
 
189
196
  this.connections.set(key, peerConnection)
190
197
 
@@ -201,11 +208,10 @@ export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> impl
201
208
  })
202
209
 
203
210
  try {
204
- await connect(peerConnection, ufrag, {
211
+ await connect(peerConnection, results.muxerFactory, ufrag, {
205
212
  role: 'server',
206
213
  log: this.log,
207
214
  logger: this.components.logger,
208
- metrics: this.components.metrics,
209
215
  events: this.metrics?.listenerEvents,
210
216
  signal,
211
217
  remoteAddr: multiaddr(`/ip${isIPv4(remoteHost) ? 4 : 6}/${remoteHost}/udp/${remotePort}/webrtc-direct`),
@@ -163,14 +163,19 @@ export class WebRTCDirectTransport implements Transport, Startable {
163
163
  const ufrag = genUfrag()
164
164
 
165
165
  // https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md#browser-to-public-server
166
- const peerConnection = await createDialerRTCPeerConnection('client', ufrag, typeof this.init.rtcConfiguration === 'function' ? await this.init.rtcConfiguration() : this.init.rtcConfiguration ?? {})
166
+ const {
167
+ peerConnection,
168
+ muxerFactory
169
+ } = await createDialerRTCPeerConnection('client', ufrag, {
170
+ rtcConfiguration: typeof this.init.rtcConfiguration === 'function' ? await this.init.rtcConfiguration() : this.init.rtcConfiguration ?? {},
171
+ dataChannel: this.init.dataChannel
172
+ })
167
173
 
168
174
  try {
169
- return await connect(peerConnection, ufrag, {
175
+ return await connect(peerConnection, muxerFactory, ufrag, {
170
176
  role: 'client',
171
177
  log: this.log,
172
178
  logger: this.components.logger,
173
- metrics: this.components.metrics,
174
179
  events: this.metrics?.dialerEvents,
175
180
  signal: options.signal,
176
181
  remoteAddr: ma,
@@ -9,13 +9,12 @@ import { generateNoisePrologue } from './generate-noise-prologue.js'
9
9
  import * as sdp from './sdp.js'
10
10
  import type { DirectRTCPeerConnection } from './get-rtcpeerconnection.js'
11
11
  import type { DataChannelOptions } from '../../index.js'
12
- import type { ComponentLogger, Connection, CounterGroup, Logger, Metrics, PeerId, PrivateKey, Upgrader } from '@libp2p/interface'
12
+ import type { ComponentLogger, Connection, CounterGroup, Logger, PeerId, PrivateKey, Upgrader } from '@libp2p/interface'
13
13
  import type { Multiaddr } from '@multiformats/multiaddr'
14
14
 
15
15
  export interface ConnectOptions {
16
16
  log: Logger
17
17
  logger: ComponentLogger
18
- metrics?: Metrics
19
18
  events?: CounterGroup
20
19
  remoteAddr: Multiaddr
21
20
  role: 'client' | 'server'
@@ -37,9 +36,13 @@ export interface ServerOptions extends ConnectOptions {
37
36
 
38
37
  const CONNECTION_STATE_CHANGE_EVENT = isFirefox ? 'iceconnectionstatechange' : 'connectionstatechange'
39
38
 
40
- export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: string, options: ClientOptions): Promise<Connection>
41
- export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: string, options: ServerOptions): Promise<void>
42
- export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: string, options: ConnectOptions): Promise<any> {
39
+ function isServer (options: ClientOptions | ServerOptions, peerConnection: any): peerConnection is DirectRTCPeerConnection {
40
+ return options.role === 'server'
41
+ }
42
+
43
+ export async function connect (peerConnection: RTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ClientOptions): Promise<Connection>
44
+ export async function connect (peerConnection: DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ServerOptions): Promise<void>
45
+ export async function connect (peerConnection: RTCPeerConnection | DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ClientOptions | ServerOptions): Promise<any> {
43
46
  // create data channel for running the noise handshake. Once the data
44
47
  // channel is opened, the listener will initiate the noise handshake. This
45
48
  // is used to confirm the identity of the peer.
@@ -88,7 +91,7 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
88
91
 
89
92
  options.log.trace('%s handshake channel opened', options.role)
90
93
 
91
- if (options.role === 'server') {
94
+ if (isServer(options, peerConnection)) {
92
95
  // now that the connection has been opened, add the remote's certhash to
93
96
  // it's multiaddr so we can complete the noise handshake
94
97
  const remoteFingerprint = peerConnection.remoteFingerprint()?.value ?? ''
@@ -124,6 +127,7 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
124
127
  // Creating the connection before completion of the noise
125
128
  // handshake ensures that the stream opening callback is set up
126
129
  const maConn = toMultiaddrConnection({
130
+ // @ts-expect-error types are broken
127
131
  peerConnection,
128
132
  remoteAddr: options.remoteAddr,
129
133
  metrics: options.events,
@@ -137,7 +141,7 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
137
141
  case 'disconnected':
138
142
  case 'closed':
139
143
  maConn.close().catch((err) => {
140
- options.log.error('error closing connection', err)
144
+ options.log.error('error closing connection - %e', err)
141
145
  maConn.abort(err)
142
146
  })
143
147
  break
@@ -149,13 +153,6 @@ export async function connect (peerConnection: DirectRTCPeerConnection, ufrag: s
149
153
  // Track opened peer connection
150
154
  options.events?.increment({ peer_connection: true })
151
155
 
152
- const muxerFactory = new DataChannelMuxerFactory({
153
- // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
154
- peerConnection,
155
- metrics: options.events,
156
- dataChannelOptions: options.dataChannel
157
- })
158
-
159
156
  if (options.role === 'client') {
160
157
  // For outbound connections, the remote is expected to start the noise
161
158
  // handshake. Therefore, we need to secure an inbound noise connection
@@ -1,4 +1,10 @@
1
- export async function createDialerRTCPeerConnection (role: 'client' | 'server', ufrag: string, rtcConfiguration?: RTCConfiguration | (() => RTCConfiguration | Promise<RTCConfiguration>), certificate?: RTCCertificate): Promise<RTCPeerConnection> {
1
+ import { DataChannelMuxerFactory } from '../../muxer.ts'
2
+ import type { CreateDialerRTCPeerConnectionOptions } from './get-rtcpeerconnection.ts'
3
+
4
+ export async function createDialerRTCPeerConnection (role: 'client' | 'server', ufrag: string, options: CreateDialerRTCPeerConnectionOptions = {}): Promise<{ peerConnection: RTCPeerConnection, muxerFactory: DataChannelMuxerFactory }> {
5
+ // @ts-expect-error options type is wrong
6
+ let certificate: RTCCertificate = options.certificate
7
+
2
8
  if (certificate == null) {
3
9
  // ECDSA is preferred over RSA here. From our testing we find that P-256 elliptic
4
10
  // curve is supported by Pion, webrtc-rs, as well as Chromium (P-228 and P-384
@@ -13,10 +19,21 @@ export async function createDialerRTCPeerConnection (role: 'client' | 'server',
13
19
  })
14
20
  }
15
21
 
16
- const rtcConfig = typeof rtcConfiguration === 'function' ? await rtcConfiguration() : rtcConfiguration
22
+ const rtcConfig = typeof options.rtcConfiguration === 'function' ? await options.rtcConfiguration() : options.rtcConfiguration
17
23
 
18
- return new RTCPeerConnection({
24
+ const peerConnection = new RTCPeerConnection({
19
25
  ...(rtcConfig ?? {}),
20
26
  certificates: [certificate]
21
27
  })
28
+
29
+ const muxerFactory = new DataChannelMuxerFactory({
30
+ peerConnection,
31
+ metrics: options.events,
32
+ dataChannelOptions: options.dataChannel
33
+ })
34
+
35
+ return {
36
+ peerConnection,
37
+ muxerFactory
38
+ }
22
39
  }
@@ -2,8 +2,10 @@ import { Crypto } from '@peculiar/webcrypto'
2
2
  import { PeerConnection } from 'node-datachannel'
3
3
  import { RTCPeerConnection } from 'node-datachannel/polyfill'
4
4
  import { DEFAULT_ICE_SERVERS, MAX_MESSAGE_SIZE } from '../../constants.js'
5
+ import { DataChannelMuxerFactory } from '../../muxer.ts'
5
6
  import { generateTransportCertificate } from './generate-certificates.js'
6
- import type { TransportCertificate } from '../../index.js'
7
+ import type { DataChannelOptions, TransportCertificate } from '../../index.js'
8
+ import type { CounterGroup } from '@libp2p/interface'
7
9
  import type { CertificateFingerprint } from 'node-datachannel'
8
10
 
9
11
  const crypto = new Crypto()
@@ -85,8 +87,17 @@ function mapIceServers (iceServers?: RTCIceServer[]): string[] {
85
87
  .flat() ?? []
86
88
  }
87
89
 
88
- export async function createDialerRTCPeerConnection (role: 'client' | 'server', ufrag: string, rtcConfiguration?: RTCConfiguration | (() => RTCConfiguration | Promise<RTCConfiguration>), certificate?: TransportCertificate): Promise<DirectRTCPeerConnection> {
89
- if (certificate == null) {
90
+ export interface CreateDialerRTCPeerConnectionOptions {
91
+ rtcConfiguration?: RTCConfiguration | (() => RTCConfiguration | Promise<RTCConfiguration>)
92
+ certificate?: TransportCertificate
93
+ events?: CounterGroup
94
+ dataChannel?: DataChannelOptions
95
+ }
96
+
97
+ export async function createDialerRTCPeerConnection (role: 'client', ufrag: string, options?: CreateDialerRTCPeerConnectionOptions): Promise<{ peerConnection: globalThis.RTCPeerConnection, muxerFactory: DataChannelMuxerFactory }>
98
+ export async function createDialerRTCPeerConnection (role: 'server', ufrag: string, options?: CreateDialerRTCPeerConnectionOptions): Promise<{ peerConnection: DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory }>
99
+ export async function createDialerRTCPeerConnection (role: 'client' | 'server', ufrag: string, options: CreateDialerRTCPeerConnectionOptions = {}): Promise<{ peerConnection: globalThis.RTCPeerConnection | DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory }> {
100
+ if (options.certificate == null) {
90
101
  // ECDSA is preferred over RSA here. From our testing we find that P-256
91
102
  // elliptic curve is supported by Pion, webrtc-rs, as well as Chromium
92
103
  // (P-228 and P-384 was not supported in Chromium). We use the same hash
@@ -96,24 +107,36 @@ export async function createDialerRTCPeerConnection (role: 'client' | 'server',
96
107
  namedCurve: 'P-256'
97
108
  }, true, ['sign', 'verify'])
98
109
 
99
- certificate = await generateTransportCertificate(keyPair, {
110
+ options.certificate = await generateTransportCertificate(keyPair, {
100
111
  days: 365
101
112
  })
102
113
  }
103
114
 
104
- const rtcConfig = typeof rtcConfiguration === 'function' ? await rtcConfiguration() : rtcConfiguration
115
+ const rtcConfig = typeof options.rtcConfiguration === 'function' ? await options.rtcConfiguration() : options.rtcConfiguration
105
116
 
106
- return new DirectRTCPeerConnection({
117
+ const peerConnection = new DirectRTCPeerConnection({
107
118
  ...rtcConfig,
108
119
  ufrag,
109
120
  peerConnection: new PeerConnection(`${role}-${Date.now()}`, {
110
121
  disableFingerprintVerification: true,
111
122
  disableAutoNegotiation: true,
112
- certificatePemFile: certificate.pem,
113
- keyPemFile: certificate.privateKey,
123
+ certificatePemFile: options.certificate.pem,
124
+ keyPemFile: options.certificate.privateKey,
114
125
  enableIceUdpMux: role === 'server',
115
126
  maxMessageSize: MAX_MESSAGE_SIZE,
116
127
  iceServers: mapIceServers(rtcConfig?.iceServers ?? DEFAULT_ICE_SERVERS.map(urls => ({ urls })))
117
128
  })
118
129
  })
130
+
131
+ const muxerFactory = new DataChannelMuxerFactory({
132
+ // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370
133
+ peerConnection,
134
+ metrics: options.events,
135
+ dataChannelOptions: options.dataChannel
136
+ })
137
+
138
+ return {
139
+ peerConnection,
140
+ muxerFactory
141
+ }
119
142
  }
@@ -1,5 +1,4 @@
1
1
  import { AbstractMultiaddrConnection } from '@libp2p/utils'
2
- import type { RTCPeerConnection } from './webrtc/index.js'
3
2
  import type { AbortOptions, MultiaddrConnection } from '@libp2p/interface'
4
3
  import type { AbstractMultiaddrConnectionInit, SendResult } from '@libp2p/utils'
5
4
  import type { Uint8ArrayList } from 'uint8arraylist'
package/src/stream.ts CHANGED
@@ -88,23 +88,6 @@ export class WebRTCStream extends AbstractStream {
88
88
  }
89
89
  }
90
90
 
91
- if (this.channel.readyState !== 'open') {
92
- this.log('channel ready state is "%s" and not "open", waiting for "open" event before sending data', this.channel.readyState)
93
- pEvent(this.channel, 'open', {
94
- rejectionEvents: [
95
- 'close',
96
- 'error'
97
- ]
98
- })
99
- .then(() => {
100
- this.log('channel ready state is now "%s", dispatching drain', this.channel.readyState)
101
- this.safeDispatchEvent('drain')
102
- })
103
- .catch(err => {
104
- this.abort(err.error ?? err)
105
- })
106
- }
107
-
108
91
  // pipe framed protobuf messages through a length prefixed decoder, and
109
92
  // surface data from the `Message.message` field through a source.
110
93
  Promise.resolve().then(async () => {
@@ -113,7 +96,7 @@ export class WebRTCStream extends AbstractStream {
113
96
  }
114
97
  })
115
98
  .catch(err => {
116
- this.log.error('error processing incoming data channel messages', err)
99
+ this.log.error('error processing incoming data channel messages - %e', err)
117
100
  })
118
101
 
119
102
  // close when both writable ends are closed or an error occurs
@@ -124,6 +107,26 @@ export class WebRTCStream extends AbstractStream {
124
107
  }
125
108
  }
126
109
  this.addEventListener('close', cleanUpDatachannelOnClose)
110
+
111
+ // chrome can receive message events before the open even is fired - calling
112
+ // code needs to attach message event listeners before these events occur
113
+ // but we need to wait before sending any data so this has to be done async
114
+ if (this.channel.readyState !== 'open') {
115
+ this.log('channel ready state is "%s" and not "open", waiting for "open" event before sending data', this.channel.readyState)
116
+ pEvent(this.channel, 'open', {
117
+ rejectionEvents: [
118
+ 'close',
119
+ 'error'
120
+ ]
121
+ })
122
+ .then(() => {
123
+ this.log('channel ready state is now "%s", dispatching drain', this.channel.readyState)
124
+ this.safeDispatchEvent('drain')
125
+ })
126
+ .catch(err => {
127
+ this.abort(err.error ?? err)
128
+ })
129
+ }
127
130
  }
128
131
 
129
132
  sendNewStream (): void {
@@ -204,8 +207,13 @@ export class WebRTCStream extends AbstractStream {
204
207
  options?.signal?.throwIfAborted()
205
208
  this.receivedFinAck = Promise.withResolvers<void>()
206
209
 
210
+ // wait for either:
211
+ // 1. the FIN_ACK to be received
212
+ // 2. the datachannel to close
213
+ // 3. timeout
207
214
  await Promise.any([
208
215
  raceSignal(this.receivedFinAck.promise, options?.signal),
216
+ pEvent(this.channel, 'close'),
209
217
  new Promise<void>(resolve => {
210
218
  AbortSignal.timeout(this.finAckTimeout)
211
219
  .addEventListener('abort', () => {
package/src/util.ts CHANGED
@@ -77,7 +77,7 @@ export function drainAndClose (channel: RTCDataChannel, direction: string, drain
77
77
  }
78
78
  })
79
79
  .catch(err => {
80
- options.log.error('error closing outbound stream', err)
80
+ options.log.error('error closing outbound stream - %e', err)
81
81
  })
82
82
  }
83
83
 
@@ -1,14 +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
- "TransportCertificate": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.TransportCertificate.html",
5
- ".:TransportCertificate": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.TransportCertificate.html",
6
- "WebRTCDirectTransportComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.WebRTCDirectTransportComponents.html",
7
- "WebRTCTransportComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.WebRTCTransportComponents.html",
8
- "WebRTCTransportDirectInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.WebRTCTransportDirectInit.html",
9
- "WebRTCTransportInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_webrtc.WebRTCTransportInit.html",
10
- "webRTC": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTC.html",
11
- ".:webRTC": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTC.html",
12
- "webRTCDirect": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTCDirect.html",
13
- ".:webRTCDirect": "https://libp2p.github.io/js-libp2p/functions/_libp2p_webrtc.webRTCDirect.html"
14
- }