Skip to content
Prev Previous commit
http2: Abort immediately if passed an already aborted signal
- Also add test to that effect
  • Loading branch information
MadaraUchiha committed Nov 21, 2020
commit 9aca7c4e0f764cf6405868acbc8be9fd6d6cdd1a
14 changes: 9 additions & 5 deletions lib/internal/http2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -1726,11 +1726,15 @@ class ClientHttp2Session extends Http2Session {
const { signal } = options;
if (signal) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if this is aborted already?

Copy link
Copy Markdown
Contributor Author

@MadaraUchiha MadaraUchiha Nov 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an excellent question.

Looking at the DOM implementation, it appears like the expected behavior is to abort immediately. I shall add this (and a test) after my 🍕 :D

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would need to abort immediately.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina Done, PTALA.

Also @benjamingr this is likely something we'd want to do in http_client as well.

validateAbortSignal(signal, 'options.signal');
const listener = () => stream.destroy(new AbortError());
signal.addEventListener('abort', listener);
stream.once('close', () => {
signal.removeEventListener('abort', listener);
});
const aborter = () => stream.destroy(new AbortError());
if (signal.aborted) {
aborter();
} else {
signal.addEventListener('abort', aborter);
stream.once('close', () => {
signal.removeEventListener('abort', aborter);
});
}
}

const onConnect = FunctionPrototypeBind(requestOnConnect,
Expand Down
35 changes: 35 additions & 0 deletions test/parallel/test-http2-client-destroy.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,38 @@ const Countdown = require('../common/countdown');
assert.strictEqual(req.destroyed, true);
}));
}
// Pass an already destroyed signal to abort immediately.
{
const server = h2.createServer();
const controller = new AbortController();

server.on('stream', common.mustNotCall());
server.listen(0, common.mustCall(() => {
const client = h2.connect(`http://localhost:${server.address().port}`);
client.on('close', common.mustCall());

const { signal } = controller;
controller.abort();

assert.strictEqual(signal[kEvents].get('abort'), undefined);

client.on('error', common.mustCall(() => {
// After underlying stream dies, signal listener detached
assert.strictEqual(signal[kEvents].get('abort'), undefined);
}));

const req = client.request({}, { signal });
// Signal already aborted, so no event listener attached.
assert.strictEqual(signal[kEvents].get('abort'), undefined);

assert.strictEqual(req.aborted, false);
// Destroyed on same tick as request made
assert.strictEqual(req.destroyed, true);

req.on('error', common.mustCall((err) => {
assert.strictEqual(err.code, 'ABORT_ERR');
assert.strictEqual(err.name, 'AbortError');
}));
req.on('close', common.mustCall(() => server.close()));
}));
}