@libp2p/circuit-relay-v2 3.2.24-6059227cb → 3.2.24-87bc8d4fb

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 (51) hide show
  1. package/dist/index.min.js +1 -1
  2. package/dist/index.min.js.map +4 -4
  3. package/dist/src/constants.d.ts +6 -1
  4. package/dist/src/constants.d.ts.map +1 -1
  5. package/dist/src/constants.js +6 -1
  6. package/dist/src/constants.js.map +1 -1
  7. package/dist/src/index.d.ts +7 -162
  8. package/dist/src/index.d.ts.map +1 -1
  9. package/dist/src/index.js +2 -12
  10. package/dist/src/index.js.map +1 -1
  11. package/dist/src/server/index.d.ts +45 -41
  12. package/dist/src/server/index.d.ts.map +1 -1
  13. package/dist/src/server/index.js +51 -32
  14. package/dist/src/server/index.js.map +1 -1
  15. package/dist/src/server/reservation-store.d.ts +36 -2
  16. package/dist/src/server/reservation-store.d.ts.map +1 -1
  17. package/dist/src/server/reservation-store.js.map +1 -1
  18. package/dist/src/transport/discovery.d.ts +17 -2
  19. package/dist/src/transport/discovery.d.ts.map +1 -1
  20. package/dist/src/transport/discovery.js +2 -2
  21. package/dist/src/transport/discovery.js.map +1 -1
  22. package/dist/src/transport/index.d.ts +42 -34
  23. package/dist/src/transport/index.d.ts.map +1 -1
  24. package/dist/src/transport/index.js +5 -291
  25. package/dist/src/transport/index.js.map +1 -1
  26. package/dist/src/transport/reservation-store.d.ts +37 -3
  27. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  28. package/dist/src/transport/reservation-store.js +6 -4
  29. package/dist/src/transport/reservation-store.js.map +1 -1
  30. package/dist/src/transport/transport.d.ts +54 -0
  31. package/dist/src/transport/transport.d.ts.map +1 -0
  32. package/dist/src/transport/transport.js +314 -0
  33. package/dist/src/transport/transport.js.map +1 -0
  34. package/dist/src/utils.d.ts.map +1 -1
  35. package/dist/src/utils.js +59 -36
  36. package/dist/src/utils.js.map +1 -1
  37. package/package.json +24 -19
  38. package/src/constants.ts +7 -1
  39. package/src/index.ts +8 -198
  40. package/src/server/index.ts +105 -37
  41. package/src/server/reservation-store.ts +42 -2
  42. package/src/transport/discovery.ts +22 -4
  43. package/src/transport/index.ts +46 -338
  44. package/src/transport/reservation-store.ts +46 -8
  45. package/src/transport/transport.ts +380 -0
  46. package/src/utils.ts +66 -37
  47. package/dist/src/transport/stream-to-conn.d.ts +0 -19
  48. package/dist/src/transport/stream-to-conn.d.ts.map +0 -1
  49. package/dist/src/transport/stream-to-conn.js +0 -60
  50. package/dist/src/transport/stream-to-conn.js.map +0 -1
  51. package/src/transport/stream-to-conn.ts +0 -91
@@ -0,0 +1,380 @@
1
+ import { DialError, InvalidMessageError, serviceCapabilities, serviceDependencies, start, stop, transportSymbol } from '@libp2p/interface'
2
+ import { peerFilter } from '@libp2p/peer-collections'
3
+ import { peerIdFromMultihash, peerIdFromString } from '@libp2p/peer-id'
4
+ import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn'
5
+ import { multiaddr } from '@multiformats/multiaddr'
6
+ import { Circuit } from '@multiformats/multiaddr-matcher'
7
+ import { pbStream } from 'it-protobuf-stream'
8
+ import { setMaxListeners } from 'main-event'
9
+ import * as Digest from 'multiformats/hashes/digest'
10
+ import { CustomProgressEvent } from 'progress-events'
11
+ import { CIRCUIT_PROTO_CODE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE, DEFAULT_DISCOVERY_FILTER_SIZE, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
12
+ import { StopMessage, HopMessage, Status } from '../pb/index.js'
13
+ import { CircuitListen, CircuitSearch, LimitTracker } from '../utils.js'
14
+ import { RelayDiscovery } from './discovery.js'
15
+ import { createListener } from './listener.js'
16
+ import { ReservationStore } from './reservation-store.js'
17
+ import type { CircuitRelayTransportComponents, CircuitRelayTransportInit } from './index.js'
18
+ import type { Transport, CreateListenerOptions, Listener, Upgrader, ComponentLogger, Logger, Connection, Stream, ConnectionGater, PeerId, PeerStore, OutboundConnectionUpgradeEvents, DialTransportOptions, OpenConnectionProgressEvents, IncomingStreamData } from '@libp2p/interface'
19
+ import type { AddressManager, ConnectionManager, Registrar, TransportManager } from '@libp2p/interface-internal'
20
+ import type { Multiaddr } from '@multiformats/multiaddr'
21
+ import type { ProgressEvent } from 'progress-events'
22
+
23
+ const isValidStop = (request: StopMessage): request is Required<StopMessage> => {
24
+ if (request.peer == null) {
25
+ return false
26
+ }
27
+
28
+ try {
29
+ request.peer.addrs.forEach(multiaddr)
30
+ } catch {
31
+ return false
32
+ }
33
+
34
+ return true
35
+ }
36
+
37
+ const defaults = {
38
+ maxInboundStopStreams: MAX_CONNECTIONS,
39
+ maxOutboundStopStreams: MAX_CONNECTIONS,
40
+ stopTimeout: 30000
41
+ }
42
+
43
+ export type CircuitRelayDialEvents =
44
+ OutboundConnectionUpgradeEvents |
45
+ OpenConnectionProgressEvents |
46
+ ProgressEvent<'circuit-relay:open-connection'> |
47
+ ProgressEvent<'circuit-relay:reuse-connection'> |
48
+ ProgressEvent<'circuit-relay:open-hop-stream'> |
49
+ ProgressEvent<'circuit-relay:write-connect-message'> |
50
+ ProgressEvent<'circuit-relay:read-connect-response'>
51
+
52
+ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents> {
53
+ private readonly discovery?: RelayDiscovery
54
+ private readonly registrar: Registrar
55
+ private readonly peerStore: PeerStore
56
+ private readonly connectionManager: ConnectionManager
57
+ private readonly transportManager: TransportManager
58
+ private readonly peerId: PeerId
59
+ private readonly upgrader: Upgrader
60
+ private readonly addressManager: AddressManager
61
+ private readonly connectionGater: ConnectionGater
62
+ public readonly reservationStore: ReservationStore
63
+ private readonly logger: ComponentLogger
64
+ private readonly maxInboundStopStreams: number
65
+ private readonly maxOutboundStopStreams?: number
66
+ private started: boolean
67
+ private readonly log: Logger
68
+ private shutdownController: AbortController
69
+
70
+ constructor (components: CircuitRelayTransportComponents, init: CircuitRelayTransportInit = {}) {
71
+ this.log = components.logger.forComponent('libp2p:circuit-relay:transport')
72
+ this.registrar = components.registrar
73
+ this.peerStore = components.peerStore
74
+ this.connectionManager = components.connectionManager
75
+ this.transportManager = components.transportManager
76
+ this.logger = components.logger
77
+ this.peerId = components.peerId
78
+ this.upgrader = components.upgrader
79
+ this.addressManager = components.addressManager
80
+ this.connectionGater = components.connectionGater
81
+ this.maxInboundStopStreams = init.maxInboundStopStreams ?? defaults.maxInboundStopStreams
82
+ this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
83
+ this.shutdownController = new AbortController()
84
+
85
+ this.discovery = new RelayDiscovery(components, {
86
+ filter: init.discoveryFilter ?? peerFilter(DEFAULT_DISCOVERY_FILTER_SIZE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE)
87
+ })
88
+ this.discovery.addEventListener('relay:discover', (evt) => {
89
+ this.reservationStore.addRelay(evt.detail, 'discovered')
90
+ .catch(err => {
91
+ if (err.name !== 'HadEnoughRelaysError' && err.name !== 'RelayQueueFullError') {
92
+ this.log.error('could not add discovered relay %p', evt.detail, err)
93
+ }
94
+ })
95
+ })
96
+ this.reservationStore = new ReservationStore(components, init)
97
+ this.reservationStore.addEventListener('relay:not-enough-relays', () => {
98
+ this.discovery?.startDiscovery()
99
+ })
100
+ this.reservationStore.addEventListener('relay:found-enough-relays', () => {
101
+ this.discovery?.stopDiscovery()
102
+ })
103
+
104
+ this.started = false
105
+ }
106
+
107
+ readonly [Symbol.toStringTag] = '@libp2p/circuit-relay-v2-transport'
108
+
109
+ readonly [serviceCapabilities]: string[] = [
110
+ '@libp2p/transport',
111
+ '@libp2p/circuit-relay-v2-transport'
112
+ ]
113
+
114
+ get [serviceDependencies] (): string[] {
115
+ // we only need identify if discovery is enabled
116
+ if (this.discovery != null) {
117
+ return [
118
+ '@libp2p/identify'
119
+ ]
120
+ }
121
+
122
+ return []
123
+ }
124
+
125
+ readonly [transportSymbol] = true
126
+
127
+ isStarted (): boolean {
128
+ return this.started
129
+ }
130
+
131
+ async start (): Promise<void> {
132
+ this.shutdownController = new AbortController()
133
+ setMaxListeners(Infinity, this.shutdownController.signal)
134
+
135
+ await this.registrar.handle(RELAY_V2_STOP_CODEC, (data) => {
136
+ const signal = this.upgrader.createInboundAbortSignal(this.shutdownController.signal)
137
+
138
+ void this.onStop(data, signal)
139
+ .catch(err => {
140
+ this.log.error('error while handling STOP protocol', err)
141
+ data.stream.abort(err)
142
+ })
143
+ .finally(() => {
144
+ signal.clear()
145
+ })
146
+ }, {
147
+ maxInboundStreams: this.maxInboundStopStreams,
148
+ maxOutboundStreams: this.maxOutboundStopStreams,
149
+ runOnLimitedConnection: true
150
+ })
151
+
152
+ await start(this.discovery, this.reservationStore)
153
+
154
+ this.started = true
155
+ }
156
+
157
+ async stop (): Promise<void> {
158
+ this.shutdownController.abort()
159
+ await stop(this.discovery, this.reservationStore)
160
+ await this.registrar.unhandle(RELAY_V2_STOP_CODEC)
161
+
162
+ this.started = false
163
+ }
164
+
165
+ /**
166
+ * Dial a peer over a relay
167
+ */
168
+ async dial (ma: Multiaddr, options: DialTransportOptions<CircuitRelayDialEvents>): Promise<Connection> {
169
+ if (ma.protoCodes().filter(code => code === CIRCUIT_PROTO_CODE).length !== 1) {
170
+ const errMsg = 'Invalid circuit relay address'
171
+ this.log.error(errMsg, ma)
172
+ throw new DialError(errMsg)
173
+ }
174
+
175
+ // Check the multiaddr to see if it contains a relay and a destination peer
176
+ const addrs = ma.toString().split('/p2p-circuit')
177
+ const relayAddr = multiaddr(addrs[0])
178
+ const destinationAddr = multiaddr(addrs[addrs.length - 1])
179
+ const relayId = relayAddr.getPeerId()
180
+ const destinationId = destinationAddr.getPeerId()
181
+
182
+ if (relayId == null || destinationId == null) {
183
+ const errMsg = `ircuit relay dial to ${ma.toString()} failed as address did not have both relay and destination PeerIDs`
184
+ this.log.error(`c${errMsg}`)
185
+ throw new DialError(`C${errMsg}`)
186
+ }
187
+
188
+ const relayPeer = peerIdFromString(relayId)
189
+ const destinationPeer = peerIdFromString(destinationId)
190
+
191
+ const relayConnections = this.connectionManager.getConnections(relayPeer)
192
+ let relayConnection = relayConnections[0]
193
+
194
+ if (relayConnection == null) {
195
+ await this.peerStore.merge(relayPeer, {
196
+ multiaddrs: [relayAddr]
197
+ })
198
+
199
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:open-connection'))
200
+ relayConnection = await this.connectionManager.openConnection(relayPeer, options)
201
+ } else {
202
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:reuse-connection'))
203
+ }
204
+
205
+ let stream: Stream | undefined
206
+
207
+ try {
208
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:open-hop-stream'))
209
+ stream = await relayConnection.newStream(RELAY_V2_HOP_CODEC, options)
210
+
211
+ const pbstr = pbStream(stream)
212
+ const hopstr = pbstr.pb(HopMessage)
213
+
214
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:write-connect-message'))
215
+ await hopstr.write({
216
+ type: HopMessage.Type.CONNECT,
217
+ peer: {
218
+ id: destinationPeer.toMultihash().bytes,
219
+ addrs: [multiaddr(destinationAddr).bytes]
220
+ }
221
+ }, options)
222
+
223
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:read-connect-response'))
224
+ const status = await hopstr.read(options)
225
+
226
+ if (status.status !== Status.OK) {
227
+ throw new InvalidMessageError(`failed to connect via relay with status ${status?.status?.toString() ?? 'undefined'}`)
228
+ }
229
+
230
+ const limits = new LimitTracker(status.limit)
231
+
232
+ const maConn = streamToMaConnection({
233
+ stream: pbstr.unwrap(),
234
+ remoteAddr: ma,
235
+ localAddr: relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toString()}`),
236
+ log: this.log,
237
+ onDataRead: limits.onData,
238
+ onDataWrite: limits.onData
239
+ })
240
+
241
+ const conn = await this.upgrader.upgradeOutbound(maConn, {
242
+ ...options,
243
+ limits: limits.getLimits()
244
+ })
245
+
246
+ conn.log('outbound relayed connection established to %p with limits %o, over connection %s', conn.remotePeer, status.limit ?? 'none', relayConnection.id)
247
+
248
+ return conn
249
+ } catch (err: any) {
250
+ this.log.error('circuit relay dial to destination %p via relay %p failed', destinationPeer, relayPeer, err)
251
+ stream?.abort(err)
252
+
253
+ throw err
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Create a listener
259
+ */
260
+ createListener (options: CreateListenerOptions): Listener {
261
+ return createListener({
262
+ peerId: this.peerId,
263
+ connectionManager: this.connectionManager,
264
+ addressManager: this.addressManager,
265
+ reservationStore: this.reservationStore,
266
+ logger: this.logger
267
+ })
268
+ }
269
+
270
+ /**
271
+ * Filter check for all Multiaddrs that this transport can listen on
272
+ */
273
+ listenFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
274
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
275
+
276
+ return multiaddrs.filter((ma) => {
277
+ return CircuitListen.exactMatch(ma) || CircuitSearch.exactMatch(ma)
278
+ })
279
+ }
280
+
281
+ /**
282
+ * Filter check for all Multiaddrs that this transport can dial
283
+ */
284
+ dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
285
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
286
+
287
+ return multiaddrs.filter((ma) => {
288
+ return Circuit.exactMatch(ma)
289
+ })
290
+ }
291
+
292
+ /**
293
+ * An incoming STOP request means a remote peer wants to dial us via a relay
294
+ */
295
+ async onStop ({ connection, stream }: IncomingStreamData, signal: AbortSignal): Promise<void> {
296
+ if (!this.reservationStore.hasReservation(connection.remotePeer)) {
297
+ try {
298
+ this.log('dialed via relay we did not have a reservation on, start listening on that relay address')
299
+ await this.transportManager.listen([connection.remoteAddr.encapsulate('/p2p-circuit')])
300
+ } catch (err: any) {
301
+ // failed to refresh our hitherto unknown relay reservation but allow the connection attempt anyway
302
+ this.log.error('failed to listen on a relay peer we were dialed via but did not have a reservation on', err)
303
+ }
304
+ }
305
+
306
+ const pbstr = pbStream(stream).pb(StopMessage)
307
+ const request = await pbstr.read({
308
+ signal
309
+ })
310
+
311
+ this.log('new circuit relay v2 stop stream from %p with type %s', connection.remotePeer, request.type)
312
+
313
+ if (request?.type === undefined) {
314
+ this.log.error('type was missing from circuit v2 stop protocol request from %s', connection.remotePeer)
315
+ await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
316
+ signal
317
+ })
318
+ await stream.close()
319
+ return
320
+ }
321
+
322
+ // Validate the STOP request has the required input
323
+ if (request.type !== StopMessage.Type.CONNECT) {
324
+ this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
325
+ await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }, {
326
+ signal
327
+ })
328
+ await stream.close()
329
+ return
330
+ }
331
+
332
+ if (!isValidStop(request)) {
333
+ this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
334
+ await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
335
+ signal
336
+ })
337
+ await stream.close({
338
+ signal
339
+ })
340
+ return
341
+ }
342
+
343
+ const remotePeerId = peerIdFromMultihash(Digest.decode(request.peer.id))
344
+
345
+ if ((await this.connectionGater.denyInboundRelayedConnection?.(connection.remotePeer, remotePeerId)) === true) {
346
+ this.log.error('connection gater denied inbound relayed connection from %p', connection.remotePeer)
347
+ await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, {
348
+ signal
349
+ })
350
+ await stream.close({
351
+ signal
352
+ })
353
+ return
354
+ }
355
+
356
+ this.log.trace('sending success response to %p', connection.remotePeer)
357
+ await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.OK }, {
358
+ signal
359
+ })
360
+
361
+ const limits = new LimitTracker(request.limit)
362
+ const remoteAddr = connection.remoteAddr.encapsulate(`/p2p-circuit/p2p/${remotePeerId.toString()}`)
363
+ const localAddr = this.addressManager.getAddresses()[0]
364
+ const maConn = streamToMaConnection({
365
+ stream: pbstr.unwrap().unwrap(),
366
+ remoteAddr,
367
+ localAddr,
368
+ log: this.log,
369
+ onDataRead: limits.onData,
370
+ onDataWrite: limits.onData
371
+ })
372
+
373
+ await this.upgrader.upgradeInbound(maConn, {
374
+ limits: limits.getLimits(),
375
+ signal
376
+ })
377
+
378
+ maConn.log('inbound relayed connection established to %p with limits %o, over connection %s', remotePeerId, request.limit ?? 'none', connection.id)
379
+ }
380
+ }
package/src/utils.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { setMaxListeners } from '@libp2p/interface'
2
- import { pipe } from '@libp2p/utils'
3
1
  import { CODE_P2P_CIRCUIT } from '@multiformats/multiaddr'
4
2
  import { P2P } from '@multiformats/multiaddr-matcher'
5
3
  import { fmt, code, and } from '@multiformats/multiaddr-matcher/utils'
@@ -9,21 +7,35 @@ import { sha256 } from 'multiformats/hashes/sha2'
9
7
  import { DurationLimitError, TransferLimitError } from './errors.js'
10
8
  import type { RelayReservation } from './index.js'
11
9
  import type { Limit } from './pb/index.js'
12
- import type { ConnectionLimits, LoggerOptions, Stream, MessageStream, StreamMessageEvent } from '@libp2p/interface'
10
+ import type { ConnectionLimits, LoggerOptions, Stream } from '@libp2p/interface'
11
+ import type { Source } from 'it-stream-types'
13
12
  import type { Uint8ArrayList } from 'uint8arraylist'
14
13
 
15
- function countStreamBytes (source: MessageStream, limit: { remaining: bigint }, options: LoggerOptions): void {
14
+ async function * countStreamBytes (source: Source<Uint8Array | Uint8ArrayList>, limit: { remaining: bigint }, options: LoggerOptions): AsyncGenerator<Uint8Array | Uint8ArrayList, void, unknown> {
16
15
  const limitBytes = limit.remaining
17
16
 
18
- const abortIfStreamByteLimitExceeded = (evt: StreamMessageEvent): void => {
19
- const len = BigInt(evt.data.byteLength)
20
- limit.remaining -= len
17
+ for await (const buf of source) {
18
+ const len = BigInt(buf.byteLength)
19
+
20
+ if ((limit.remaining - len) < 0) {
21
+ // this is a safe downcast since len is guarantee to be in the range for a number
22
+ const remaining = Number(limit.remaining)
23
+ limit.remaining = 0n
21
24
 
22
- if (limit.remaining < 0) {
23
- source.abort(new TransferLimitError(`data limit of ${limitBytes} bytes exceeded`))
25
+ try {
26
+ if (remaining !== 0) {
27
+ yield buf.subarray(0, remaining)
28
+ }
29
+ } catch (err: any) {
30
+ options.log.error(err)
31
+ }
32
+
33
+ throw new TransferLimitError(`data limit of ${limitBytes} bytes exceeded`)
24
34
  }
35
+
36
+ limit.remaining -= len
37
+ yield buf
25
38
  }
26
- source.addEventListener('message', abortIfStreamByteLimitExceeded)
27
39
  }
28
40
 
29
41
  export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: AbortSignal, reservation: RelayReservation, options: LoggerOptions): void {
@@ -37,13 +49,13 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort
37
49
 
38
50
  if (reservation.limit?.duration != null) {
39
51
  options.log('limiting relayed connection duration to %dms', reservation.limit.duration)
40
- const durationSignal = AbortSignal.timeout(reservation.limit.duration)
41
- setMaxListeners(Infinity, durationSignal)
42
- signals.push(durationSignal)
52
+ signals.push(AbortSignal.timeout(reservation.limit.duration))
43
53
  }
44
54
 
45
55
  const signal = anySignal(signals)
46
- setMaxListeners(Infinity, signal)
56
+
57
+ let srcDstFinished = false
58
+ let dstSrcFinished = false
47
59
 
48
60
  let dataLimit: { remaining: bigint } | undefined
49
61
 
@@ -53,34 +65,51 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort
53
65
  }
54
66
  }
55
67
 
56
- const onAbort = (): void => {
57
- let err: Error
68
+ queueMicrotask(() => {
69
+ const onAbort = (): void => {
70
+ options.log('relayed connection reached time limit')
71
+ dst.abort(new DurationLimitError(`duration limit of ${reservation.limit?.duration} ms exceeded`))
72
+ }
58
73
 
59
- if (abortSignal.aborted) {
60
- err = abortSignal.reason
61
- } else {
62
- err = new DurationLimitError(`duration limit of ${reservation.limit?.duration} ms exceeded`)
74
+ signal.addEventListener('abort', onAbort, { once: true })
75
+
76
+ void dst.sink(dataLimit == null ? src.source : countStreamBytes(src.source, dataLimit, options))
77
+ .catch(err => {
78
+ options.log.error('error while relaying streams src -> dst', err)
79
+ abortStreams(err)
80
+ })
81
+ .finally(() => {
82
+ srcDstFinished = true
83
+
84
+ if (dstSrcFinished) {
85
+ signal.removeEventListener('abort', onAbort)
86
+ signal.clear()
87
+ }
88
+ })
89
+ })
90
+
91
+ queueMicrotask(() => {
92
+ const onAbort = (): void => {
93
+ options.log('relayed connection reached time limit')
94
+ src.abort(new DurationLimitError(`duration limit of ${reservation.limit?.duration} ms exceeded`))
63
95
  }
64
96
 
65
- abortStreams(err)
66
- }
67
- signal.addEventListener('abort', onAbort, { once: true })
97
+ signal.addEventListener('abort', onAbort, { once: true })
68
98
 
69
- if (dataLimit != null) {
70
- countStreamBytes(dst, dataLimit, options)
71
- countStreamBytes(src, dataLimit, options)
72
- }
99
+ void src.sink(dataLimit == null ? dst.source : countStreamBytes(dst.source, dataLimit, options))
100
+ .catch(err => {
101
+ options.log.error('error while relaying streams dst -> src', err)
102
+ abortStreams(err)
103
+ })
104
+ .finally(() => {
105
+ dstSrcFinished = true
73
106
 
74
- // join the streams together
75
- pipe(
76
- src, dst, src
77
- )
78
- .catch(err => {
79
- abortStreams(err)
80
- })
81
- .finally(() => {
82
- signal.clear()
83
- })
107
+ if (srcDstFinished) {
108
+ signal.removeEventListener('abort', onAbort)
109
+ signal.clear()
110
+ }
111
+ })
112
+ })
84
113
  }
85
114
 
86
115
  /**
@@ -1,19 +0,0 @@
1
- import { Uint8ArrayList } from 'uint8arraylist';
2
- import type { MultiaddrConnection, Stream } from '@libp2p/interface';
3
- import type { AbstractMultiaddrConnectionInit } from '@libp2p/utils';
4
- export interface StreamMultiaddrConnectionInit extends Omit<AbstractMultiaddrConnectionInit, 'direction'> {
5
- stream: Stream;
6
- /**
7
- * A callback invoked when data is read from the stream
8
- */
9
- onDataRead?(buf: Uint8ArrayList | Uint8Array): void;
10
- /**
11
- * A callback invoked when data is written to the stream
12
- */
13
- onDataWrite?(buf: Uint8ArrayList | Uint8Array): void;
14
- }
15
- /**
16
- * Convert a Stream into a MultiaddrConnection.
17
- */
18
- export declare function streamToMaConnection(init: StreamMultiaddrConnectionInit): MultiaddrConnection;
19
- //# sourceMappingURL=stream-to-conn.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stream-to-conn.d.ts","sourceRoot":"","sources":["../../../src/transport/stream-to-conn.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAgB,mBAAmB,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAClF,OAAO,KAAK,EAAE,+BAA+B,EAAc,MAAM,eAAe,CAAA;AAEhF,MAAM,WAAW,6BAA8B,SAAQ,IAAI,CAAC,+BAA+B,EAAE,WAAW,CAAC;IACvG,MAAM,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,CAAC,GAAG,EAAE,cAAc,GAAG,UAAU,GAAG,IAAI,CAAA;IAEnD;;OAEG;IACH,WAAW,CAAC,CAAC,GAAG,EAAE,cAAc,GAAG,UAAU,GAAG,IAAI,CAAA;CACrD;AAoED;;GAEG;AACH,wBAAgB,oBAAoB,CAAE,IAAI,EAAE,6BAA6B,GAAG,mBAAmB,CAE9F"}
@@ -1,60 +0,0 @@
1
- import { AbstractMultiaddrConnection } from '@libp2p/utils';
2
- import { Uint8ArrayList } from 'uint8arraylist';
3
- class StreamMultiaddrConnection extends AbstractMultiaddrConnection {
4
- stream;
5
- init;
6
- constructor(init) {
7
- super({
8
- ...init,
9
- direction: init.stream.direction
10
- });
11
- this.init = init;
12
- this.stream = init.stream;
13
- this.stream.addEventListener('close', (evt) => {
14
- this.onTransportClosed(evt.error);
15
- });
16
- this.stream.addEventListener('remoteCloseWrite', (evt) => {
17
- this.onRemoteCloseWrite();
18
- // close our end when the remote closes
19
- this.close()
20
- .catch(err => {
21
- this.abort(err);
22
- });
23
- });
24
- // count incoming bytes
25
- this.stream.addEventListener('message', (evt) => {
26
- init.onDataRead?.(evt.data);
27
- this.onData(evt.data);
28
- });
29
- // forward drain events
30
- this.stream.addEventListener('drain', () => {
31
- this.safeDispatchEvent('drain');
32
- });
33
- }
34
- sendData(data) {
35
- this.init.onDataWrite?.(data);
36
- return {
37
- sentBytes: data.byteLength,
38
- canSendMore: this.stream.send(data)
39
- };
40
- }
41
- async sendClose(options) {
42
- await this.stream.close(options);
43
- }
44
- sendReset() {
45
- this.stream.abort(new Error('An error occurred'));
46
- }
47
- sendPause() {
48
- this.stream.pause();
49
- }
50
- sendResume() {
51
- this.stream.resume();
52
- }
53
- }
54
- /**
55
- * Convert a Stream into a MultiaddrConnection.
56
- */
57
- export function streamToMaConnection(init) {
58
- return new StreamMultiaddrConnection(init);
59
- }
60
- //# sourceMappingURL=stream-to-conn.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stream-to-conn.js","sourceRoot":"","sources":["../../../src/transport/stream-to-conn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAkB/C,MAAM,yBAA0B,SAAQ,2BAA2B;IACzD,MAAM,CAAQ;IACd,IAAI,CAA+B;IAE3C,YAAa,IAAmC;QAC9C,KAAK,CAAC;YACJ,GAAG,IAAI;YACP,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;SAEjC,CAAC,CAAA;QAEF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QAEzB,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC5C,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE;YACvD,IAAI,CAAC,kBAAkB,EAAE,CAAA;YAEzB,uCAAuC;YACvC,IAAI,CAAC,KAAK,EAAE;iBACT,KAAK,CAAC,GAAG,CAAC,EAAE;gBACX,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YACjB,CAAC,CAAC,CAAA;QACN,CAAC,CAAC,CAAA;QAEF,uBAAuB;QACvB,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YAC9C,IAAI,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAC3B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACvB,CAAC,CAAC,CAAA;QAEF,uBAAuB;QACvB,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YACzC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACjC,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,QAAQ,CAAE,IAAoB;QAC5B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAA;QAE7B,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;SACpC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAE,OAAsB;QACrC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClC,CAAC;IAED,SAAS;QACP,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAA;IACnD,CAAC;IAED,SAAS;QACP,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAE,IAAmC;IACvE,OAAO,IAAI,yBAAyB,CAAC,IAAI,CAAC,CAAA;AAC5C,CAAC"}