Skip to content

Commit 2fdcdad

Browse files
authored
fix: detection of client premature close (#218)
* fix: detection of client premature close Ref: #217 Signed-off-by: Tomas Dvorak <[email protected]> * fix: improve detection of client premature close Ref: #217 Signed-off-by: Tomas Dvorak <[email protected]> * fixup! fix: improve detection of client premature close Ref: #217 Signed-off-by: Tomas Dvorak <[email protected]> * fixup! fixup! fix: improve detection of client premature close Ref: #217 Signed-off-by: Tomas Dvorak <[email protected]> --------- Signed-off-by: Tomas Dvorak <[email protected]>
1 parent b13dead commit 2fdcdad

File tree

2 files changed

+68
-60
lines changed

2 files changed

+68
-60
lines changed

src/listener.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ export const getRequestListener = (
189189
outgoing.on('close', () => {
190190
if (incoming.errored) {
191191
req[getAbortController]().abort(incoming.errored.toString())
192+
} else if (!outgoing.writableFinished) {
193+
req[getAbortController]().abort('Client connection prematurely closed.')
192194
}
193195
})
194196

test/listener.test.ts

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -180,94 +180,100 @@ describe('Abort request', () => {
180180
server.close()
181181
})
182182

183-
it('should emit an abort event when the nodejs request is aborted', async () => {
184-
const requests: Request[] = []
185-
const abortedPromise = new Promise<void>((resolve) => {
186-
onAbort = (req) => {
187-
requests.push(req)
188-
resolve()
189-
}
190-
})
191-
192-
const req = request(server)
193-
.get('/abort')
194-
.end(() => {})
195-
196-
await reqReadyPromise
197-
198-
req.abort()
199-
200-
await abortedPromise
201-
202-
expect(requests).toHaveLength(1)
203-
const abortedReq = requests[0]
204-
expect(abortedReq).toBeInstanceOf(Request)
205-
expect(abortedReq.signal.aborted).toBe(true)
206-
})
207-
208-
it('should emit an abort event when the nodejs request is aborted on multiple requests', async () => {
209-
const requests: Request[] = []
210-
211-
{
183+
it.each(['get', 'put', 'patch', 'delete'] as const)(
184+
'should emit an abort event when the nodejs %s request is aborted',
185+
async (method) => {
186+
const requests: Request[] = []
212187
const abortedPromise = new Promise<void>((resolve) => {
213188
onAbort = (req) => {
214189
requests.push(req)
215190
resolve()
216191
}
217192
})
218193

219-
reqReadyPromise = new Promise<void>((r) => {
220-
reqReadyResolve = r
221-
})
222-
223194
const req = request(server)
224-
.get('/abort')
195+
[method]('/abort')
225196
.end(() => {})
226197

227198
await reqReadyPromise
228199

229200
req.abort()
230201

231202
await abortedPromise
232-
}
233-
234-
expect(requests).toHaveLength(1)
235203

236-
for (const abortedReq of requests) {
204+
expect(requests).toHaveLength(1)
205+
const abortedReq = requests[0]
237206
expect(abortedReq).toBeInstanceOf(Request)
238207
expect(abortedReq.signal.aborted).toBe(true)
239208
}
209+
)
240210

241-
{
242-
const abortedPromise = new Promise<void>((resolve) => {
243-
onAbort = (req) => {
244-
requests.push(req)
245-
resolve()
246-
}
247-
})
211+
it.each(['get', 'post', 'head', 'patch', 'delete', 'put'] as const)(
212+
'should emit an abort event when the nodejs request is aborted on multiple %s requests',
213+
async (method) => {
214+
const requests: Request[] = []
248215

249-
reqReadyPromise = new Promise<void>((r) => {
250-
reqReadyResolve = r
251-
})
216+
{
217+
const abortedPromise = new Promise<void>((resolve) => {
218+
onAbort = (req) => {
219+
requests.push(req)
220+
resolve()
221+
}
222+
})
252223

253-
const req = request(server)
254-
.get('/abort')
255-
.end(() => {})
224+
reqReadyPromise = new Promise<void>((r) => {
225+
reqReadyResolve = r
226+
})
256227

257-
await reqReadyPromise
228+
const req = request(server)
229+
[method]('/abort')
230+
.end(() => {})
258231

259-
req.abort()
232+
await reqReadyPromise
260233

261-
await abortedPromise
262-
}
234+
req.abort()
263235

264-
expect(requests).toHaveLength(2)
236+
await abortedPromise
237+
}
265238

266-
for (const abortedReq of requests) {
267-
expect(abortedReq).toBeInstanceOf(Request)
268-
expect(abortedReq.signal.aborted).toBe(true)
239+
expect(requests).toHaveLength(1)
240+
241+
for (const abortedReq of requests) {
242+
expect(abortedReq).toBeInstanceOf(Request)
243+
expect(abortedReq.signal.aborted).toBe(true)
244+
}
245+
246+
{
247+
const abortedPromise = new Promise<void>((resolve) => {
248+
onAbort = (req) => {
249+
requests.push(req)
250+
resolve()
251+
}
252+
})
253+
254+
reqReadyPromise = new Promise<void>((r) => {
255+
reqReadyResolve = r
256+
})
257+
258+
const req = request(server)
259+
[method]('/abort')
260+
.end(() => {})
261+
262+
await reqReadyPromise
263+
264+
req.abort()
265+
266+
await abortedPromise
267+
}
268+
269+
expect(requests).toHaveLength(2)
270+
271+
for (const abortedReq of requests) {
272+
expect(abortedReq).toBeInstanceOf(Request)
273+
expect(abortedReq.signal.aborted).toBe(true)
274+
}
269275
}
270-
})
276+
)
271277
})
272278

273279
describe('overrideGlobalObjects', () => {

0 commit comments

Comments
 (0)