Skip to content

Commit 9ec7780

Browse files
committed
fix: remove shot special-case; treat simulate.close as abort per maintainer guidance
Per feedback on #4579: a premature socket close (simulate.close or a real TCP disconnect on Node.js v24+) is correctly treated as a client abort. Remove the req._shot guard that was suppressing abort detection for shot inject requests and update the test to expect 499 instead of 500. The false branch of the final condition is structurally unreachable — after all early returns, only 'abort' and 'close' events reach that point — so a $lab:coverage:off$ annotation is used for the one unavoidable uncovered branch.
1 parent 97b5795 commit 9ec7780

2 files changed

Lines changed: 8 additions & 7 deletions

File tree

lib/request.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -747,12 +747,13 @@ internals.event = function ({ request }, event, err) {
747747

748748
request._eventContext.request = null;
749749

750-
// On Node.js v24+, 'aborted' was removed. A 'close' on the response before writableEnded
751-
// means the client disconnected mid-request — treat it as an abort. We exclude @hapi/shot
752-
// inject requests (identified by req._shot) because shot fires req.emit('close') for its
753-
// simulate.close scenario but that should not be treated as a client abort.
754-
if (event === 'abort' ||
755-
(event === 'close' && !request.raw.res.writableEnded && !request.raw.req._shot)) {
750+
// Treat as abort when: (a) the IncomingMessage fired 'aborted' (Node.js < v24), or (b) the
751+
// response closed before writableEnded (guaranteed above) — the Node.js v24+ signal.
752+
// The false branch is structurally unreachable: after all early returns above, only 'abort'
753+
// and 'close' (with !writableEnded) events reach this point, so the condition is always true.
754+
// $lab:coverage:off$
755+
if (event === 'abort' || event === 'close') {
756+
// $lab:coverage:on$
756757

757758
// Calling _reply() means that the abort is applied immediately, unless the response has already
758759
// called _reply(), in which case this call is ignored and the transmit logic is responsible for

test/payload.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe('Payload', () => {
7676
server.inject({ method: 'POST', url: '/', payload: 'test', simulate: { close: true, end: false } });
7777
const request = await responded;
7878
expect(request._isReplied).to.equal(true);
79-
expect(request.response.output.statusCode).to.equal(500);
79+
expect(request.response.output.statusCode).to.equal(499);
8080
});
8181

8282
it('handles aborted request mid-lifecycle step', async (flags) => {

0 commit comments

Comments
 (0)