diff --git a/src/listener.ts b/src/listener.ts index 9ea890f..a8d9d24 100644 --- a/src/listener.ts +++ b/src/listener.ts @@ -189,6 +189,8 @@ export const getRequestListener = ( outgoing.on('close', () => { if (incoming.errored) { req[getAbortController]().abort(incoming.errored.toString()) + } else if (!outgoing.writableFinished) { + req[getAbortController]().abort('Client connection prematurely closed.') } }) diff --git a/test/listener.test.ts b/test/listener.test.ts index f062743..e2d8f8c 100644 --- a/test/listener.test.ts +++ b/test/listener.test.ts @@ -180,35 +180,10 @@ describe('Abort request', () => { server.close() }) - it('should emit an abort event when the nodejs request is aborted', async () => { - const requests: Request[] = [] - const abortedPromise = new Promise((resolve) => { - onAbort = (req) => { - requests.push(req) - resolve() - } - }) - - const req = request(server) - .get('/abort') - .end(() => {}) - - await reqReadyPromise - - req.abort() - - await abortedPromise - - expect(requests).toHaveLength(1) - const abortedReq = requests[0] - expect(abortedReq).toBeInstanceOf(Request) - expect(abortedReq.signal.aborted).toBe(true) - }) - - it('should emit an abort event when the nodejs request is aborted on multiple requests', async () => { - const requests: Request[] = [] - - { + it.each(['get', 'put', 'patch', 'delete'] as const)( + 'should emit an abort event when the nodejs %s request is aborted', + async (method) => { + const requests: Request[] = [] const abortedPromise = new Promise((resolve) => { onAbort = (req) => { requests.push(req) @@ -216,12 +191,8 @@ describe('Abort request', () => { } }) - reqReadyPromise = new Promise((r) => { - reqReadyResolve = r - }) - const req = request(server) - .get('/abort') + [method]('/abort') .end(() => {}) await reqReadyPromise @@ -229,45 +200,80 @@ describe('Abort request', () => { req.abort() await abortedPromise - } - - expect(requests).toHaveLength(1) - for (const abortedReq of requests) { + expect(requests).toHaveLength(1) + const abortedReq = requests[0] expect(abortedReq).toBeInstanceOf(Request) expect(abortedReq.signal.aborted).toBe(true) } + ) - { - const abortedPromise = new Promise((resolve) => { - onAbort = (req) => { - requests.push(req) - resolve() - } - }) + it.each(['get', 'post', 'head', 'patch', 'delete', 'put'] as const)( + 'should emit an abort event when the nodejs request is aborted on multiple %s requests', + async (method) => { + const requests: Request[] = [] - reqReadyPromise = new Promise((r) => { - reqReadyResolve = r - }) + { + const abortedPromise = new Promise((resolve) => { + onAbort = (req) => { + requests.push(req) + resolve() + } + }) - const req = request(server) - .get('/abort') - .end(() => {}) + reqReadyPromise = new Promise((r) => { + reqReadyResolve = r + }) - await reqReadyPromise + const req = request(server) + [method]('/abort') + .end(() => {}) - req.abort() + await reqReadyPromise - await abortedPromise - } + req.abort() - expect(requests).toHaveLength(2) + await abortedPromise + } - for (const abortedReq of requests) { - expect(abortedReq).toBeInstanceOf(Request) - expect(abortedReq.signal.aborted).toBe(true) + expect(requests).toHaveLength(1) + + for (const abortedReq of requests) { + expect(abortedReq).toBeInstanceOf(Request) + expect(abortedReq.signal.aborted).toBe(true) + } + + { + const abortedPromise = new Promise((resolve) => { + onAbort = (req) => { + requests.push(req) + resolve() + } + }) + + reqReadyPromise = new Promise((r) => { + reqReadyResolve = r + }) + + const req = request(server) + [method]('/abort') + .end(() => {}) + + await reqReadyPromise + + req.abort() + + await abortedPromise + } + + expect(requests).toHaveLength(2) + + for (const abortedReq of requests) { + expect(abortedReq).toBeInstanceOf(Request) + expect(abortedReq.signal.aborted).toBe(true) + } } - }) + ) }) describe('overrideGlobalObjects', () => {