Skip to content

Commit 413eb7d

Browse files
quic: destroy stream on invalid promise body
When `setBody()` receives a promise that resolves to an unsupported body type, `configureOutbound()` throws inside the fulfillment handler. The rejection handler only covered the original promise, so the error rejected a discarded promise and crashed the process as an unhandled rejection. Catch errors thrown by `configureOutbound()` in the fulfillment handler and destroy the stream with them, as the rejection handler already does. For an unsupported resolved value, `stream.closed` now rejects with the same `ERR_INVALID_ARG_TYPE` that `setBody()` throws synchronously. Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
1 parent 24b9ca9 commit 413eb7d

2 files changed

Lines changed: 50 additions & 4 deletions

File tree

‎lib/internal/quic/quic.js‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,14 +1375,21 @@ function configureOutbound(handle, stream, body) {
13751375
// Handle Promise - await and recurse. Native promises auto-flatten,
13761376
// so the resolved value will never itself be a promise.
13771377
if (isPromise(body)) {
1378+
const onError = (err) => {
1379+
if (!stream.destroyed) {
1380+
stream.destroy(err);
1381+
}
1382+
};
13781383
PromisePrototypeThen(
13791384
body,
1380-
(resolved) => configureOutbound(handle, stream, resolved),
1381-
(err) => {
1382-
if (!stream.destroyed) {
1383-
stream.destroy(err);
1385+
(resolved) => {
1386+
try {
1387+
configureOutbound(handle, stream, resolved);
1388+
} catch (err) {
1389+
onError(err);
13841390
}
13851391
},
1392+
onError,
13861393
);
13871394
return;
13881395
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: body: Promise resolving to an invalid body destroys the stream.
4+
// When the body is a Promise that resolves to an unsupported type, the
5+
// stream should be destroyed with the same ERR_INVALID_ARG_TYPE that
6+
// setBody() throws synchronously for that type.
7+
8+
import { hasQuic, skip, mustCall } from '../common/index.mjs';
9+
import assert from 'node:assert';
10+
11+
if (!hasQuic) {
12+
skip('QUIC is not enabled');
13+
}
14+
15+
const { listen, connect } = await import('../common/quic.mjs');
16+
17+
const serverEndpoint = await listen(mustCall(async (serverSession) => {
18+
await serverSession.closed;
19+
}), { transportParams: { maxIdleTimeout: 5 } });
20+
21+
const clientSession = await connect(serverEndpoint.address, {
22+
transportParams: { maxIdleTimeout: 5 },
23+
});
24+
await clientSession.opened;
25+
26+
const stream = await clientSession.createBidirectionalStream();
27+
28+
assert.throws(() => stream.setBody(42), { code: 'ERR_INVALID_ARG_TYPE' });
29+
30+
const stream2 = await clientSession.createBidirectionalStream();
31+
32+
const closedPromise = assert.rejects(stream2.closed, {
33+
code: 'ERR_INVALID_ARG_TYPE',
34+
});
35+
36+
stream2.setBody(Promise.resolve(42));
37+
38+
await Promise.all([closedPromise, clientSession.closed]);
39+
await serverEndpoint.close();

0 commit comments

Comments
 (0)