-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add an abort controller to signal request timeouts
- Loading branch information
1 parent
1c48074
commit 0811780
Showing
9 changed files
with
175 additions
and
22 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,39 @@ | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
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.