Skip to content

Commit 4f1b090

Browse files
usualomamotemen
andauthored
fix: Avoid error if connection is aborted before internal request object is created (#221)
* test: add test for request abort without requestCache Co-authored-by: Hironao OTSUBO <[email protected]> * fix: Avoid error if connection is aborted before internal request object is created At this time, since the user is not listening for abort events, so we should return immediately. --------- Co-authored-by: Hironao OTSUBO <[email protected]>
1 parent 105a5cf commit 4f1b090

File tree

4 files changed

+36
-4
lines changed

4 files changed

+36
-4
lines changed

src/listener.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { IncomingMessage, ServerResponse, OutgoingHttpHeaders } from 'node:http'
22
import type { Http2ServerRequest, Http2ServerResponse } from 'node:http2'
33
import {
4-
getAbortController,
4+
abortControllerKey,
55
newRequest,
66
Request as LightweightRequest,
77
toRequestError,
@@ -187,10 +187,15 @@ export const getRequestListener = (
187187

188188
// Detect if request was aborted.
189189
outgoing.on('close', () => {
190+
const abortController = req[abortControllerKey] as AbortController | undefined
191+
if (!abortController) {
192+
return
193+
}
194+
190195
if (incoming.errored) {
191-
req[getAbortController]().abort(incoming.errored.toString())
196+
req[abortControllerKey].abort(incoming.errored.toString())
192197
} else if (!outgoing.writableFinished) {
193-
req[getAbortController]().abort('Client connection prematurely closed.')
198+
req[abortControllerKey].abort('Client connection prematurely closed.')
194199
}
195200
})
196201

src/request.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ const getRequestCache = Symbol('getRequestCache')
9696
const requestCache = Symbol('requestCache')
9797
const incomingKey = Symbol('incomingKey')
9898
const urlKey = Symbol('urlKey')
99-
const abortControllerKey = Symbol('abortControllerKey')
99+
export const abortControllerKey = Symbol('abortControllerKey')
100100
export const getAbortController = Symbol('getAbortController')
101101

102102
const requestPrototype: Record<string | symbol, any> = {

test/listener.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,17 @@ describe('Abort request', () => {
274274
}
275275
}
276276
)
277+
278+
it('should handle request abort without requestCache', async () => {
279+
const fetchCallback = async () => {
280+
// NOTE: we don't req.signal
281+
await new Promise(() => {}) // never resolve
282+
}
283+
const requestListener = getRequestListener(fetchCallback)
284+
const server = createServer(requestListener)
285+
const req = request(server).post('/abort').timeout({ deadline: 1 })
286+
await expect(req).rejects.toHaveProperty('timeout')
287+
})
277288
})
278289

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

test/request.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Request as LightweightRequest,
66
GlobalRequest,
77
getAbortController,
8+
abortControllerKey,
89
RequestError,
910
} from '../src/request'
1011

@@ -81,6 +82,21 @@ describe('Request', () => {
8182
expect(z).not.toBe(y)
8283
})
8384

85+
it('should be able to safely check if an AbortController has been initialized by referencing the abortControllerKey', async () => {
86+
const req = newRequest({
87+
headers: {
88+
host: 'localhost',
89+
},
90+
rawHeaders: ['host', 'localhost'],
91+
url: '/foo.txt',
92+
} as IncomingMessage)
93+
94+
expect(req[abortControllerKey]).toBeUndefined() // not initialized, do not initialize internal request object automatically
95+
96+
expect(req[getAbortController]()).toBeDefined()
97+
expect(req[abortControllerKey]).toBeDefined() // initialized
98+
})
99+
84100
it('Should throw error if host header contains path', async () => {
85101
expect(() => {
86102
newRequest({

0 commit comments

Comments
 (0)