Skip to content

Commit 346aeaf

Browse files
committed
quic: eliminate "ready"/"not ready" states for QuicSession
QuicClientSession and QuicServerSessions are now always immediately ready for use when they are created, so no more need to track ready state. PR-URL: #34283 Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 6665dda commit 346aeaf

File tree

3 files changed

+35
-120
lines changed

3 files changed

+35
-120
lines changed

doc/api/quic.md

-10
Original file line numberDiff line numberDiff line change
@@ -1269,16 +1269,6 @@ empty object when the key exchange is not ephemeral. The supported types are
12691269

12701270
For example: `{ type: 'ECDH', name: 'prime256v1', size: 256 }`.
12711271

1272-
#### quicclientsession.ready
1273-
<!-- YAML
1274-
added: REPLACEME
1275-
-->
1276-
1277-
* Type: {boolean}
1278-
1279-
Set to `true` if the `QuicClientSession` is ready for use. False if the
1280-
`QuicSocket` has not yet been bound.
1281-
12821272
#### quicclientsession.setSocket(socket])
12831273
<!-- YAML
12841274
added: REPLACEME

lib/internal/quic/core.js

+17-99
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ const kBind = Symbol('kBind');
212212
const kClose = Symbol('kClose');
213213
const kCert = Symbol('kCert');
214214
const kClientHello = Symbol('kClientHello');
215-
const kContinueConnect = Symbol('kContinueConnect');
216215
const kCompleteListen = Symbol('kCompleteListen');
217216
const kDestroy = Symbol('kDestroy');
218217
const kEndpointBound = Symbol('kEndpointBound');
@@ -226,11 +225,9 @@ const kInternalServerState = Symbol('kInternalServerState');
226225
const kListen = Symbol('kListen');
227226
const kMakeStream = Symbol('kMakeStream');
228227
const kMaybeBind = Symbol('kMaybeBind');
229-
const kMaybeReady = Symbol('kMaybeReady');
230228
const kOnFileOpened = Symbol('kOnFileOpened');
231229
const kOnFileUnpipe = Symbol('kOnFileUnpipe');
232230
const kOnPipedFileHandleRead = Symbol('kOnPipedFileHandleRead');
233-
const kSocketReady = Symbol('kSocketReady');
234231
const kRemoveSession = Symbol('kRemove');
235232
const kRemoveStream = Symbol('kRemoveStream');
236233
const kServerBusy = Symbol('kServerBusy');
@@ -1148,9 +1145,6 @@ class QuicSocket extends EventEmitter {
11481145

11491146
state.state = kSocketBound;
11501147

1151-
for (const session of state.sessions)
1152-
session[kSocketReady]();
1153-
11541148
process.nextTick(() => {
11551149
// User code may have run before this so we need to check the
11561150
// destroyed state. If it has been destroyed, do nothing.
@@ -1175,13 +1169,6 @@ class QuicSocket extends EventEmitter {
11751169
return;
11761170
state.state = kSocketBound;
11771171

1178-
// Once the QuicSocket has been bound, we notify all currently
1179-
// existing QuicSessions. QuicSessions created after this
1180-
// point will automatically be notified that the QuicSocket
1181-
// is ready.
1182-
for (const session of state.sessions)
1183-
session[kSocketReady]();
1184-
11851172
// The ready event indicates that the QuicSocket is ready to be
11861173
// used to either listen or connect. No QuicServerSession should
11871174
// exist before this event, and all QuicClientSession will remain
@@ -1380,16 +1367,7 @@ class QuicSocket extends EventEmitter {
13801367
if (this.closing)
13811368
throw new ERR_INVALID_STATE('QuicSocket is closing');
13821369

1383-
const session = new QuicClientSession(this, options);
1384-
1385-
try {
1386-
session[kContinueConnect](type, ip);
1387-
} catch (err) {
1388-
session.destroy();
1389-
throw err;
1390-
}
1391-
1392-
return session;
1370+
return new QuicClientSession(this, options, type, ip);
13931371
}
13941372

13951373
[kEndpointClose](endpoint, error) {
@@ -2207,10 +2185,7 @@ class QuicSession extends EventEmitter {
22072185
// signaling the completion of the TLS handshake.
22082186
const makeStream = QuicSession[kMakeStream].bind(this, stream, halfOpen);
22092187
let deferred = false;
2210-
if (this.allowEarlyData && !this.ready) {
2211-
deferred = true;
2212-
this.once('ready', makeStream);
2213-
} else if (!this.handshakeComplete) {
2188+
if (!this.handshakeComplete) {
22142189
deferred = true;
22152190
this.once('secure', makeStream);
22162191
}
@@ -2348,10 +2323,6 @@ class QuicServerSession extends QuicSession {
23482323
} = options;
23492324
super(socket, { highWaterMark, defaultEncoding });
23502325
this[kSetHandle](handle);
2351-
2352-
// Both the handle and socket are immediately usable
2353-
// at this point so trigger the ready event.
2354-
this[kSocketReady]();
23552326
}
23562327

23572328
// Called only when a clientHello event handler is registered.
@@ -2388,12 +2359,6 @@ class QuicServerSession extends QuicSession {
23882359
callback.bind(this[kHandle]));
23892360
}
23902361

2391-
[kSocketReady]() {
2392-
process.nextTick(emit.bind(this, 'ready'));
2393-
}
2394-
2395-
get ready() { return true; }
2396-
23972362
get allowEarlyData() { return false; }
23982363

23992364
addContext(servername, context = {}) {
@@ -2418,7 +2383,6 @@ class QuicClientSession extends QuicSession {
24182383
handshakeStarted: false,
24192384
minDHSize: undefined,
24202385
port: undefined,
2421-
ready: 0,
24222386
remoteTransportParams: undefined,
24232387
requestOCSP: undefined,
24242388
secureContext: undefined,
@@ -2429,7 +2393,7 @@ class QuicClientSession extends QuicSession {
24292393
qlogEnabled: false,
24302394
};
24312395

2432-
constructor(socket, options) {
2396+
constructor(socket, options, type, ip) {
24332397
const sc_options = {
24342398
...options,
24352399
minVersion: 'TLSv1.3',
@@ -2486,33 +2450,8 @@ class QuicClientSession extends QuicSession {
24862450
remoteTransportParams !== undefined &&
24872451
sessionTicket !== undefined;
24882452

2489-
if (socket.bound)
2490-
this[kSocketReady]();
2491-
}
2492-
2493-
[kHandshakePost]() {
2494-
const { type, size } = this.ephemeralKeyInfo;
2495-
if (type === 'DH' && size < this[kInternalClientState].minDHSize) {
2496-
this.destroy(new ERR_TLS_DH_PARAM_SIZE(size));
2497-
return false;
2498-
}
2499-
return true;
2500-
}
2501-
2502-
[kCert](response) {
2503-
this.emit('OCSPResponse', response);
2504-
}
2505-
2506-
[kContinueConnect](type, ip) {
2507-
const state = this[kInternalClientState];
25082453
setTransportParams(state.transportParams);
25092454

2510-
const options =
2511-
(state.verifyHostnameIdentity ?
2512-
QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY : 0) |
2513-
(state.requestOCSP ?
2514-
QUICCLIENTSESSION_OPTION_REQUEST_OCSP : 0);
2515-
25162455
const handle =
25172456
_createClientSession(
25182457
this.socket[kHandle],
@@ -2526,7 +2465,10 @@ class QuicClientSession extends QuicSession {
25262465
state.dcid,
25272466
state.preferredAddressPolicy,
25282467
this.alpnProtocol,
2529-
options,
2468+
(state.verifyHostnameIdentity ?
2469+
QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY : 0) |
2470+
(state.requestOCSP ?
2471+
QUICCLIENTSESSION_OPTION_REQUEST_OCSP : 0),
25302472
state.qlogEnabled,
25312473
state.autoStart);
25322474

@@ -2556,45 +2498,25 @@ class QuicClientSession extends QuicSession {
25562498
}
25572499

25582500
this[kSetHandle](handle);
2559-
2560-
// Listeners may have been added before the handle was created.
2561-
// Ensure that we toggle those listeners in the handle state.
2562-
2563-
const internalState = this[kInternalState];
2564-
if (this.listenerCount('keylog') > 0) {
2565-
toggleListeners(internalState.state, 'keylog', true);
2566-
}
2567-
2568-
if (this.listenerCount('pathValidation') > 0)
2569-
toggleListeners(internalState.state, 'pathValidation', true);
2570-
2571-
if (this.listenerCount('usePreferredAddress') > 0)
2572-
toggleListeners(internalState.state, 'usePreferredAddress', true);
2573-
2574-
this[kMaybeReady](0x2);
25752501
}
25762502

2577-
[kSocketReady]() {
2578-
this[kMaybeReady](0x1);
2503+
[kHandshakePost]() {
2504+
const { type, size } = this.ephemeralKeyInfo;
2505+
if (type === 'DH' && size < this[kInternalClientState].minDHSize) {
2506+
this.destroy(new ERR_TLS_DH_PARAM_SIZE(size));
2507+
return false;
2508+
}
2509+
return true;
25792510
}
25802511

2581-
// The QuicClientSession is ready for use only after
2582-
// (a) The QuicSocket has been bound and
2583-
// (b) The internal handle has been created
2584-
[kMaybeReady](flag) {
2585-
this[kInternalClientState].ready |= flag;
2586-
if (this.ready)
2587-
process.nextTick(emit.bind(this, 'ready'));
2512+
[kCert](response) {
2513+
this.emit('OCSPResponse', response);
25882514
}
25892515

25902516
get allowEarlyData() {
25912517
return this[kInternalClientState].allowEarlyData;
25922518
}
25932519

2594-
get ready() {
2595-
return this[kInternalClientState].ready === 0x3;
2596-
}
2597-
25982520
get handshakeStarted() {
25992521
return this[kInternalClientState].handshakeStarted;
26002522
}
@@ -2608,11 +2530,7 @@ class QuicClientSession extends QuicSession {
26082530
if (state.handshakeStarted)
26092531
return;
26102532
state.handshakeStarted = true;
2611-
if (!this.ready) {
2612-
this.once('ready', () => this[kHandle].startHandshake());
2613-
} else {
2614-
this[kHandle].startHandshake();
2615-
}
2533+
this[kHandle].startHandshake();
26162534
}
26172535

26182536
get ephemeralKeyInfo() {

test/parallel/test-quic-quicsession-resume.js

+18-11
Original file line numberDiff line numberDiff line change
@@ -42,29 +42,36 @@ const countdown = new Countdown(2, () => {
4242

4343
await server.listen();
4444

45+
let storedTicket;
46+
let storedParams;
47+
4548
const req = await client.connect({
4649
address: common.localhostIPv4,
4750
port: server.endpoints[0].address.port,
4851
});
4952

50-
const stream = req.openStream({ halfOpen: true });
51-
stream.end('hello');
52-
stream.resume();
53-
stream.on('close', () => countdown.dec());
54-
5553
req.on('sessionTicket', common.mustCall((ticket, params) => {
5654
assert(ticket instanceof Buffer);
5755
assert(params instanceof Buffer);
5856
debug(' Ticket: %s', ticket.toString('hex'));
5957
debug(' Params: %s', params.toString('hex'));
60-
61-
// Destroy this initial client session...
62-
req.destroy();
63-
64-
// Wait a tick then start a new one.
65-
setImmediate(newSession, ticket, params);
58+
storedTicket = ticket;
59+
storedParams = params;
6660
}, 1));
6761

62+
req.on('secure', () => {
63+
const stream = req.openStream({ halfOpen: true });
64+
stream.end('hello');
65+
stream.resume();
66+
stream.on('close', () => {
67+
req.close();
68+
countdown.dec();
69+
// Wait a turn then start a new session using the stored
70+
// ticket and transportParameters
71+
setImmediate(newSession, storedTicket, storedParams);
72+
});
73+
});
74+
6875
async function newSession(sessionTicket, remoteTransportParams) {
6976
const req = await client.connect({
7077
address: common.localhostIPv4,

0 commit comments

Comments
 (0)