1
1
import { randomBytes } from '@libp2p/crypto'
2
- import { ProtocolError , serviceCapabilities } from '@libp2p/interface'
3
- import { byteStream } from '@libp2p/utils'
4
- import { setMaxListeners } from 'main-event'
2
+ import { ProtocolError , serviceCapabilities , setMaxListeners , TimeoutError } from '@libp2p/interface'
3
+ import { pEvent } from 'p-event'
4
+ import { raceSignal } from 'race-signal'
5
+ import { Uint8ArrayList } from 'uint8arraylist'
5
6
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
6
7
import { PROTOCOL_PREFIX , PROTOCOL_NAME , PING_LENGTH , PROTOCOL_VERSION , TIMEOUT , MAX_INBOUND_STREAMS , MAX_OUTBOUND_STREAMS } from './constants.js'
7
8
import type { PingComponents , PingInit , Ping as PingInterface } from './index.js'
@@ -59,25 +60,32 @@ export class Ping implements Startable, PingInterface {
59
60
async handlePing ( stream : Stream , connection : Connection ) : Promise < void > {
60
61
const log = stream . log . newScope ( 'ping' )
61
62
log . trace ( 'ping from %p' , connection . remotePeer )
62
-
63
63
const signal = AbortSignal . timeout ( this . timeout )
64
64
setMaxListeners ( Infinity , signal )
65
-
65
+ signal . addEventListener ( 'abort' , ( ) => {
66
+ stream . abort ( new TimeoutError ( 'Ping timed out' ) )
67
+ } )
66
68
const start = Date . now ( )
67
- const bytes = byteStream ( stream )
68
69
69
- while ( stream . readStatus === 'readable' ) {
70
- const buf = await bytes . read ( {
71
- bytes : PING_LENGTH ,
72
- signal
73
- } )
74
- await bytes . write ( buf , {
75
- signal
76
- } )
70
+ for await ( const buf of stream ) {
71
+ if ( stream . status !== 'open' ) {
72
+ log ( 'stream status changed to %s' , stream . status )
73
+ break
74
+ }
77
75
78
- log ( 'ping from %p complete in %dms' , connection . remotePeer , Date . now ( ) - start )
76
+ if ( ! stream . send ( buf ) ) {
77
+ log ( 'waiting for stream to drain' )
78
+ await pEvent ( stream , 'drain' , {
79
+ rejectionEvents : [
80
+ 'close'
81
+ ]
82
+ } )
83
+ log ( 'stream drained' )
84
+ }
79
85
}
80
86
87
+ log ( 'ping from %p complete in %dms' , connection . remotePeer , Date . now ( ) - start )
88
+
81
89
await stream . close ( {
82
90
signal
83
91
} )
@@ -87,50 +95,38 @@ export class Ping implements Startable, PingInterface {
87
95
* Ping a given peer and wait for its response, getting the operation latency.
88
96
*/
89
97
async ping ( peer : PeerId | Multiaddr | Multiaddr [ ] , options : AbortOptions = { } ) : Promise < number > {
90
- const start = Date . now ( )
91
98
const data = randomBytes ( PING_LENGTH )
92
- const connection = await this . components . connectionManager . openConnection ( peer , options )
93
- const log = connection . log . newScope ( 'ping' )
94
- let stream : Stream | undefined
95
-
96
- if ( options . signal == null ) {
97
- const signal = AbortSignal . timeout ( this . timeout )
98
-
99
- options = {
100
- ...options ,
101
- signal
102
- }
103
- }
99
+ const stream = await this . components . connectionManager . openStream ( peer , this . protocol , {
100
+ runOnLimitedConnection : this . runOnLimitedConnection ,
101
+ ...options
102
+ } )
103
+ const log = stream . log . newScope ( 'ping' )
104
104
105
105
try {
106
- stream = await connection . newStream ( this . protocol , {
107
- ...options ,
108
- runOnLimitedConnection : this . runOnLimitedConnection
106
+ const start = Date . now ( )
107
+ const finished = Promise . withResolvers < number > ( )
108
+ const received = new Uint8ArrayList ( )
109
+
110
+ stream . addEventListener ( 'message' , ( evt ) => {
111
+ received . append ( evt . data )
112
+
113
+ if ( received . byteLength === PING_LENGTH ) {
114
+ const rtt = Date . now ( ) - start
115
+
116
+ if ( ! uint8ArrayEquals ( data , received . subarray ( ) ) ) {
117
+ finished . reject ( new ProtocolError ( `Received wrong ping ack after ${ rtt } ms` ) )
118
+ } else {
119
+ finished . resolve ( rtt )
120
+ }
121
+ }
109
122
} )
110
123
111
- const bytes = byteStream ( stream )
112
-
113
- const [ , result ] = await Promise . all ( [
114
- bytes . write ( data , options ) ,
115
- bytes . read ( {
116
- ...options ,
117
- bytes : PING_LENGTH
118
- } )
119
- ] )
120
-
121
- const ms = Date . now ( ) - start
122
-
123
- stream . close ( )
124
-
125
- if ( ! uint8ArrayEquals ( data , result . subarray ( ) ) ) {
126
- throw new ProtocolError ( `Received wrong ping ack after ${ ms } ms` )
127
- }
128
-
129
- log ( 'ping %p complete in %dms' , connection . remotePeer , ms )
124
+ stream . send ( data )
125
+ await stream . close ( options )
130
126
131
- return ms
127
+ return await raceSignal ( finished . promise , options . signal )
132
128
} catch ( err : any ) {
133
- log . error ( 'error while pinging %p ' , connection . remotePeer , err )
129
+ log . error ( 'error while pinging %o - %e ' , peer , err )
134
130
135
131
stream ?. abort ( err )
136
132
0 commit comments