-
Notifications
You must be signed in to change notification settings - Fork 160
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: AbortController to signal request timeouts
This commit adds a nodejs AbortController to the HTTP request object that is used to signal cancellation for a function execution in the event of a timeout or client disconnect.
- Loading branch information
1 parent
1c48074
commit b04f564
Showing
10 changed files
with
189 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import {Request, Response} from '../functions'; | ||
import {NextFunction} from 'express'; | ||
|
||
export const timeoutMiddleware = (timeoutMilliseconds: number) => { | ||
return (req: Request, res: Response, next: NextFunction) => { | ||
// In modern versions of Node.js that support the AbortController API we add one to | ||
// signal function timeout. | ||
if (timeoutMilliseconds > 0 && AbortController) { | ||
req.abortController = new AbortController(); | ||
req.setTimeout(timeoutMilliseconds); | ||
let executionComplete = false; | ||
res.on('timeout', () => { | ||
// This event is triggered when the underlying socket times out due to inactivity. | ||
if (!executionComplete) { | ||
executionComplete = true; | ||
req.abortController?.abort('timeout'); | ||
} | ||
}); | ||
req.on('close', () => { | ||
// This event is triggered when the underlying HTTP connection is closed. This can | ||
// happen if the data plane times out the request, the client disconnects or the | ||
// response is complete. | ||
if (!executionComplete) { | ||
executionComplete = true; | ||
req.abortController?.abort('request closed'); | ||
} | ||
}); | ||
req.on('end', () => { | ||
// This event is triggered when the function execution completes and we | ||
// write an HTTP response. | ||
executionComplete = true; | ||
}); | ||
} | ||
// Always call next to continue middleware processing. | ||
next(); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import * as assert from 'assert'; | ||
import * as sinon from 'sinon'; | ||
import {NextFunction} from 'express'; | ||
import {Request, Response} from '../../src/functions'; | ||
|
||
import {timeoutMiddleware} from '../../src/middleware/timeout'; | ||
|
||
describe('timeoutMiddleware', () => { | ||
let request: Request; | ||
let response: Response; | ||
let next: NextFunction; | ||
beforeEach(() => { | ||
request = { | ||
setTimeout: sinon.spy(), | ||
on: sinon.spy(), | ||
} as unknown as Request; | ||
response = { | ||
on: sinon.spy(), | ||
} as unknown as Response; | ||
next = sinon.spy(); | ||
}); | ||
|
||
it('calls the next function', () => { | ||
const middleware = timeoutMiddleware(1000); | ||
middleware(request, response, next); | ||
assert.strictEqual((next as sinon.SinonSpy).called, true); | ||
}); | ||
|
||
it('adds an abort controller to the request', () => { | ||
const middleware = timeoutMiddleware(1000); | ||
middleware(request, response, next); | ||
assert.strictEqual(!!request.abortController, true); | ||
}); | ||
|
||
it('adds an abort controller to the request', () => { | ||
const middleware = timeoutMiddleware(1000); | ||
middleware(request, response, next); | ||
assert.strictEqual(!!request.abortController, true); | ||
}); | ||
|
||
it('sets the request timeout', () => { | ||
const middleware = timeoutMiddleware(1000); | ||
middleware(request, response, next); | ||
assert.strictEqual( | ||
(request.setTimeout as sinon.SinonSpy).calledWith(1000), | ||
true | ||
); | ||
}); | ||
}); |
Oops, something went wrong.