diff options
| author | akiyamn | 2023-09-24 23:22:21 +1000 |
|---|---|---|
| committer | akiyamn | 2023-09-24 23:22:21 +1000 |
| commit | 4e87195739f2a5d9a05451b48773c8afdc680765 (patch) | |
| tree | 9cba501844a4a11dcbdffc4050ed8189561c55ed /node_modules/stoppable | |
| download | price-tracker-worker-4e87195739f2a5d9a05451b48773c8afdc680765.tar.gz price-tracker-worker-4e87195739f2a5d9a05451b48773c8afdc680765.zip | |
Initial commit (by create-cloudflare CLI)
Diffstat (limited to 'node_modules/stoppable')
| -rw-r--r-- | node_modules/stoppable/LICENSE | 21 | ||||
| -rw-r--r-- | node_modules/stoppable/lib/stoppable.js | 65 | ||||
| -rw-r--r-- | node_modules/stoppable/package.json | 49 | ||||
| -rw-r--r-- | node_modules/stoppable/readme.md | 127 |
4 files changed, 262 insertions, 0 deletions
diff --git a/node_modules/stoppable/LICENSE b/node_modules/stoppable/LICENSE new file mode 100644 index 0000000..171534d --- /dev/null +++ b/node_modules/stoppable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Hunter Loftis <hunter@hunterloftis.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/stoppable/lib/stoppable.js b/node_modules/stoppable/lib/stoppable.js new file mode 100644 index 0000000..68d526c --- /dev/null +++ b/node_modules/stoppable/lib/stoppable.js @@ -0,0 +1,65 @@ +'use strict' + +const https = require('https') + +module.exports = (server, grace) => { + grace = typeof grace === 'undefined' ? Infinity : grace + const reqsPerSocket = new Map() + let stopped = false + let gracefully = true + + if (server instanceof https.Server) { + server.on('secureConnection', onConnection) + } else { + server.on('connection', onConnection) + } + + server.on('request', onRequest) + server.stop = stop + server._pendingSockets = reqsPerSocket + return server + + function onConnection (socket) { + reqsPerSocket.set(socket, 0) + socket.once('close', () => reqsPerSocket.delete(socket)) + } + + function onRequest (req, res) { + reqsPerSocket.set(req.socket, reqsPerSocket.get(req.socket) + 1) + res.once('finish', () => { + const pending = reqsPerSocket.get(req.socket) - 1 + reqsPerSocket.set(req.socket, pending) + if (stopped && pending === 0) { + req.socket.end() + } + }) + } + + function stop (callback) { + // allow request handlers to update state before we act on that state + setImmediate(() => { + stopped = true + if (grace < Infinity) { + setTimeout(destroyAll, grace).unref() + } + server.close(e => { + if (callback) { + callback(e, gracefully) + } + }) + reqsPerSocket.forEach(endIfIdle) + }) + } + + function endIfIdle (requests, socket) { + if (requests === 0) socket.end() + } + + function destroyAll () { + gracefully = false + reqsPerSocket.forEach((reqs, socket) => socket.end()) + setImmediate(() => { + reqsPerSocket.forEach((reqs, socket) => socket.destroy()) + }) + } +} diff --git a/node_modules/stoppable/package.json b/node_modules/stoppable/package.json new file mode 100644 index 0000000..701b6b7 --- /dev/null +++ b/node_modules/stoppable/package.json @@ -0,0 +1,49 @@ +{ + "name": "stoppable", + "version": "1.1.0", + "engines": { + "node": ">=4", + "npm": ">=6" + }, + "keywords": [ + "server", + "net", + "connect", + "socket", + "connection", + "stop", + "close", + "disconnect", + "disconnection", + "http", + "https", + "tcp" + ], + "files": [ + "lib" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/hunterloftis/stoppable.git" + }, + "homepage": "https://github.com/hunterloftis/stoppable", + "scripts": { + "lint": "standard --fix \"lib/**/*.js\" \"test/**/*.js\"", + "spec": "nyc --check-coverage mocha --bail \"test/*.test.js\"", + "test": "npm run lint && npm audit && npm run spec", + "coverage": "nyc mocha --bail \"test/*.test.js\"", + "perf:baseline": "node test/performance.js & sleep 2 && artillery quick -d 10 -r 1000 -o /dev/null -k http://localhost:8000", + "perf:stoppable": "node test/performance.js 1 & sleep 2 && artillery quick -d 10 -r 1000 -o /dev/null -k http://localhost:8000" + }, + "main": "lib/stoppable.js", + "license": "MIT", + "devDependencies": { + "artillery": "^1.6.0-15", + "awaiting": "^3.0.0", + "chai": "^4.1.2", + "mocha": "^5.0.5", + "nyc": "^11.6.0", + "requisition": "^1.7.0", + "standard": "^11.0.1" + } +} diff --git a/node_modules/stoppable/readme.md b/node_modules/stoppable/readme.md new file mode 100644 index 0000000..d6f1d62 --- /dev/null +++ b/node_modules/stoppable/readme.md @@ -0,0 +1,127 @@ +# Stoppable + +[](https://travis-ci.org/hunterloftis/stoppable) + +> Node's `server.close()` the way you probably [expected it to work by default](https://github.com/nodejs/node/issues/2642). + +## Summary + +```js +const server = stoppable(http.createServer(handler)) +server.stop() +``` + +Stoppable stops accepting new connections and closes existing, idle connections (including keep-alives) +without killing requests that are in-flight. + +## Requirements + +- Node.js v6+ + +Node.js v4.x is *unofficially* supported. + +## Installation + +```bash +yarn add stoppable +``` + +(or use npm) + +## Usage + +**constructor** + +```js +stoppable(server, grace) +``` + +Decorates the server instance with a `stop` method. +Returns the server instance, so can be chained, or can be run as a standalone statement. + +- server: Any HTTP or HTTPS Server instance +- grace: Milliseconds to wait before force-closing connections + +`grace` defaults to Infinity (don't force-close). +If you want to immediately kill all sockets you can use a grace of 0. + +**stop()** + +```js +server.stop(callback) +``` + +Closes the server. + +- callback: passed along to the existing `server.close` function to auto-register a 'close' event. +The first agrument is an error, and the second argument is a boolean that indicates whether it stopped gracefully. + +## Design decisions + +- Monkey patching generally sucks, but in this case it's the nicest API. Let's call it "decorating." +- `grace` could be specified on `stop`, but it's better to match the existing `server.close` API. +- Clients should be handled respectfully, so we aren't just destroying sockets, we're sending `FIN` packets first. +- Any solution to this problem requires bookkeeping on every connection and request/response. +We're doing a minimum of work on these "hot" code paths and delaying as much as possible to the actual `stop` method. + +## Performance + +There's no way to provide this functionality without bookkeeping on connection, disconnection, request, and response. +However, Stoppable strives to do minimal work in hot code paths and to use optimal data structures. + +I'd be interested to see real-world performance benchmarks; +the simple loopback artillery benchmark included in the lib shows very little overhead from using a stoppable server: + +### Without Stoppable + +```plain + Scenarios launched: 10000 + Scenarios completed: 10000 + Requests completed: 10000 + RPS sent: 939.85 + Request latency: + min: 0.5 + max: 51.3 + median: 2.1 + p95: 3.7 + p99: 15.3 + Scenario duration: + min: 1 + max: 60.7 + median: 3.6 + p95: 7.6 + p99: 19 + Scenario counts: + 0: 10000 (100%) + Codes: + 200: 10000 +``` + +### With Stoppable + +```plain + Scenarios launched: 10000 + Scenarios completed: 10000 + Requests completed: 10000 + RPS sent: 940.73 + Request latency: + min: 0.5 + max: 43.4 + median: 2.1 + p95: 3.8 + p99: 15.5 + Scenario duration: + min: 1.1 + max: 57 + median: 3.7 + p95: 8 + p99: 19.4 + Scenario counts: + 0: 10000 (100%) + Codes: + 200: 10000 +``` + +## License + +MIT
\ No newline at end of file |
