@libp2p/webrtc 5.2.24 → 6.0.0-55b7e5fea
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -20
- package/dist/index.min.js +28 -29
- 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 +28 -31
- 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/dist/typedoc-urls.json +0 -14
- package/src/maconn.ts +0 -101
package/src/stream.ts
CHANGED
@@ -1,20 +1,17 @@
|
|
1
|
-
import {
|
2
|
-
import { AbstractStream } from '@libp2p/utils
|
3
|
-
import { anySignal } from 'any-signal'
|
1
|
+
import { StreamResetError, StreamStateError } from '@libp2p/interface'
|
2
|
+
import { AbstractStream } from '@libp2p/utils'
|
4
3
|
import * as lengthPrefixed from 'it-length-prefixed'
|
5
4
|
import { pushable } from 'it-pushable'
|
6
|
-
import
|
7
|
-
import pTimeout from 'p-timeout'
|
8
|
-
import { raceEvent } from 'race-event'
|
5
|
+
import { pEvent } from 'p-event'
|
9
6
|
import { raceSignal } from 'race-signal'
|
10
7
|
import { Uint8ArrayList } from 'uint8arraylist'
|
11
|
-
import {
|
8
|
+
import { DEFAULT_FIN_ACK_TIMEOUT, MAX_BUFFERED_AMOUNT, MAX_MESSAGE_SIZE, PROTOBUF_OVERHEAD } from './constants.js'
|
12
9
|
import { Message } from './private-to-public/pb/message.js'
|
10
|
+
import { isFirefox } from './util.js'
|
13
11
|
import type { DataChannelOptions } from './index.js'
|
14
|
-
import type { AbortOptions,
|
15
|
-
import type { AbstractStreamInit } from '@libp2p/utils
|
12
|
+
import type { AbortOptions, MessageStreamDirection, Logger } from '@libp2p/interface'
|
13
|
+
import type { AbstractStreamInit, SendResult } from '@libp2p/utils'
|
16
14
|
import type { Pushable } from 'it-pushable'
|
17
|
-
import type { DeferredPromise } from 'p-defer'
|
18
15
|
|
19
16
|
export interface WebRTCStreamInit extends AbstractStreamInit, DataChannelOptions {
|
20
17
|
/**
|
@@ -39,124 +36,40 @@ export class WebRTCStream extends AbstractStream {
|
|
39
36
|
* and then the protobuf decoder.
|
40
37
|
*/
|
41
38
|
private readonly incomingData: Pushable<Uint8Array>
|
42
|
-
|
43
39
|
private readonly maxBufferedAmount: number
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
/**
|
48
|
-
* The maximum size of a message in bytes
|
49
|
-
*/
|
50
|
-
private readonly maxMessageSize: number
|
51
|
-
|
52
|
-
/**
|
53
|
-
* When this promise is resolved, the remote has sent us a FIN flag
|
54
|
-
*/
|
55
|
-
private readonly receiveFinAck: DeferredPromise<void>
|
56
|
-
private readonly finAckTimeout: number
|
57
|
-
private readonly openTimeout: number
|
58
|
-
private readonly closeController: AbortController
|
40
|
+
private receivedFinAck?: PromiseWithResolvers<void>
|
41
|
+
private finAckTimeout: number
|
59
42
|
|
60
43
|
constructor (init: WebRTCStreamInit) {
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
void Promise.resolve(async () => {
|
67
|
-
if (this.timeline.abort != null || this.timeline.reset !== null) {
|
68
|
-
return
|
69
|
-
}
|
70
|
-
|
71
|
-
// wait for FIN_ACK if we haven't received it already
|
72
|
-
try {
|
73
|
-
await pTimeout(this.receiveFinAck.promise, {
|
74
|
-
milliseconds: this.finAckTimeout
|
75
|
-
})
|
76
|
-
} catch (err) {
|
77
|
-
this.log.error('error receiving FIN_ACK', err)
|
78
|
-
}
|
79
|
-
})
|
80
|
-
.then(() => {
|
81
|
-
// stop processing incoming messages
|
82
|
-
this.incomingData.end()
|
83
|
-
|
84
|
-
// final cleanup
|
85
|
-
originalOnEnd?.(err)
|
86
|
-
})
|
87
|
-
.catch(err => {
|
88
|
-
this.log.error('error ending stream', err)
|
89
|
-
})
|
90
|
-
.finally(() => {
|
91
|
-
this.channel.close()
|
92
|
-
})
|
93
|
-
}
|
94
|
-
|
95
|
-
super(init)
|
44
|
+
super({
|
45
|
+
...init,
|
46
|
+
maxMessageSize: (init.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD
|
47
|
+
})
|
96
48
|
|
97
49
|
this.channel = init.channel
|
98
50
|
this.channel.binaryType = 'arraybuffer'
|
99
51
|
this.incomingData = pushable<Uint8Array>()
|
100
|
-
this.bufferedAmountLowEventTimeout = init.bufferedAmountLowEventTimeout ?? BUFFERED_AMOUNT_LOW_TIMEOUT
|
101
52
|
this.maxBufferedAmount = init.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT
|
102
|
-
this.
|
103
|
-
this.receiveFinAck = pDefer()
|
104
|
-
this.finAckTimeout = init.closeTimeout ?? FIN_ACK_TIMEOUT
|
105
|
-
this.openTimeout = init.openTimeout ?? OPEN_TIMEOUT
|
106
|
-
this.closeController = new AbortController()
|
107
|
-
|
108
|
-
// set up initial state
|
109
|
-
switch (this.channel.readyState) {
|
110
|
-
case 'open':
|
111
|
-
this.timeline.open = new Date().getTime()
|
112
|
-
break
|
113
|
-
|
114
|
-
case 'closed':
|
115
|
-
case 'closing':
|
116
|
-
if (this.timeline.close === undefined || this.timeline.close === 0) {
|
117
|
-
this.timeline.close = Date.now()
|
118
|
-
}
|
119
|
-
break
|
120
|
-
case 'connecting':
|
121
|
-
// noop
|
122
|
-
break
|
123
|
-
|
124
|
-
default:
|
125
|
-
this.log.error('unknown datachannel state %s', this.channel.readyState)
|
126
|
-
throw new StreamStateError('Unknown datachannel state')
|
127
|
-
}
|
53
|
+
this.finAckTimeout = init.finAckTimeout ?? DEFAULT_FIN_ACK_TIMEOUT
|
128
54
|
|
129
55
|
// handle RTCDataChannel events
|
130
|
-
this.channel.
|
131
|
-
this.
|
132
|
-
}
|
133
|
-
|
134
|
-
this.channel.onclose = (_evt) => {
|
135
|
-
this.log.trace('received onclose event')
|
56
|
+
this.channel.onclose = () => {
|
57
|
+
this.log.trace('received datachannel close event')
|
136
58
|
|
137
|
-
|
138
|
-
this.
|
139
|
-
|
140
|
-
// if the channel has closed we'll never receive a FIN_ACK so resolve the
|
141
|
-
// promise so we don't try to wait later
|
142
|
-
this.receiveFinAck.resolve()
|
143
|
-
|
144
|
-
void this.close().catch(err => {
|
145
|
-
this.log.error('error closing stream after channel closed', err)
|
146
|
-
})
|
59
|
+
this.onRemoteCloseWrite()
|
60
|
+
this.onTransportClosed()
|
147
61
|
}
|
148
62
|
|
149
63
|
this.channel.onerror = (evt) => {
|
150
|
-
|
64
|
+
const err = (evt as RTCErrorEvent).error
|
151
65
|
|
152
|
-
|
153
|
-
this.closeController.abort()
|
66
|
+
this.log.trace('received datachannel error event - %e', err)
|
154
67
|
|
155
|
-
const err = (evt as RTCErrorEvent).error
|
156
68
|
this.abort(err)
|
157
69
|
}
|
158
70
|
|
159
71
|
this.channel.onmessage = async (event: MessageEvent<ArrayBuffer>) => {
|
72
|
+
this.log('incoming message %d bytes', event.data.byteLength)
|
160
73
|
const { data } = event
|
161
74
|
|
162
75
|
if (data === null || data.byteLength === 0) {
|
@@ -166,187 +79,186 @@ export class WebRTCStream extends AbstractStream {
|
|
166
79
|
this.incomingData.push(new Uint8Array(data, 0, data.byteLength))
|
167
80
|
}
|
168
81
|
|
169
|
-
|
82
|
+
// dispatch drain event when the buffered amount drops to zero
|
83
|
+
this.channel.bufferedAmountLowThreshold = 0
|
84
|
+
|
85
|
+
this.channel.onbufferedamountlow = () => {
|
86
|
+
if (this.writableNeedsDrain) {
|
87
|
+
this.safeDispatchEvent('drain')
|
88
|
+
}
|
89
|
+
}
|
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
|
+
}
|
170
107
|
|
171
108
|
// pipe framed protobuf messages through a length prefixed decoder, and
|
172
109
|
// surface data from the `Message.message` field through a source.
|
173
110
|
Promise.resolve().then(async () => {
|
174
111
|
for await (const buf of lengthPrefixed.decode(this.incomingData)) {
|
175
|
-
|
176
|
-
|
177
|
-
if (message != null) {
|
178
|
-
self.sourcePush(new Uint8ArrayList(message))
|
179
|
-
}
|
112
|
+
this.processIncomingProtobuf(buf)
|
180
113
|
}
|
181
114
|
})
|
182
115
|
.catch(err => {
|
183
116
|
this.log.error('error processing incoming data channel messages', err)
|
184
117
|
})
|
118
|
+
|
119
|
+
// close when both writable ends are closed or an error occurs
|
120
|
+
const cleanUpDatachannelOnClose = (): void => {
|
121
|
+
if (this.channel.readyState === 'open') {
|
122
|
+
this.log.trace('stream closed, closing underlying datachannel')
|
123
|
+
this.channel.close()
|
124
|
+
}
|
125
|
+
}
|
126
|
+
this.addEventListener('close', cleanUpDatachannelOnClose)
|
185
127
|
}
|
186
128
|
|
187
129
|
sendNewStream (): void {
|
188
130
|
// opening new streams is handled by WebRTC so this is a noop
|
189
131
|
}
|
190
132
|
|
191
|
-
|
192
|
-
if (this.channel.readyState
|
133
|
+
_sendMessage (data: Uint8ArrayList): void {
|
134
|
+
if (this.channel.readyState !== 'open') {
|
193
135
|
throw new StreamStateError(`Invalid datachannel state - ${this.channel.readyState}`)
|
194
136
|
}
|
195
137
|
|
196
|
-
|
197
|
-
const timeout = AbortSignal.timeout(this.openTimeout)
|
198
|
-
const signal = anySignal([
|
199
|
-
this.closeController.signal,
|
200
|
-
timeout
|
201
|
-
])
|
202
|
-
|
203
|
-
try {
|
204
|
-
this.log('channel state is "%s" and not "open", waiting for "open" event before sending data', this.channel.readyState)
|
205
|
-
await raceEvent(this.channel, 'open', signal)
|
206
|
-
} finally {
|
207
|
-
signal.clear()
|
208
|
-
}
|
138
|
+
this.log.trace('sending message, channel state "%s"', this.channel.readyState)
|
209
139
|
|
210
|
-
|
140
|
+
if (isFirefox) {
|
141
|
+
// TODO: firefox can deliver small messages out of order - remove once a
|
142
|
+
// browser with https://bugzilla.mozilla.org/show_bug.cgi?id=1983831 is
|
143
|
+
// available in playwright-test
|
144
|
+
this.channel.send(data.subarray())
|
145
|
+
return
|
211
146
|
}
|
212
147
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
this.closeController.signal,
|
217
|
-
timeout
|
218
|
-
])
|
219
|
-
|
220
|
-
try {
|
221
|
-
this.log('channel buffer is %d, wait for "bufferedamountlow" event', this.channel.bufferedAmount)
|
222
|
-
await raceEvent(this.channel, 'bufferedamountlow', signal)
|
223
|
-
} catch (err: any) {
|
224
|
-
if (timeout.aborted) {
|
225
|
-
throw new TimeoutError(`Timed out waiting for DataChannel buffer to clear after ${this.bufferedAmountLowEventTimeout}ms`)
|
226
|
-
}
|
227
|
-
|
228
|
-
throw err
|
229
|
-
} finally {
|
230
|
-
signal.clear()
|
231
|
-
}
|
148
|
+
// send message without copying data
|
149
|
+
for (const buf of data) {
|
150
|
+
this.channel.send(buf)
|
232
151
|
}
|
152
|
+
}
|
233
153
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
154
|
+
sendData (data: Uint8ArrayList): SendResult {
|
155
|
+
if (this.channel.readyState !== 'open') {
|
156
|
+
return {
|
157
|
+
sentBytes: 0,
|
158
|
+
canSendMore: false
|
159
|
+
}
|
240
160
|
}
|
241
|
-
}
|
242
161
|
|
243
|
-
|
244
|
-
|
245
|
-
//
|
246
|
-
//
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
data
|
162
|
+
// TODO: firefox can deliver small messages out of order - remove once a
|
163
|
+
// browser with https://bugzilla.mozilla.org/show_bug.cgi?id=1983831 is
|
164
|
+
// available in playwright-test
|
165
|
+
// ----
|
166
|
+
// this is also necessary to work with rust-libp2p 0.54 though 0.53 seems ok
|
167
|
+
this._sendMessage(
|
168
|
+
lengthPrefixed.encode.single(Message.encode({
|
169
|
+
message: data.subarray()
|
170
|
+
}))
|
171
|
+
)
|
172
|
+
|
173
|
+
/*
|
174
|
+
// TODO: enable this when FF and rust-libp2p are not broken
|
175
|
+
// send message without copying data
|
176
|
+
for (const message of data) {
|
177
|
+
this._sendMessage(
|
178
|
+
lengthPrefixed.encode.single(Message.encode({
|
179
|
+
message
|
180
|
+
}))
|
181
|
+
)
|
182
|
+
}
|
258
183
|
}
|
184
|
+
*/
|
259
185
|
|
260
|
-
|
186
|
+
return {
|
187
|
+
sentBytes: data.byteLength,
|
188
|
+
canSendMore: this.channel.bufferedAmount < this.maxBufferedAmount
|
189
|
+
}
|
261
190
|
}
|
262
191
|
|
263
|
-
|
192
|
+
sendReset (err: Error): void {
|
264
193
|
try {
|
265
|
-
|
194
|
+
this.log.error('sending reset - %e', err)
|
195
|
+
this._sendFlag(Message.Flag.RESET)
|
196
|
+
this.receivedFinAck?.reject(err)
|
266
197
|
} catch (err) {
|
267
198
|
this.log.error('failed to send reset - %e', err)
|
268
|
-
} finally {
|
269
|
-
this.channel.close()
|
270
199
|
}
|
271
200
|
}
|
272
201
|
|
273
|
-
async sendCloseWrite (options
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
})
|
288
|
-
} catch (err) {
|
289
|
-
this.log.error('failed to await FIN_ACK', err)
|
290
|
-
}
|
291
|
-
} else {
|
292
|
-
this.log.trace('sending FIN failed, not awaiting FIN_ACK')
|
293
|
-
}
|
294
|
-
|
295
|
-
// if we've attempted to receive a FIN_ACK, do not try again
|
296
|
-
this.receiveFinAck.resolve()
|
202
|
+
async sendCloseWrite (options?: AbortOptions): Promise<void> {
|
203
|
+
this._sendFlag(Message.Flag.FIN)
|
204
|
+
options?.signal?.throwIfAborted()
|
205
|
+
this.receivedFinAck = Promise.withResolvers<void>()
|
206
|
+
|
207
|
+
await Promise.any([
|
208
|
+
raceSignal(this.receivedFinAck.promise, options?.signal),
|
209
|
+
new Promise<void>(resolve => {
|
210
|
+
AbortSignal.timeout(this.finAckTimeout)
|
211
|
+
.addEventListener('abort', () => {
|
212
|
+
resolve()
|
213
|
+
})
|
214
|
+
})
|
215
|
+
])
|
297
216
|
}
|
298
217
|
|
299
|
-
async sendCloseRead (): Promise<void> {
|
300
|
-
|
301
|
-
|
302
|
-
}
|
303
|
-
|
304
|
-
await this._sendFlag(Message.Flag.STOP_SENDING)
|
218
|
+
async sendCloseRead (options?: AbortOptions): Promise<void> {
|
219
|
+
this._sendFlag(Message.Flag.STOP_SENDING)
|
220
|
+
options?.signal?.throwIfAborted()
|
305
221
|
}
|
306
222
|
|
307
223
|
/**
|
308
224
|
* Handle incoming
|
309
225
|
*/
|
310
|
-
private processIncomingProtobuf (buffer: Uint8ArrayList):
|
226
|
+
private processIncomingProtobuf (buffer: Uint8ArrayList): void {
|
311
227
|
const message = Message.decode(buffer)
|
312
228
|
|
229
|
+
// ignore data messages if we've closed the readable end already
|
230
|
+
if (message.message != null && (this.readStatus === 'readable' || this.readStatus === 'paused')) {
|
231
|
+
this.onData(new Uint8ArrayList(message.message))
|
232
|
+
}
|
233
|
+
|
313
234
|
if (message.flag !== undefined) {
|
314
235
|
this.log.trace('incoming flag %s, write status "%s", read status "%s"', message.flag, this.writeStatus, this.readStatus)
|
315
236
|
|
316
237
|
if (message.flag === Message.Flag.FIN) {
|
317
|
-
//
|
318
|
-
this.
|
319
|
-
|
320
|
-
this.log.trace('sending FIN_ACK')
|
321
|
-
void this._sendFlag(Message.Flag.FIN_ACK)
|
322
|
-
.catch(err => {
|
323
|
-
this.log.error('error sending FIN_ACK immediately', err)
|
324
|
-
})
|
238
|
+
// we should expect no more data from the remote, stop reading
|
239
|
+
this._sendFlag(Message.Flag.FIN_ACK)
|
240
|
+
this.onRemoteCloseWrite()
|
325
241
|
}
|
326
242
|
|
327
243
|
if (message.flag === Message.Flag.RESET) {
|
328
|
-
//
|
329
|
-
this.reset
|
244
|
+
// stop reading and writing to the stream immediately
|
245
|
+
this.receivedFinAck?.reject(new StreamResetError('The stream was reset'))
|
246
|
+
this.onRemoteReset()
|
330
247
|
}
|
331
248
|
|
332
249
|
if (message.flag === Message.Flag.STOP_SENDING) {
|
333
|
-
//
|
334
|
-
this.
|
250
|
+
// the remote has stopped reading
|
251
|
+
this.onRemoteCloseRead()
|
335
252
|
}
|
336
253
|
|
337
254
|
if (message.flag === Message.Flag.FIN_ACK) {
|
338
|
-
|
339
|
-
this.
|
255
|
+
// remote received our FIN
|
256
|
+
this.receivedFinAck?.resolve()
|
340
257
|
}
|
341
258
|
}
|
342
|
-
|
343
|
-
// ignore data messages if we've closed the readable end already
|
344
|
-
if (this.readStatus === 'ready') {
|
345
|
-
return message.message
|
346
|
-
}
|
347
259
|
}
|
348
260
|
|
349
|
-
private
|
261
|
+
private _sendFlag (flag: Message.Flag): boolean {
|
350
262
|
if (this.channel.readyState !== 'open') {
|
351
263
|
// flags can be sent while we or the remote are closing the datachannel so
|
352
264
|
// if the channel isn't open, don't try to send it but return false to let
|
@@ -360,7 +272,7 @@ export class WebRTCStream extends AbstractStream {
|
|
360
272
|
const prefixedBuf = lengthPrefixed.encode.single(messageBuf)
|
361
273
|
|
362
274
|
try {
|
363
|
-
|
275
|
+
this._sendMessage(prefixedBuf)
|
364
276
|
|
365
277
|
return true
|
366
278
|
} catch (err: any) {
|
@@ -369,6 +281,14 @@ export class WebRTCStream extends AbstractStream {
|
|
369
281
|
|
370
282
|
return false
|
371
283
|
}
|
284
|
+
|
285
|
+
sendPause (): void {
|
286
|
+
// TODO: read backpressure?
|
287
|
+
}
|
288
|
+
|
289
|
+
sendResume (): void {
|
290
|
+
// TODO: read backpressure?
|
291
|
+
}
|
372
292
|
}
|
373
293
|
|
374
294
|
export interface WebRTCStreamOptions extends DataChannelOptions {
|
@@ -383,12 +303,7 @@ export interface WebRTCStreamOptions extends DataChannelOptions {
|
|
383
303
|
/**
|
384
304
|
* The stream direction
|
385
305
|
*/
|
386
|
-
direction:
|
387
|
-
|
388
|
-
/**
|
389
|
-
* A callback invoked when the channel ends
|
390
|
-
*/
|
391
|
-
onEnd?(err?: Error | undefined): void
|
306
|
+
direction: MessageStreamDirection
|
392
307
|
|
393
308
|
/**
|
394
309
|
* The logger to create a scope from
|
@@ -399,15 +314,16 @@ export interface WebRTCStreamOptions extends DataChannelOptions {
|
|
399
314
|
* If true the underlying datachannel is being used to perform the noise
|
400
315
|
* handshake during connection establishment
|
401
316
|
*/
|
402
|
-
|
317
|
+
isHandshake?: boolean
|
403
318
|
}
|
404
319
|
|
405
320
|
export function createStream (options: WebRTCStreamOptions): WebRTCStream {
|
406
|
-
const { channel, direction,
|
321
|
+
const { channel, direction, isHandshake } = options
|
407
322
|
|
408
323
|
return new WebRTCStream({
|
409
324
|
...options,
|
410
325
|
id: `${channel.id}`,
|
411
|
-
log: options.log.newScope(`${
|
326
|
+
log: options.log.newScope(`${isHandshake === true ? 'handshake' : direction}:${channel.id}`),
|
327
|
+
protocol: ''
|
412
328
|
})
|
413
329
|
}
|
package/src/util.ts
CHANGED
@@ -2,8 +2,9 @@ import { detect } from 'detect-browser'
|
|
2
2
|
import pDefer from 'p-defer'
|
3
3
|
import pTimeout from 'p-timeout'
|
4
4
|
import { DATA_CHANNEL_DRAIN_TIMEOUT, DEFAULT_ICE_SERVERS, UFRAG_ALPHABET, UFRAG_PREFIX } from './constants.js'
|
5
|
-
import type { PeerConnection } from '@ipshipyard/node-datachannel'
|
6
5
|
import type { LoggerOptions } from '@libp2p/interface'
|
6
|
+
import type { Duplex, Source } from 'it-stream-types'
|
7
|
+
import type { PeerConnection } from 'node-datachannel'
|
7
8
|
|
8
9
|
const browser = detect()
|
9
10
|
export const isFirefox = ((browser != null) && browser.name === 'firefox')
|
@@ -12,6 +13,26 @@ export const nopSource = async function * nop (): AsyncGenerator<Uint8Array, any
|
|
12
13
|
|
13
14
|
export const nopSink = async (_: any): Promise<void> => {}
|
14
15
|
|
16
|
+
// Duplex that does nothing. Needed to fulfill the interface
|
17
|
+
export function inertDuplex (): Duplex<any, any, any> {
|
18
|
+
return {
|
19
|
+
source: {
|
20
|
+
[Symbol.asyncIterator] () {
|
21
|
+
return {
|
22
|
+
async next () {
|
23
|
+
// This will never resolve
|
24
|
+
return new Promise(() => { })
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
},
|
29
|
+
sink: async (source: Source<any>) => {
|
30
|
+
// This will never resolve
|
31
|
+
return new Promise(() => { })
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
15
36
|
export function drainAndClose (channel: RTCDataChannel, direction: string, drainTimeout: number = DATA_CHANNEL_DRAIN_TIMEOUT, options: LoggerOptions): void {
|
16
37
|
if (channel.readyState !== 'open') {
|
17
38
|
return
|
package/src/webrtc/index.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export { RTCSessionDescription, RTCIceCandidate, RTCPeerConnection } from '
|
1
|
+
export { RTCSessionDescription, RTCIceCandidate, RTCPeerConnection } from 'node-datachannel/polyfill'
|
package/dist/src/maconn.d.ts
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
import type { RTCPeerConnection } from './webrtc/index.js';
|
2
|
-
import type { ComponentLogger, Logger, MultiaddrConnection, MultiaddrConnectionTimeline, CounterGroup } from '@libp2p/interface';
|
3
|
-
import type { AbortOptions, Multiaddr } from '@multiformats/multiaddr';
|
4
|
-
import type { Source, Sink } from 'it-stream-types';
|
5
|
-
import type { Uint8ArrayList } from 'uint8arraylist';
|
6
|
-
interface WebRTCMultiaddrConnectionInit {
|
7
|
-
/**
|
8
|
-
* WebRTC Peer Connection
|
9
|
-
*/
|
10
|
-
peerConnection: RTCPeerConnection;
|
11
|
-
/**
|
12
|
-
* The multiaddr address used to communicate with the remote peer
|
13
|
-
*/
|
14
|
-
remoteAddr: Multiaddr;
|
15
|
-
/**
|
16
|
-
* Holds the relevant events timestamps of the connection
|
17
|
-
*/
|
18
|
-
timeline: MultiaddrConnectionTimeline;
|
19
|
-
/**
|
20
|
-
* Optional metrics counter group for this connection
|
21
|
-
*/
|
22
|
-
metrics?: CounterGroup;
|
23
|
-
}
|
24
|
-
export interface WebRTCMultiaddrConnectionComponents {
|
25
|
-
logger: ComponentLogger;
|
26
|
-
}
|
27
|
-
export declare class WebRTCMultiaddrConnection implements MultiaddrConnection {
|
28
|
-
readonly log: Logger;
|
29
|
-
/**
|
30
|
-
* WebRTC Peer Connection
|
31
|
-
*/
|
32
|
-
readonly peerConnection: RTCPeerConnection;
|
33
|
-
/**
|
34
|
-
* The multiaddr address used to communicate with the remote peer
|
35
|
-
*/
|
36
|
-
remoteAddr: Multiaddr;
|
37
|
-
/**
|
38
|
-
* Holds the life cycle times of the connection
|
39
|
-
*/
|
40
|
-
timeline: MultiaddrConnectionTimeline;
|
41
|
-
/**
|
42
|
-
* Optional metrics counter group for this connection
|
43
|
-
*/
|
44
|
-
metrics?: CounterGroup;
|
45
|
-
/**
|
46
|
-
* The stream source, a no-op as the transport natively supports multiplexing
|
47
|
-
*/
|
48
|
-
source: AsyncGenerator<Uint8Array, any, unknown>;
|
49
|
-
/**
|
50
|
-
* The stream destination, a no-op as the transport natively supports multiplexing
|
51
|
-
*/
|
52
|
-
sink: Sink<Source<Uint8Array | Uint8ArrayList>, Promise<void>>;
|
53
|
-
constructor(components: WebRTCMultiaddrConnectionComponents, init: WebRTCMultiaddrConnectionInit);
|
54
|
-
close(options?: AbortOptions): Promise<void>;
|
55
|
-
abort(err: Error): void;
|
56
|
-
}
|
57
|
-
export {};
|
58
|
-
//# sourceMappingURL=maconn.d.ts.map
|
package/dist/src/maconn.d.ts.map
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"maconn.d.ts","sourceRoot":"","sources":["../../src/maconn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChI,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,UAAU,6BAA6B;IACrC;;OAEG;IACH,cAAc,EAAE,iBAAiB,CAAA;IAEjC;;OAEG;IACH,UAAU,EAAE,SAAS,CAAA;IAErB;;OAEG;IACH,QAAQ,EAAE,2BAA2B,CAAA;IAErC;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB;AAED,MAAM,WAAW,mCAAmC;IAClD,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,QAAQ,CAAC,cAAc,EAAE,iBAAiB,CAAA;IAE1C;;OAEG;IACH,UAAU,EAAE,SAAS,CAAA;IAErB;;OAEG;IACH,QAAQ,EAAE,2BAA2B,CAAA;IAErC;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IAEtB;;OAEG;IACH,MAAM,EAAE,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,CAAc;IAE9D;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,cAAc,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAU;gBAE3D,UAAU,EAAE,mCAAmC,EAAE,IAAI,EAAE,6BAA6B;IAmB3F,KAAK,CAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAQnD,KAAK,CAAE,GAAG,EAAE,KAAK,GAAG,IAAI;CAOzB"}
|