mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Signed-off-by: Antoine du Hamel <duhamelantoine1995@gmail.com> PR-URL: https://github.com/nodejs/node/pull/64716 Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Aviv Keller <me@aviv.sh>
95 lines
3.2 KiB
JavaScript
95 lines
3.2 KiB
JavaScript
// Flags: --experimental-quic --experimental-stream-iter --no-warnings
|
|
|
|
// Test: stream writer.fail() emits a RESET_STREAM with a non-zero
|
|
// application error code instead of the previously hard-coded 0n.
|
|
//
|
|
// Two cases are exercised against the test fixture's non-h3 ALPN
|
|
// (`quic-test`), which selects the C++ DefaultApplication and exposes
|
|
// `internalErrorCode === 0x1n` (NGTCP2_INTERNAL_ERROR) via session
|
|
// state:
|
|
//
|
|
// 1. `writer.fail(plainError)` — peer receives RESET_STREAM with the
|
|
// session's `internalErrorCode` (`0x1`), proving the hard-coded
|
|
// `0n` regression is gone and that the C++ -> JS state plumbing
|
|
// surfaces the application's code.
|
|
// 2. `writer.fail(new QuicError('msg', { errorCode: 0x42n }))` —
|
|
// peer receives RESET_STREAM with the explicit code, proving
|
|
// the QuicError fast path.
|
|
//
|
|
// The peer-side observation goes through `stream.onreset(err)` where
|
|
// `err` is `ERR_QUIC_APPLICATION_ERROR` exposing the wire code on
|
|
// `err.errorCode` (a BigInt).
|
|
|
|
import { hasQuic, skip, mustCall } from '../common/index.mjs';
|
|
import assert from 'node:assert';
|
|
|
|
if (!hasQuic) {
|
|
skip('QUIC is not enabled');
|
|
}
|
|
|
|
const { listen, connect } = await import('../common/quic.mjs');
|
|
const { QuicError } = await import('node:quic');
|
|
|
|
function wireCodeOf(err) {
|
|
assert.strictEqual(err.code, 'ERR_QUIC_APPLICATION_ERROR');
|
|
return err.errorCode;
|
|
}
|
|
|
|
// Server: capture the next two streams. Each stream receives an
|
|
// onreset handler synchronously inside onstream so the C++ -> JS
|
|
// dispatch ordering (data packet -> reset packet) finds the handler
|
|
// already attached.
|
|
const expectedCodes = [0x1n, 0x42n];
|
|
let nextStreamIndex = 0;
|
|
const allDone = Promise.withResolvers();
|
|
const observed = [];
|
|
|
|
const serverEndpoint = await listen(mustCall((serverSession) => {
|
|
serverSession.onstream = mustCall(async (stream) => {
|
|
const i = nextStreamIndex++;
|
|
stream.onreset = mustCall((err) => {
|
|
observed[i] = wireCodeOf(err);
|
|
if (observed.length === expectedCodes.length &&
|
|
observed[0] !== undefined && observed[1] !== undefined) {
|
|
allDone.resolve();
|
|
}
|
|
});
|
|
|
|
// The peer's reset causes stream.closed to reject.
|
|
await assert.rejects(stream.closed, {
|
|
code: 'ERR_QUIC_APPLICATION_ERROR',
|
|
});
|
|
}, 2);
|
|
}));
|
|
|
|
const clientSession = await connect(serverEndpoint.address, {
|
|
verifyPeer: 'manual',
|
|
});
|
|
await clientSession.opened;
|
|
|
|
// 1. Plain Error -> session.internalErrorCode (0x1n for non-h3).
|
|
{
|
|
const stream = await clientSession.createBidirectionalStream();
|
|
const writer = stream.writer;
|
|
// Write a tiny chunk to guarantee the server-side stream is
|
|
// created via onstream before the RESET_STREAM frame arrives.
|
|
writer.writeSync('x');
|
|
writer.fail(new Error('plain error reason'));
|
|
}
|
|
|
|
// 2. QuicError with explicit code -> peer sees that exact code.
|
|
{
|
|
const stream = await clientSession.createBidirectionalStream();
|
|
const writer = stream.writer;
|
|
writer.writeSync('y');
|
|
writer.fail(new QuicError('explicit code', { errorCode: 0x42n }));
|
|
}
|
|
|
|
await allDone.promise;
|
|
|
|
assert.strictEqual(observed[0], expectedCodes[0]);
|
|
assert.strictEqual(observed[1], expectedCodes[1]);
|
|
|
|
await clientSession.close();
|
|
await serverEndpoint.close();
|