Skip to content

Commit 4db0645

Browse files
achingbrainprivacyguard
andauthoredNov 15, 2024··
fix: gracefully handle remote stream closure during ping (#2822)
Fixes #2770. This issue happens because the ping protocol does not handle the case where the remote peer closes the stream and with the introduction of `connectionMonitor` this became an issue. It opens a stream for the ping protocol, sends a ping, gets the response then closes the stream. The local peer throws an exception and triggers a yamux reset. --------- Co-authored-by: privacyguard <privacyguard@users.noreply.github.com>
1 parent eee97c7 commit 4db0645

File tree

1 file changed

+23
-2
lines changed

1 file changed

+23
-2
lines changed
 

‎packages/protocol-ping/src/ping.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { randomBytes } from '@libp2p/crypto'
2-
import { ProtocolError, TimeoutError } from '@libp2p/interface'
2+
import { ProtocolError, TimeoutError, setMaxListeners } from '@libp2p/interface'
33
import { byteStream } from 'it-byte-stream'
44
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
55
import { PROTOCOL_PREFIX, PROTOCOL_NAME, PING_LENGTH, PROTOCOL_VERSION, TIMEOUT, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS } from './constants.js'
@@ -60,10 +60,12 @@ export class PingService implements Startable, PingServiceInterface {
6060
const { stream } = data
6161
const start = Date.now()
6262
const bytes = byteStream(stream)
63+
let pinged = false
6364

6465
Promise.resolve().then(async () => {
6566
while (true) {
6667
const signal = AbortSignal.timeout(this.timeout)
68+
setMaxListeners(Infinity, signal)
6769
signal.addEventListener('abort', () => {
6870
stream?.abort(new TimeoutError('ping timeout'))
6971
})
@@ -74,15 +76,34 @@ export class PingService implements Startable, PingServiceInterface {
7476
await bytes.write(buf, {
7577
signal
7678
})
79+
80+
pinged = true
7781
}
7882
})
7983
.catch(err => {
80-
this.log.error('incoming ping from %p failed with error', data.connection.remotePeer, err)
84+
// ignore the error if we've processed at least one ping, the remote
85+
// closed the stream and we handled or are handling the close cleanly
86+
if (pinged && err.name === 'UnexpectedEOFError' && stream.readStatus !== 'ready') {
87+
return
88+
}
89+
90+
this.log.error('incoming ping from %p failed with error - %e', data.connection.remotePeer, err)
8191
stream?.abort(err)
8292
})
8393
.finally(() => {
8494
const ms = Date.now() - start
8595
this.log('incoming ping from %p complete in %dms', data.connection.remotePeer, ms)
96+
97+
const signal = AbortSignal.timeout(this.timeout)
98+
setMaxListeners(Infinity, signal)
99+
100+
stream.close({
101+
signal
102+
})
103+
.catch(err => {
104+
this.log.error('error closing ping stream from %p - %e', data.connection.remotePeer, err)
105+
stream?.abort(err)
106+
})
86107
})
87108
}
88109

0 commit comments

Comments
 (0)
Please sign in to comment.