Skip to content
This repository was archived by the owner on Aug 29, 2023. It is now read-only.

Commit 28fe750

Browse files
fix: remove abortable-iterator and close socket directly on abort (#220)
Usage of `abortable-iterator` has a non-negligible cost, see libp2p/js-libp2p#1420 so remove it and close the socket directly when the abort signal fires. Co-authored-by: achingbrain <[email protected]>
1 parent 1bfc601 commit 28fe750

File tree

4 files changed

+78
-10
lines changed

4 files changed

+78
-10
lines changed

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@
148148
"@libp2p/utils": "^3.0.2",
149149
"@multiformats/mafmt": "^11.0.3",
150150
"@multiformats/multiaddr": "^11.0.0",
151-
"abortable-iterator": "^4.0.2",
152151
"err-code": "^3.0.1",
153152
"stream-to-it": "^0.2.2"
154153
},

src/index.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class TCP implements Transport {
9494
async dial (ma: Multiaddr, options: TCPDialOptions): Promise<Connection> {
9595
options.keepAlive = options.keepAlive ?? true
9696

97+
// options.signal destroys the socket before 'connect' event
9798
const socket = await this._connect(ma, options)
9899

99100
// Avoid uncaught errors caused by unstable connections
@@ -103,14 +104,32 @@ class TCP implements Transport {
103104

104105
const maConn = toMultiaddrConnection(socket, {
105106
remoteAddr: ma,
106-
signal: options.signal,
107107
socketInactivityTimeout: this.opts.outboundSocketInactivityTimeout,
108108
socketCloseTimeout: this.opts.socketCloseTimeout,
109109
metrics: this.metrics?.dialerEvents
110110
})
111+
112+
const onAbort = () => {
113+
maConn.close().catch(err => {
114+
log.error('Error closing maConn after abort', err)
115+
})
116+
}
117+
options.signal?.addEventListener('abort', onAbort, { once: true })
118+
111119
log('new outbound connection %s', maConn.remoteAddr)
112120
const conn = await options.upgrader.upgradeOutbound(maConn)
113-
log('outbound connection upgraded %s', maConn.remoteAddr)
121+
log('outbound connection %s upgraded', maConn.remoteAddr)
122+
123+
options.signal?.removeEventListener('abort', onAbort)
124+
125+
if (options.signal?.aborted === true) {
126+
conn.close().catch(err => {
127+
log.error('Error closing conn after abort', err)
128+
})
129+
130+
throw new AbortError()
131+
}
132+
114133
return conn
115134
}
116135

src/socket-to-conn.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { abortableSource } from 'abortable-iterator'
21
import { logger } from '@libp2p/logger'
32
// @ts-expect-error no types
43
import toIterable from 'stream-to-it'
@@ -17,7 +16,6 @@ interface ToConnectionOptions {
1716
listeningAddr?: Multiaddr
1817
remoteAddr?: Multiaddr
1918
localAddr?: Multiaddr
20-
signal?: AbortSignal
2119
socketInactivityTimeout?: number
2220
socketCloseTimeout?: number
2321
metrics?: CounterGroup
@@ -99,10 +97,6 @@ export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptio
9997

10098
const maConn: MultiaddrConnection = {
10199
async sink (source) {
102-
if ((options?.signal) != null) {
103-
source = abortableSource(source, options.signal)
104-
}
105-
106100
try {
107101
await sink(source)
108102
} catch (err: any) {
@@ -119,7 +113,7 @@ export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptio
119113
socket.end()
120114
},
121115

122-
source: (options.signal != null) ? abortableSource(source, options.signal) : source,
116+
source,
123117

124118
// If the remote address was passed, use it - it may have the peer ID encapsulated
125119
remoteAddr,

test/listen-dial.spec.ts

+56
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import all from 'it-all'
88
import { mockRegistrar, mockUpgrader } from '@libp2p/interface-mocks'
99
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
1010
import type { Transport, Upgrader } from '@libp2p/interface-transport'
11+
import pDefer from 'p-defer'
12+
import type { MultiaddrConnection } from '@libp2p/interface-connection'
1113

1214
const isCI = process.env.CI
1315

@@ -20,6 +22,7 @@ describe('listen', () => {
2022
transport = tcp()()
2123
upgrader = mockUpgrader()
2224
})
25+
2326
afterEach(async () => {
2427
try {
2528
if (listener != null) {
@@ -326,4 +329,57 @@ describe('dial', () => {
326329
await conn.close()
327330
await listener.close()
328331
})
332+
333+
it('aborts during dial', async () => {
334+
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
335+
const maConnPromise = pDefer<MultiaddrConnection>()
336+
337+
// @ts-expect-error missing return value
338+
upgrader.upgradeOutbound = async (maConn) => {
339+
maConnPromise.resolve(maConn)
340+
341+
// take a long time to give us time to abort the dial
342+
await new Promise<void>((resolve) => {
343+
setTimeout(() => resolve(), 100)
344+
})
345+
}
346+
347+
const listener = transport.createListener({
348+
upgrader
349+
})
350+
await listener.listen(ma)
351+
352+
const abortController = new AbortController()
353+
354+
// abort once the upgrade process has started
355+
void maConnPromise.promise.then(() => abortController.abort())
356+
357+
await expect(transport.dial(ma, {
358+
upgrader,
359+
signal: abortController.signal
360+
})).to.eventually.be.rejected('The operation was aborted')
361+
362+
await expect(maConnPromise.promise).to.eventually.have.nested.property('timeline.close')
363+
.that.is.ok('did not gracefully close maConn')
364+
365+
await listener.close()
366+
})
367+
368+
it('aborts before dial', async () => {
369+
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
370+
const listener = transport.createListener({
371+
upgrader
372+
})
373+
await listener.listen(ma)
374+
375+
const abortController = new AbortController()
376+
abortController.abort()
377+
378+
await expect(transport.dial(ma, {
379+
upgrader,
380+
signal: abortController.signal
381+
})).to.eventually.be.rejected('The operation was aborted')
382+
383+
await listener.close()
384+
})
329385
})

0 commit comments

Comments
 (0)