Skip to content

Commit d542dcb

Browse files
committed
feat: added keepalive to connections
* Added a `keepAliveDelay` parameter to client, server and connections. * Connections will send a ping frame after each interval, this should reset the timeout for both sides, given that the ping is received and responded to. * Added tests for this feature. * Fixes #4 [ci skip]
1 parent e325865 commit d542dcb

File tree

7 files changed

+362
-3
lines changed

7 files changed

+362
-3
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ export class Connection {
269269
* https://stackoverflow.com/q/50343130/582917
270270
*/
271271
pathStats(): Array<PathStats>
272+
sendAckEliciting(): void
272273
}
273274
export class StreamIter {
274275
[Symbol.iterator](): Iterator<number, void, void>

src/QUICClient.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class QUICClient extends EventTarget {
5757
codeToReason,
5858
maxReadableStreamBytes,
5959
maxWritableStreamBytes,
60+
keepaliveIntervalTime,
6061
logger = new Logger(`${this.name}`),
6162
config = {},
6263
}: {
@@ -74,6 +75,7 @@ class QUICClient extends EventTarget {
7475
codeToReason?: StreamCodeToReason;
7576
maxReadableStreamBytes?: number;
7677
maxWritableStreamBytes?: number;
78+
keepaliveIntervalTime?: number;
7779
logger?: Logger;
7880
config?: Partial<QUICConfig>;
7981
}) {
@@ -199,9 +201,9 @@ class QUICClient extends EventTarget {
199201
socket.removeEventListener('error', handleQUICSocketError);
200202
// Remove the temporary connection error handler
201203
connection.removeEventListener('error', handleConnectionError);
202-
204+
// Setting up keep alive
205+
connection.setKeepAlive(keepaliveIntervalTime);
203206
// Now we create the client
204-
205207
const client = new QUICClient({
206208
crypto,
207209
socket,

src/QUICConnection.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class QUICConnection extends EventTarget {
6161
protected logger: Logger;
6262
protected socket: QUICSocket;
6363
protected timer?: ReturnType<typeof setTimeout>;
64+
protected keepAliveInterval?: ReturnType<typeof setInterval>;
6465
public readonly closedP: Promise<void>;
6566
protected resolveCloseP?: () => void;
6667

@@ -330,6 +331,11 @@ class QUICConnection extends EventTarget {
330331
force?: boolean;
331332
} = {}) {
332333
this.logger.info(`Destroy ${this.constructor.name}`);
334+
// Clean up keep alive
335+
if (this.keepAliveInterval != null) {
336+
clearTimeout(this.keepAliveInterval);
337+
delete this.keepAliveInterval;
338+
}
333339
// Handle destruction concurrently
334340
const destroyProms: Array<Promise<void>> = [];
335341
for (const stream of this.streamMap.values()) {
@@ -694,6 +700,27 @@ class QUICConnection extends EventTarget {
694700
});
695701
}
696702

703+
/**
704+
* Used to update or disable the keep alive interval.
705+
* Calling this will reset the delay before the next keep alive.
706+
*/
707+
@ready(new errors.ErrorQUICConnectionDestroyed())
708+
public setKeepAlive(intervalDelay?: number) {
709+
// Clearing timeout prior to update
710+
if (this.keepAliveInterval != null) {
711+
clearTimeout(this.keepAliveInterval);
712+
delete this.keepAliveInterval;
713+
}
714+
// Setting up keep alive interval
715+
if (intervalDelay != null) {
716+
this.keepAliveInterval = setInterval(async () => {
717+
// Trigger an ping frame and send
718+
this.conn.sendAckEliciting();
719+
await this.send();
720+
}, intervalDelay);
721+
}
722+
}
723+
697724
// Timeout handling, these methods handle time keeping for quiche.
698725
// Quiche will request an amount of time, We then call `onTimeout()` after that time has passed.
699726
protected deadline: number = 0;

src/QUICServer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class QUICServer extends EventTarget {
4949
protected codeToReason: StreamCodeToReason | undefined;
5050
protected maxReadableStreamBytes?: number | undefined;
5151
protected maxWritableStreamBytes?: number | undefined;
52+
protected keepaliveIntervalTime?: number | undefined;
5253
protected connectionMap: QUICConnectionMap;
5354

5455
/**
@@ -75,6 +76,7 @@ class QUICServer extends EventTarget {
7576
codeToReason,
7677
maxReadableStreamBytes,
7778
maxWritableStreamBytes,
79+
keepaliveIntervalTime,
7880
logger,
7981
}: {
8082
crypto: {
@@ -91,6 +93,7 @@ class QUICServer extends EventTarget {
9193
codeToReason?: StreamCodeToReason;
9294
maxReadableStreamBytes?: number;
9395
maxWritableStreamBytes?: number;
96+
keepaliveIntervalTime?: number;
9497
logger?: Logger;
9598
}) {
9699
super();
@@ -120,6 +123,7 @@ class QUICServer extends EventTarget {
120123
this.codeToReason = codeToReason;
121124
this.maxReadableStreamBytes = maxReadableStreamBytes;
122125
this.maxWritableStreamBytes = maxWritableStreamBytes;
126+
this.keepaliveIntervalTime = keepaliveIntervalTime;
123127
}
124128

125129
@ready(new errors.ErrorQUICServerNotRunning())
@@ -299,6 +303,7 @@ class QUICServer extends EventTarget {
299303
`${QUICConnection.name} ${scid.toString().slice(32)}`,
300304
),
301305
});
306+
connection.setKeepAlive(this.keepaliveIntervalTime);
302307

303308
this.dispatchEvent(
304309
new events.QUICServerConnectionEvent({ detail: connection }),

src/native/napi/connection.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,4 +1068,11 @@ impl Connection {
10681068
|s| s.into()
10691069
).collect();
10701070
}
1071+
1072+
#[napi]
1073+
pub fn send_ack_eliciting(&mut self) -> napi::Result<()> {
1074+
return self.0.send_ack_eliciting().or_else(
1075+
|err| Err(Error::from_reason(err.to_string()))
1076+
);
1077+
}
10711078
}

src/native/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ interface Connection {
129129
localError(): ConnectionError | null;
130130
stats(): Stats;
131131
pathStats(): Array<PathStats>;
132+
sendAckEliciting(): void;
132133
}
133134

134135
interface ConnectionConstructor {

0 commit comments

Comments
 (0)