Skip to content

Commit 0183736

Browse files
committed
feat: new connections are rejected if connection is stopping
[ci skip]
1 parent c8bdd7d commit 0183736

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

src/QUICConnection.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { buildQuicheConfig, minIdleTimeout } from './config';
2727
import QUICConnectionId from './QUICConnectionId';
2828
import QUICStream from './QUICStream';
2929
import { quiche, ConnectionErrorCode } from './native';
30+
import { Shutdown } from './native/types';
3031
import * as utils from './utils';
3132
import * as events from './events';
3233
import * as errors from './errors';
@@ -948,6 +949,13 @@ class QUICConnection {
948949
for (const streamId of this.conn.readable() as Iterable<StreamId>) {
949950
let quicStream = this.streamMap.get(streamId);
950951
if (quicStream == null) {
952+
if (this[running] === false || this[status] === 'stopping') {
953+
// We should reject new connections when stopping
954+
this.conn.streamShutdown(streamId, Shutdown.Write, 1);
955+
this.conn.streamShutdown(streamId, Shutdown.Read, 1);
956+
continue;
957+
}
958+
951959
quicStream = QUICStream.createQUICStream({
952960
initiated: 'peer',
953961
streamId,
@@ -977,6 +985,13 @@ class QUICConnection {
977985
for (const streamId of this.conn.writable() as Iterable<StreamId>) {
978986
let quicStream = this.streamMap.get(streamId);
979987
if (quicStream == null) {
988+
if (this[running] === false || this[status] === 'stopping') {
989+
// We should reject new connections when stopping
990+
this.conn.streamShutdown(streamId, Shutdown.Write, 1);
991+
this.conn.streamShutdown(streamId, Shutdown.Read, 1);
992+
continue;
993+
}
994+
980995
quicStream = QUICStream.createQUICStream({
981996
initiated: 'peer',
982997
streamId,

tests/QUICStream.test.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import QUICServer from '@/QUICServer';
77
import QUICClient from '@/QUICClient';
88
import QUICStream from '@/QUICStream';
99
import * as testsUtils from './utils';
10-
import { generateTLSConfig } from './utils';
10+
import { generateTLSConfig, sleep } from './utils';
1111

1212
describe(QUICStream.name, () => {
1313
const logger = new Logger(`${QUICStream.name} Test`, LogLevel.WARN, [
@@ -1414,4 +1414,82 @@ describe(QUICStream.name, () => {
14141414
]);
14151415
await client.destroy({ force: true });
14161416
});
1417+
test('new streams are rejected when a connection is ending', async () => {
1418+
const message = Buffer.from('The Quick Brown Fox Jumped Over The Lazy Dog');
1419+
const connectionEventProm =
1420+
utils.promise<events.EventQUICServerConnection>();
1421+
const tlsConfig = await generateTLSConfig(defaultType);
1422+
const server = new QUICServer({
1423+
crypto: {
1424+
key,
1425+
ops: serverCrypto,
1426+
},
1427+
logger: logger.getChild(QUICServer.name),
1428+
config: {
1429+
key: tlsConfig.leafKeyPairPEM.privateKey,
1430+
cert: tlsConfig.leafCertPEM,
1431+
verifyPeer: false,
1432+
},
1433+
});
1434+
socketCleanMethods.extractSocket(server);
1435+
server.addEventListener(
1436+
events.EventQUICServerConnection.name,
1437+
(e: events.EventQUICServerConnection) => connectionEventProm.resolveP(e),
1438+
);
1439+
await server.start({
1440+
host: localhost,
1441+
});
1442+
const client = await QUICClient.createQUICClient({
1443+
host: localhost,
1444+
port: server.port,
1445+
localHost: localhost,
1446+
crypto: {
1447+
ops: clientCrypto,
1448+
},
1449+
logger: logger.getChild(QUICClient.name),
1450+
config: {
1451+
verifyPeer: false,
1452+
},
1453+
});
1454+
socketCleanMethods.extractSocket(client);
1455+
const conn = (await connectionEventProm.p).detail;
1456+
1457+
// Do the test
1458+
const { p: waitP, resolveP: waitResolveP } = utils.promise();
1459+
const activeServerStreams: Array<Promise<void>> = [];
1460+
conn.addEventListener(
1461+
events.EventQUICConnectionStream.name,
1462+
async (streamEvent: events.EventQUICConnectionStream) => {
1463+
const stream = streamEvent.detail;
1464+
await waitP;
1465+
const streamProm = stream.readable.pipeTo(stream.writable);
1466+
activeServerStreams.push(streamProm);
1467+
},
1468+
);
1469+
1470+
const stream = client.connection.newStream();
1471+
const writer = stream.writable.getWriter();
1472+
await writer.write(message);
1473+
await writer.close();
1474+
1475+
// Start unforced close of client
1476+
const clientDestroyP = client.destroy({ force: false });
1477+
// Yield to allow `destroy` to progress
1478+
await sleep(0);
1479+
// New client streams should throw
1480+
expect(() => client.connection.newStream()).toThrow();
1481+
// Creating a stream on the server side should throw
1482+
const newStream = conn.newStream();
1483+
await newStream.writable.close();
1484+
const asd = (async () => {
1485+
for await (const _ of newStream.readable) {
1486+
// Do nothing
1487+
}
1488+
})();
1489+
await expect(asd).rejects.toThrow('read 1');
1490+
1491+
waitResolveP();
1492+
await clientDestroyP;
1493+
await server.stop({ force: true });
1494+
});
14171495
});

0 commit comments

Comments
 (0)