node/test/parallel/test-quic-h3-headers-support.mjs
James M Snell 430f89eb8d quic: improve peer cert verification
On the client, add verifyPeer: 'auto', 'strict', and
'manual' modes. The 'strict' mode will reject invalid
certs at the handshake layer, while the 'manual' mode
allows the application to inspect the peer cert and decide
whether to trust it or not. The 'auto' mode is the default
and will reject invalid certs at a middle layer after the
onhandshake event.

Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-by: Opencode/Opus 4.6
PR-URL: https://github.com/nodejs/node/pull/63483
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
2026-05-24 19:14:10 -07:00

78 lines
2.5 KiB
JavaScript

// Flags: --experimental-quic --no-warnings
// Test: Headers support detection for non-H3 sessions.
// headersSupported is UNSUPPORTED for non-H3 sessions
// Sending headers on non-H3 session throws ERR_INVALID_STATE
// Setting header callbacks on non-H3 stream throws ERR_INVALID_STATE
import { hasQuic, skip, mustCall } from '../common/index.mjs';
import assert from 'node:assert';
import * as fixtures from '../common/fixtures.mjs';
const { readKey } = fixtures;
const { throws } = assert;
if (!hasQuic) {
skip('QUIC is not enabled');
}
const { listen, connect } = await import('node:quic');
const { createPrivateKey } = await import('node:crypto');
const key = createPrivateKey(readKey('agent1-key.pem'));
const cert = readKey('agent1-cert.pem');
const encoder = new TextEncoder();
const serverDone = Promise.withResolvers();
const serverEndpoint = await listen(mustCall(async (serverSession) => {
serverSession.onstream = mustCall(async (stream) => {
// Sending headers on non-H3 stream throws.
throws(() => stream.sendHeaders({ ':status': '200' }), { code: 'ERR_INVALID_STATE' });
// Setting onheaders on non-H3 stream throws.
throws(() => stream.onheaders = () => {}, { code: 'ERR_INVALID_STATE' });
// Setting ontrailers on non-H3 stream throws.
throws(() => stream.ontrailers = () => {}, { code: 'ERR_INVALID_STATE' });
// Setting oninfo on non-H3 stream throws.
throws(() => stream.oninfo = () => {}, { code: 'ERR_INVALID_STATE' });
// Setting onwanttrailers on non-H3 stream throws.
throws(() => stream.onwanttrailers = () => {}, { code: 'ERR_INVALID_STATE' });
// sendInformationalHeaders throws on non-H3.
throws(() => stream.sendInformationalHeaders({ ':status': '103' }), {
code: 'ERR_INVALID_STATE',
});
// sendTrailers throws on non-H3.
throws(() => stream.sendTrailers({ 'x-trailer': 'value' }), { code: 'ERR_INVALID_STATE' });
stream.writer.endSync();
serverSession.close();
serverDone.resolve();
});
}), {
sni: { '*': { keys: [key], certs: [cert] } },
alpn: 'quic-test',
});
const clientSession = await connect(serverEndpoint.address, {
servername: 'localhost',
verifyPeer: 'manual',
alpn: 'quic-test',
});
await clientSession.opened;
const stream = await clientSession.createBidirectionalStream({
body: encoder.encode('ping'),
});
// Client side — sending headers on non-H3 stream throws.
throws(() => stream.sendHeaders({ ':method': 'GET' }), { code: 'ERR_INVALID_STATE' });
await serverDone.promise;
await clientSession.close();
await serverEndpoint.close();