@libp2p/webrtc 5.2.24-8484de8a2 → 5.2.24-9a9b11fd4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.min.js +13 -13
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +4 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/muxer.d.ts.map +1 -1
- package/dist/src/muxer.js +7 -12
- package/dist/src/muxer.js.map +1 -1
- package/dist/src/private-to-private/initiate-connection.d.ts.map +1 -1
- package/dist/src/private-to-private/initiate-connection.js +15 -1
- package/dist/src/private-to-private/initiate-connection.js.map +1 -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 +10 -2
- package/dist/src/private-to-private/signaling-stream-handler.js.map +1 -1
- package/dist/src/private-to-private/transport.d.ts +0 -7
- package/dist/src/private-to-private/transport.d.ts.map +1 -1
- package/dist/src/private-to-private/transport.js.map +1 -1
- package/dist/src/private-to-private/util.d.ts +0 -1
- package/dist/src/private-to-private/util.d.ts.map +1 -1
- package/dist/src/private-to-private/util.js +11 -11
- package/dist/src/private-to-private/util.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.map +1 -1
- package/dist/src/rtcpeerconnection-to-conn.d.ts.map +1 -1
- package/dist/src/rtcpeerconnection-to-conn.js +3 -0
- package/dist/src/rtcpeerconnection-to-conn.js.map +1 -1
- package/dist/src/stream.d.ts +3 -2
- package/dist/src/stream.d.ts.map +1 -1
- package/dist/src/stream.js +100 -68
- package/dist/src/stream.js.map +1 -1
- package/package.json +10 -10
- package/src/constants.ts +5 -0
- package/src/index.ts +9 -0
- package/src/muxer.ts +6 -13
- package/src/private-to-private/initiate-connection.ts +18 -2
- package/src/private-to-private/signaling-stream-handler.ts +12 -2
- package/src/private-to-private/transport.ts +0 -8
- package/src/private-to-private/util.ts +12 -12
- package/src/private-to-public/transport.ts +0 -10
- package/src/rtcpeerconnection-to-conn.ts +4 -0
- package/src/stream.ts +115 -79
package/src/stream.ts
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
import { StreamStateError } from '@libp2p/interface'
|
1
|
+
import { StreamResetError, StreamStateError } from '@libp2p/interface'
|
2
2
|
import { AbstractStream } from '@libp2p/utils'
|
3
3
|
import * as lengthPrefixed from 'it-length-prefixed'
|
4
4
|
import { pushable } from 'it-pushable'
|
5
|
+
import { pEvent } from 'p-event'
|
5
6
|
import { raceSignal } from 'race-signal'
|
6
7
|
import { Uint8ArrayList } from 'uint8arraylist'
|
7
|
-
import { MAX_BUFFERED_AMOUNT, MAX_MESSAGE_SIZE, PROTOBUF_OVERHEAD } from './constants.js'
|
8
|
+
import { DEFAULT_FIN_ACK_TIMEOUT, MAX_BUFFERED_AMOUNT, MAX_MESSAGE_SIZE, PROTOBUF_OVERHEAD } from './constants.js'
|
8
9
|
import { Message } from './private-to-public/pb/message.js'
|
10
|
+
import { isFirefox } from './util.js'
|
9
11
|
import type { DataChannelOptions } from './index.js'
|
10
12
|
import type { AbortOptions, MessageStreamDirection, Logger } from '@libp2p/interface'
|
11
13
|
import type { AbstractStreamInit, SendResult } from '@libp2p/utils'
|
@@ -35,7 +37,8 @@ export class WebRTCStream extends AbstractStream {
|
|
35
37
|
*/
|
36
38
|
private readonly incomingData: Pushable<Uint8Array>
|
37
39
|
private readonly maxBufferedAmount: number
|
38
|
-
private
|
40
|
+
private receivedFinAck?: PromiseWithResolvers<void>
|
41
|
+
private finAckTimeout: number
|
39
42
|
|
40
43
|
constructor (init: WebRTCStreamInit) {
|
41
44
|
super({
|
@@ -47,44 +50,26 @@ export class WebRTCStream extends AbstractStream {
|
|
47
50
|
this.channel.binaryType = 'arraybuffer'
|
48
51
|
this.incomingData = pushable<Uint8Array>()
|
49
52
|
this.maxBufferedAmount = init.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT
|
50
|
-
this.
|
51
|
-
|
52
|
-
// set up initial state
|
53
|
-
switch (this.channel.readyState) {
|
54
|
-
case 'open':
|
55
|
-
break
|
56
|
-
|
57
|
-
case 'closed':
|
58
|
-
case 'closing':
|
59
|
-
if (this.timeline.close === undefined || this.timeline.close === 0) {
|
60
|
-
this.timeline.close = Date.now()
|
61
|
-
}
|
62
|
-
break
|
63
|
-
case 'connecting':
|
64
|
-
// noop
|
65
|
-
break
|
66
|
-
|
67
|
-
default:
|
68
|
-
this.log.error('unknown datachannel state %s', this.channel.readyState)
|
69
|
-
throw new StreamStateError('Unknown datachannel state')
|
70
|
-
}
|
53
|
+
this.finAckTimeout = init.finAckTimeout ?? DEFAULT_FIN_ACK_TIMEOUT
|
71
54
|
|
72
55
|
// handle RTCDataChannel events
|
73
|
-
this.channel.onclose = (
|
74
|
-
this.log.trace('received
|
56
|
+
this.channel.onclose = () => {
|
57
|
+
this.log.trace('received datachannel close event')
|
75
58
|
|
76
59
|
this.onRemoteCloseWrite()
|
77
60
|
this.onTransportClosed()
|
78
61
|
}
|
79
62
|
|
80
63
|
this.channel.onerror = (evt) => {
|
81
|
-
this.log.trace('received onerror event')
|
82
|
-
|
83
64
|
const err = (evt as RTCErrorEvent).error
|
65
|
+
|
66
|
+
this.log.trace('received datachannel error event - %e', err)
|
67
|
+
|
84
68
|
this.abort(err)
|
85
69
|
}
|
86
70
|
|
87
71
|
this.channel.onmessage = async (event: MessageEvent<ArrayBuffer>) => {
|
72
|
+
this.log('incoming message %d bytes', event.data.byteLength)
|
88
73
|
const { data } = event
|
89
74
|
|
90
75
|
if (data === null || data.byteLength === 0) {
|
@@ -96,32 +81,49 @@ export class WebRTCStream extends AbstractStream {
|
|
96
81
|
|
97
82
|
// dispatch drain event when the buffered amount drops to zero
|
98
83
|
this.channel.bufferedAmountLowThreshold = 0
|
84
|
+
|
99
85
|
this.channel.onbufferedamountlow = () => {
|
100
|
-
this.
|
86
|
+
if (this.writableNeedsDrain) {
|
87
|
+
this.safeDispatchEvent('drain')
|
88
|
+
}
|
101
89
|
}
|
102
90
|
|
103
|
-
|
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
|
+
}
|
104
107
|
|
105
108
|
// pipe framed protobuf messages through a length prefixed decoder, and
|
106
109
|
// surface data from the `Message.message` field through a source.
|
107
110
|
Promise.resolve().then(async () => {
|
108
111
|
for await (const buf of lengthPrefixed.decode(this.incomingData)) {
|
109
|
-
|
110
|
-
|
111
|
-
if (message != null) {
|
112
|
-
self.onData(new Uint8ArrayList(message))
|
113
|
-
}
|
112
|
+
this.processIncomingProtobuf(buf)
|
114
113
|
}
|
115
114
|
})
|
116
115
|
.catch(err => {
|
117
116
|
this.log.error('error processing incoming data channel messages', err)
|
118
117
|
})
|
119
118
|
|
120
|
-
//
|
121
|
-
const
|
122
|
-
this.channel.
|
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
|
+
}
|
123
125
|
}
|
124
|
-
this.addEventListener('close',
|
126
|
+
this.addEventListener('close', cleanUpDatachannelOnClose)
|
125
127
|
}
|
126
128
|
|
127
129
|
sendNewStream (): void {
|
@@ -129,27 +131,57 @@ export class WebRTCStream extends AbstractStream {
|
|
129
131
|
}
|
130
132
|
|
131
133
|
_sendMessage (data: Uint8ArrayList): void {
|
132
|
-
if (this.channel.readyState
|
134
|
+
if (this.channel.readyState !== 'open') {
|
133
135
|
throw new StreamStateError(`Invalid datachannel state - ${this.channel.readyState}`)
|
134
136
|
}
|
135
137
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
138
|
+
this.log.trace('sending message, channel state "%s"', this.channel.readyState)
|
139
|
+
|
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
|
146
|
+
}
|
147
|
+
|
148
|
+
// send message without copying data
|
149
|
+
for (const buf of data) {
|
150
|
+
this.channel.send(buf)
|
144
151
|
}
|
145
152
|
}
|
146
153
|
|
147
154
|
sendData (data: Uint8ArrayList): SendResult {
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
155
|
+
if (this.channel.readyState !== 'open') {
|
156
|
+
return {
|
157
|
+
sentBytes: 0,
|
158
|
+
canSendMore: false
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
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
|
+
}
|
183
|
+
}
|
184
|
+
*/
|
153
185
|
|
154
186
|
return {
|
155
187
|
sentBytes: data.byteLength,
|
@@ -157,69 +189,73 @@ export class WebRTCStream extends AbstractStream {
|
|
157
189
|
}
|
158
190
|
}
|
159
191
|
|
160
|
-
sendReset (): void {
|
161
|
-
this.receivedFinAck.resolve()
|
162
|
-
|
192
|
+
sendReset (err: Error): void {
|
163
193
|
try {
|
194
|
+
this.log.error('sending reset - %e', err)
|
164
195
|
this._sendFlag(Message.Flag.RESET)
|
196
|
+
this.receivedFinAck?.reject(err)
|
165
197
|
} catch (err) {
|
166
198
|
this.log.error('failed to send reset - %e', err)
|
167
|
-
} finally {
|
168
|
-
this.channel.close()
|
169
199
|
}
|
170
200
|
}
|
171
201
|
|
172
202
|
async sendCloseWrite (options?: AbortOptions): Promise<void> {
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
await
|
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
|
+
])
|
178
216
|
}
|
179
217
|
|
180
218
|
async sendCloseRead (options?: AbortOptions): Promise<void> {
|
181
|
-
|
182
|
-
this._sendFlag(Message.Flag.STOP_SENDING)
|
183
|
-
}
|
184
|
-
|
219
|
+
this._sendFlag(Message.Flag.STOP_SENDING)
|
185
220
|
options?.signal?.throwIfAborted()
|
186
221
|
}
|
187
222
|
|
188
223
|
/**
|
189
224
|
* Handle incoming
|
190
225
|
*/
|
191
|
-
private processIncomingProtobuf (buffer: Uint8ArrayList):
|
226
|
+
private processIncomingProtobuf (buffer: Uint8ArrayList): void {
|
192
227
|
const message = Message.decode(buffer)
|
193
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
|
+
|
194
234
|
if (message.flag !== undefined) {
|
195
235
|
this.log.trace('incoming flag %s, write status "%s", read status "%s"', message.flag, this.writeStatus, this.readStatus)
|
196
236
|
|
197
237
|
if (message.flag === Message.Flag.FIN) {
|
198
|
-
//
|
199
|
-
this.onRemoteCloseWrite()
|
238
|
+
// we should expect no more data from the remote, stop reading
|
200
239
|
this._sendFlag(Message.Flag.FIN_ACK)
|
240
|
+
this.onRemoteCloseWrite()
|
201
241
|
}
|
202
242
|
|
203
243
|
if (message.flag === Message.Flag.RESET) {
|
204
|
-
|
205
|
-
|
244
|
+
// stop reading and writing to the stream immediately
|
245
|
+
this.receivedFinAck?.reject(new StreamResetError('The stream was reset'))
|
206
246
|
this.onRemoteReset()
|
207
247
|
}
|
208
248
|
|
209
249
|
if (message.flag === Message.Flag.STOP_SENDING) {
|
210
|
-
//
|
250
|
+
// the remote has stopped reading
|
211
251
|
this.onRemoteCloseRead()
|
212
252
|
}
|
213
253
|
|
214
254
|
if (message.flag === Message.Flag.FIN_ACK) {
|
215
|
-
|
255
|
+
// remote received our FIN
|
256
|
+
this.receivedFinAck?.resolve()
|
216
257
|
}
|
217
258
|
}
|
218
|
-
|
219
|
-
// ignore data messages if we've closed the readable end already
|
220
|
-
if (this.readStatus === 'readable' || this.readStatus === 'paused') {
|
221
|
-
return message.message
|
222
|
-
}
|
223
259
|
}
|
224
260
|
|
225
261
|
private _sendFlag (flag: Message.Flag): boolean {
|