Skip to content

Commit cd31be8

Browse files
committed
feat: reconfigurable pingIntervalMs. closes #4
1 parent a4cf5f6 commit cd31be8

File tree

2 files changed

+59
-10
lines changed

2 files changed

+59
-10
lines changed

lib/client.js

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class RPCClient extends EventEmitter {
8484
}
8585

8686
reconfigure(options) {
87-
const newOpts = Object.assign({}, this._options, options);
87+
const newOpts = Object.assign(this._options, options);
8888

8989
if (!newOpts.identity) {
9090
throw Error(`'identity' is required`);
@@ -119,7 +119,9 @@ class RPCClient extends EventEmitter {
119119
this._callQueue.setConcurrency(newOpts.callConcurrency);
120120
this._backoffStrategy = new ExponentialStrategy(newOpts.backoff);
121121

122-
this._options = newOpts;
122+
if ('pingIntervalMs' in options) {
123+
this._keepAlive();
124+
}
123125
}
124126

125127
/**
@@ -348,8 +350,7 @@ class RPCClient extends EventEmitter {
348350
this.emit('ping', {rtt});
349351
});
350352

351-
this._keepAliveAbortController = new AbortController();
352-
this._keepAlive(this._keepAliveAbortController.signal);
353+
this._keepAlive();
353354

354355
process.nextTick(() => {
355356
if (leadMsgBuffer) {
@@ -494,26 +495,42 @@ class RPCClient extends EventEmitter {
494495
return this._connectPromise;
495496
}
496497

497-
async _keepAlive(signal) {
498+
async _keepAlive() {
499+
// abort any previously running keepAlive
500+
this._keepAliveAbortController?.abort();
501+
498502
try {
503+
if (this._state !== OPEN) {
504+
// don't start pinging if connection not open
505+
return;
506+
}
507+
499508
if (!this._options.pingIntervalMs || this._options.pingIntervalMs <= 0 || this._options.pingIntervalMs > 2147483647) {
500-
// don't ping
509+
// don't ping for unusuable intervals
501510
return;
502511
}
512+
513+
// setup new abort controller
514+
this._keepAliveAbortController = new AbortController();
503515

504-
for await (const _ of setInterval(this._options.pingIntervalMs, 1, {signal})) {
516+
while (true) {
517+
await setTimeout(this._options.pingIntervalMs, undefined, {signal: this._keepAliveAbortController.signal});
518+
505519
if (this._state !== OPEN) {
506-
throw Error("Cannot ping while connection not open");
520+
// keepalive no longer required
521+
break;
507522
}
523+
508524
if (this._pendingPingResponse) {
509525
// we didn't get a response to our last ping
510-
this._ws.terminate();
511-
return;
526+
throw Error("Ping timeout");
512527
}
528+
513529
this._lastPingTime = Date.now();
514530
this._pendingPingResponse = true;
515531
this._ws.ping();
516532
}
533+
517534
} catch (err) {
518535
if (err.name !== 'AbortError') {
519536
// throws on ws.ping() error

test/client.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,38 @@ describe('RPCClient', function(){
22472247

22482248
});
22492249

2250+
it("should be able to adjust ping interval", async () => {
2251+
2252+
const {endpoint, close, server} = await createServer();
2253+
const cli = new RPCClient({
2254+
endpoint,
2255+
identity: 'X',
2256+
pingIntervalMs: 30,
2257+
});
2258+
2259+
try {
2260+
2261+
await cli.connect();
2262+
2263+
const t1 = Date.now();
2264+
await once(cli, 'ping');
2265+
const r1 = Date.now() - t1;
2266+
2267+
cli.reconfigure({pingIntervalMs: 10});
2268+
2269+
const t2 = Date.now();
2270+
await once(cli, 'ping');
2271+
const r2 = Date.now() - t2;
2272+
2273+
assert.ok(r1 > 20);
2274+
assert.ok(r2 < 20);
2275+
2276+
} finally {
2277+
await cli.close();
2278+
close();
2279+
}
2280+
2281+
});
22502282

22512283
});
22522284

0 commit comments

Comments
 (0)