Skip to content

Commit e55dab1

Browse files
zerone0xclaude
andcommitted
test: fix quic regression test — pass ca cert and track server closed
Two fixes: 1. Pass ca: certs to connect() so TLS validation succeeds against the self-signed agent1-cert.pem. Without it the client rejects the server cert with UNABLE_TO_GET_ISSUER_CERT_LOCALLY, the session never fully opens, and 'await clientSession.closed' never settles. 2. Track serverSession.closed via a separate Promise.withResolvers() so the test waits for both sides to cleanly tear down before exiting. Also clarifies in the comment that the exact C++ crash path (packet-allocation failure inside SendDatagram) cannot be triggered from JS; the test exercises the closest reachable scenario and validates the null-impl_ guard introduced in session.cc. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent da73141 commit e55dab1

File tree

1 file changed

+27
-11
lines changed

1 file changed

+27
-11
lines changed
Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
// Flags: --experimental-quic --no-warnings
22
// Regression test for https://github.com/nodejs/node/pull/62126
3-
// UpdateDataStats() must not crash when called after the session's impl_ has
4-
// been reset to null (i.e. after the session is destroyed).
53
//
6-
// The crash path is in Session::SendDatagram():
4+
// Validates that UpdateDataStats() does not crash when called on a session
5+
// whose impl_ has already been reset to null (i.e. after Destroy()).
6+
//
7+
// The crash path in Session::SendDatagram():
78
// auto on_exit = OnScopeLeave([&] { ...; UpdateDataStats(); });
8-
// If the send encounters an error it calls Close(SILENT) → Destroy() →
9-
// impl_.reset(). The on_exit lambda then fires with impl_ == nullptr.
9+
// When an internal send error calls Close(SILENT) -> Destroy() -> impl_.reset(),
10+
// the on_exit lambda fires with impl_ == nullptr. The `if (!impl_) return;`
11+
// guard added by this PR prevents the crash.
12+
//
13+
// Note: the precise crash trigger (a packet-allocation failure inside
14+
// SendDatagram) cannot be induced from JS. This test exercises the closest
15+
// reachable scenario: datagrams sent immediately before session close, racing
16+
// the on_exit UpdateDataStats() call against teardown.
1017

1118
import { hasQuic, skip, mustCall } from '../common/index.mjs';
1219
import * as fixtures from '../common/fixtures.mjs';
@@ -21,34 +28,43 @@ const { createPrivateKey } = await import('node:crypto');
2128
const keys = createPrivateKey(fixtures.readKey('agent1-key.pem'));
2229
const certs = fixtures.readKey('agent1-cert.pem');
2330

24-
// Datagrams must be enabled on both sides for sendDatagram() to work.
31+
// Both sides must negotiate datagram support (maxDatagramFrameSize > 0).
2532
const kDatagramOptions = {
2633
transportParams: {
2734
maxDatagramFrameSize: 1200n,
2835
},
2936
};
3037

38+
const serverClosed = Promise.withResolvers();
39+
3140
const serverEndpoint = await listen(
3241
mustCall((serverSession) => {
3342
serverSession.opened.then(mustCall(async () => {
34-
// Send a datagram then immediately close. This exercises the
35-
// UpdateDataStats() call that fires via on_exit after SendDatagram —
36-
// the close can race with or precede the stats update, leaving
37-
// impl_ == nullptr. Before the fix this would crash.
43+
// Fire a datagram send then close immediately. This races the
44+
// UpdateDataStats() invoked by on_exit in SendDatagram() against the
45+
// Destroy() triggered by close(), exercising the null-impl_ guard.
3846
serverSession.sendDatagram(Buffer.from('hello')).catch(() => {});
3947
serverSession.close();
4048
}));
49+
serverSession.closed.then(mustCall(() => serverClosed.resolve()));
4150
}),
4251
{ keys, certs, ...kDatagramOptions },
4352
);
4453

45-
const clientSession = await connect(serverEndpoint.address, kDatagramOptions);
54+
// Pass the server certificate as the CA so TLS validation succeeds on the
55+
// client side (agent1-cert.pem is self-signed).
56+
const clientSession = await connect(serverEndpoint.address, {
57+
ca: certs,
58+
...kDatagramOptions,
59+
});
60+
4661
await clientSession.opened;
4762

4863
// Mirror the race on the client side.
4964
clientSession.sendDatagram(Buffer.from('world')).catch(() => {});
5065
clientSession.close();
5166

5267
await clientSession.closed;
68+
await serverClosed.promise;
5369
serverEndpoint.close();
5470
await serverEndpoint.closed;

0 commit comments

Comments
 (0)