mirror of
https://github.com/nodejs/node.git
synced 2025-05-04 14:26:57 +00:00

This slightly alters the behaviour of session close by first using .end() on a session socket to finish writing the data and only then calls .destroy() to make sure the Readable side is closed. This allows the socket to finish transmitting data, receive proper FIN packet and avoid ECONNRESET errors upon graceful close. onStreamClose now directly calls stream.destroy() instead of kMaybeDestroy because the latter will first check that the stream has writableFinished set. And that may not be true as we have just (synchronously) called .end() on the stream if it was not closed and that doesn't give it enough time to finish. Furthermore there is no point in waiting for 'finish' as the other party have already closed the stream and we won't be able to write anyway. This also changes a few tests to correctly handle graceful session close. This includes: * not reading request data (on client side) * not reading push stream data (on client side) * relying on socket.destroy() (on client) to finish server session due to the destroy of the socket without closing the server session. As the goaway itself is *not* a session close. Added few 'close' event mustCall checks. PR-URL: https://github.com/nodejs/node/pull/30854 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
153 lines
3.4 KiB
JavaScript
153 lines
3.4 KiB
JavaScript
'use strict';
|
|
|
|
const common = require('../common');
|
|
if (!common.hasCrypto)
|
|
common.skip('missing crypto');
|
|
|
|
const assert = require('assert');
|
|
const events = require('events');
|
|
const { createServer, connect } = require('http2');
|
|
|
|
events.captureRejections = true;
|
|
|
|
{
|
|
// Test error thrown in the server 'stream' event,
|
|
// after a respond()
|
|
|
|
const server = createServer();
|
|
server.on('stream', common.mustCall(async (stream) => {
|
|
server.close();
|
|
|
|
stream.respond({ ':status': 200 });
|
|
|
|
const _err = new Error('kaboom');
|
|
stream.on('error', common.mustCall((err) => {
|
|
assert.strictEqual(err, _err);
|
|
}));
|
|
throw _err;
|
|
}));
|
|
|
|
server.listen(0, common.mustCall(() => {
|
|
const { port } = server.address();
|
|
const session = connect(`http://localhost:${port}`);
|
|
|
|
const req = session.request();
|
|
|
|
req.on('error', common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_ERROR');
|
|
}));
|
|
|
|
req.on('close', common.mustCall(() => {
|
|
session.close();
|
|
}));
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Test error thrown in the server 'stream' event,
|
|
// before a respond().
|
|
|
|
const server = createServer();
|
|
server.on('stream', common.mustCall(async (stream) => {
|
|
server.close();
|
|
|
|
stream.on('error', common.mustNotCall());
|
|
|
|
throw new Error('kaboom');
|
|
}));
|
|
|
|
server.listen(0, common.mustCall(() => {
|
|
const { port } = server.address();
|
|
const session = connect(`http://localhost:${port}`);
|
|
|
|
const req = session.request();
|
|
|
|
req.on('response', common.mustCall((headers) => {
|
|
assert.strictEqual(headers[':status'], 500);
|
|
}));
|
|
|
|
req.on('close', common.mustCall(() => {
|
|
session.close();
|
|
}));
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Test error thrown in 'request' event
|
|
|
|
const server = createServer(common.mustCall(async (req, res) => {
|
|
server.close();
|
|
res.setHeader('content-type', 'application/json');
|
|
const _err = new Error('kaboom');
|
|
throw _err;
|
|
}));
|
|
|
|
server.listen(0, common.mustCall(() => {
|
|
const { port } = server.address();
|
|
const session = connect(`http://localhost:${port}`);
|
|
|
|
const req = session.request();
|
|
|
|
req.on('response', common.mustCall((headers) => {
|
|
assert.strictEqual(headers[':status'], 500);
|
|
assert.strictEqual(Object.hasOwnProperty.call(headers, 'content-type'),
|
|
false);
|
|
}));
|
|
|
|
req.on('close', common.mustCall(() => {
|
|
session.close();
|
|
}));
|
|
|
|
req.resume();
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Test error thrown in the client 'stream' event
|
|
|
|
const server = createServer();
|
|
server.on('stream', common.mustCall(async (stream) => {
|
|
const { port } = server.address();
|
|
|
|
server.close();
|
|
|
|
stream.pushStream({
|
|
':scheme': 'http',
|
|
':path': '/foobar',
|
|
':authority': `localhost:${port}`,
|
|
}, common.mustCall((err, push) => {
|
|
push.respond({
|
|
'content-type': 'text/html',
|
|
':status': 200
|
|
});
|
|
push.end('pushed by the server');
|
|
|
|
stream.end('test');
|
|
}));
|
|
|
|
stream.respond({
|
|
':status': 200
|
|
});
|
|
}));
|
|
|
|
server.listen(0, common.mustCall(() => {
|
|
const { port } = server.address();
|
|
const session = connect(`http://localhost:${port}`);
|
|
|
|
const req = session.request();
|
|
req.resume();
|
|
|
|
session.on('stream', common.mustCall(async (stream) => {
|
|
session.close();
|
|
|
|
const _err = new Error('kaboom');
|
|
stream.on('error', common.mustCall((err) => {
|
|
assert.strictEqual(err, _err);
|
|
}));
|
|
throw _err;
|
|
}));
|
|
|
|
req.end();
|
|
}));
|
|
}
|