Skip to content

Commit de5eb7b

Browse files
committed
wip: implementing keepalive
* Done adding the feature, * Still need to write tests * Related #6 [ci skip]
1 parent e325865 commit de5eb7b

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

src/QUICConnection.ts

Lines changed: 19 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

@@ -107,6 +108,7 @@ class QUICConnection extends EventTarget {
107108
new Error(`${type.toString()} ${code.toString()}`),
108109
maxReadableStreamBytes,
109110
maxWritableStreamBytes,
111+
keepAliveDelay,
110112
logger = new Logger(`${this.name} ${scid}`),
111113
}: {
112114
scid: QUICConnectionId;
@@ -117,6 +119,7 @@ class QUICConnection extends EventTarget {
117119
codeToReason?: StreamCodeToReason;
118120
maxReadableStreamBytes?: number;
119121
maxWritableStreamBytes?: number;
122+
keepAliveDelay?: number;
120123
logger?: Logger;
121124
}) {
122125
logger.info(`Connect ${this.name}`);
@@ -148,6 +151,7 @@ class QUICConnection extends EventTarget {
148151
codeToReason,
149152
maxReadableStreamBytes,
150153
maxWritableStreamBytes,
154+
keepAliveDelay,
151155
logger,
152156
});
153157
socket.connectionMap.set(connection.connectionId, connection);
@@ -169,6 +173,7 @@ class QUICConnection extends EventTarget {
169173
new Error(`${type.toString()} ${code.toString()}`),
170174
maxReadableStreamBytes,
171175
maxWritableStreamBytes,
176+
keepAliveDelay,
172177
logger = new Logger(`${this.name} ${scid}`),
173178
}: {
174179
scid: QUICConnectionId;
@@ -180,6 +185,7 @@ class QUICConnection extends EventTarget {
180185
codeToReason?: StreamCodeToReason;
181186
maxReadableStreamBytes?: number;
182187
maxWritableStreamBytes?: number;
188+
keepAliveDelay?: number;
183189
logger?: Logger;
184190
}): Promise<QUICConnection> {
185191
logger.info(`Accept ${this.name}`);
@@ -211,6 +217,7 @@ class QUICConnection extends EventTarget {
211217
codeToReason,
212218
maxReadableStreamBytes,
213219
maxWritableStreamBytes,
220+
keepAliveDelay,
214221
logger,
215222
});
216223
socket.connectionMap.set(connection.connectionId, connection);
@@ -228,6 +235,7 @@ class QUICConnection extends EventTarget {
228235
codeToReason,
229236
maxReadableStreamBytes,
230237
maxWritableStreamBytes,
238+
keepAliveDelay,
231239
logger,
232240
}: {
233241
type: 'client' | 'server';
@@ -239,6 +247,7 @@ class QUICConnection extends EventTarget {
239247
codeToReason: StreamCodeToReason;
240248
maxReadableStreamBytes: number | undefined;
241249
maxWritableStreamBytes: number | undefined;
250+
keepAliveDelay: number | undefined;
242251
logger: Logger;
243252
}) {
244253
super();
@@ -275,6 +284,14 @@ class QUICConnection extends EventTarget {
275284
const { p: handshakeP, resolveP: resolveHandshakeP } = utils.promise();
276285
this.handshakeP = handshakeP;
277286
this.resolveHandshakeP = resolveHandshakeP;
287+
// Setting up keep alive interval
288+
if (keepAliveDelay != null) {
289+
this.keepAliveInterval = setTimeout(async () => {
290+
// Trigger an ping frame and send
291+
this.conn.sendAckEliciting();
292+
await this.send();
293+
}, keepAliveDelay);
294+
}
278295
}
279296

280297
// Immediately call this after construction
@@ -330,6 +347,8 @@ class QUICConnection extends EventTarget {
330347
force?: boolean;
331348
} = {}) {
332349
this.logger.info(`Destroy ${this.constructor.name}`);
350+
// Clean up keep alive
351+
if (this.keepAliveInterval != null) clearTimeout(this.keepAliveInterval);
333352
// Handle destruction concurrently
334353
const destroyProms: Array<Promise<void>> = [];
335354
for (const stream of this.streamMap.values()) {

src/native/napi/connection.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,4 +1068,9 @@ impl Connection {
10681068
|s| s.into()
10691069
).collect();
10701070
}
1071+
1072+
#[napi]
1073+
pub fn send_ack_eliciting(&self) -> () {
1074+
self.0.send_ack_eliciting();
1075+
}
10711076
}

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 {

tests/QUICClient.test.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { Crypto, Host, Port } from '@/types';
22
import type * as events from '@/events';
3+
import type QUICConnection from '@/QUICConnection';
34
import Logger, { LogLevel, StreamHandler, formatting } from '@matrixai/logger';
45
import { fc, testProp } from '@fast-check/jest';
6+
import { destroyed } from '@matrixai/async-init';
57
import QUICClient from '@/QUICClient';
68
import QUICServer from '@/QUICServer';
79
import * as errors from '@/errors';
@@ -10,9 +12,10 @@ import QUICSocket from '@/QUICSocket';
1012
import * as testsUtils from './utils';
1113
import { tlsConfigWithCaArb } from './tlsUtils';
1214
import { sleep } from './utils';
15+
import * as fixtures from './fixtures/certFixtures';
1316

1417
describe(QUICClient.name, () => {
15-
const logger = new Logger(`${QUICClient.name} Test`, LogLevel.WARN, [
18+
const logger = new Logger(`${QUICClient.name} Test`, LogLevel.DEBUG, [
1619
new StreamHandler(
1720
formatting.format`${formatting.level}:${formatting.keys}:${formatting.msg}`,
1821
),
@@ -1086,4 +1089,74 @@ describe(QUICClient.name, () => {
10861089
{ numRuns: 1 },
10871090
);
10881091
});
1092+
describe('keepalive', () => {
1093+
const tlsConfig = fixtures.tlsConfigMemRSA1;
1094+
test('connection can time out', async () => {
1095+
const connectionEventProm = promise<QUICConnection>();
1096+
const server = new QUICServer({
1097+
crypto,
1098+
logger: logger.getChild(QUICServer.name),
1099+
config: {
1100+
tlsConfig,
1101+
verifyPeer: false,
1102+
maxIdleTimeout: 100,
1103+
},
1104+
});
1105+
server.addEventListener(
1106+
'connection',
1107+
(e: events.QUICServerConnectionEvent) =>
1108+
connectionEventProm.resolveP(e.detail),
1109+
);
1110+
await server.start({
1111+
host: '127.0.0.1' as Host,
1112+
});
1113+
const client = await QUICClient.createQUICClient({
1114+
host: '::ffff:127.0.0.1' as Host,
1115+
port: server.port,
1116+
localHost: '::' as Host,
1117+
crypto,
1118+
logger: logger.getChild(QUICClient.name),
1119+
config: {
1120+
verifyPeer: false,
1121+
maxIdleTimeout: 100,
1122+
},
1123+
});
1124+
// Setting no keepalive should cause the connection to time out
1125+
// It has cleaned up due to timeout
1126+
const clientConnection = client.connection;
1127+
const clientTimeoutProm = promise<void>();
1128+
clientConnection.addEventListener(
1129+
'error',
1130+
(event: events.QUICConnectionErrorEvent) => {
1131+
console.log(event.detail);
1132+
if (event.detail instanceof errors.ErrorQUICConnectionTimeout) {
1133+
console.log('RESOLVING');
1134+
clientTimeoutProm.resolveP();
1135+
}
1136+
},
1137+
);
1138+
console.log('waiting for client timeout');
1139+
await clientTimeoutProm.p;
1140+
expect(clientConnection[destroyed]).toBeTrue();
1141+
1142+
// Server connection should time out as well
1143+
const serverConnection = await connectionEventProm.p;
1144+
const serverTimeoutProm = promise<void>();
1145+
serverConnection.addEventListener(
1146+
'error',
1147+
(event: events.QUICConnectionErrorEvent) => {
1148+
console.log(event.detail);
1149+
if (event.detail instanceof errors.ErrorQUICConnectionTimeout) {
1150+
serverTimeoutProm.resolveP();
1151+
}
1152+
},
1153+
);
1154+
console.log('waiting for server timeout');
1155+
await serverTimeoutProm.p;
1156+
expect(serverConnection[destroyed]).toBeTrue();
1157+
1158+
await client.destroy();
1159+
await server.stop();
1160+
});
1161+
});
10891162
});

0 commit comments

Comments
 (0)