Repository: davidmarkclements/overload-protection
Branch: master
Commit: 340e777990e3
Files: 33
Total size: 101.2 KB
Directory structure:
gitextract_e7w9qjtr/
├── .gitignore
├── .travis.yml
├── LICENSE
├── benchmarks/
│ ├── express/
│ │ ├── excluded.js
│ │ ├── included.js
│ │ └── index.js
│ ├── http/
│ │ ├── excluded.js
│ │ ├── included.js
│ │ └── index.js
│ ├── koa/
│ │ ├── excluded.js
│ │ ├── included.js
│ │ └── index.js
│ └── restify/
│ ├── excluded.js
│ ├── included.js
│ └── index.js
├── demos/
│ ├── express/
│ │ └── index.js
│ ├── http/
│ │ └── index.js
│ ├── koa/
│ │ └── index.js
│ └── restify/
│ └── index.js
├── index.js
├── lib/
│ ├── explain.js
│ ├── express.js
│ ├── http.js
│ ├── koa.js
│ ├── restify.js
│ └── stats.js
├── package.json
├── readme.md
└── test/
├── index.js
└── integration/
├── express/
│ └── index.js
├── http/
│ └── index.js
├── koa/
│ └── index.js
└── restify/
└── index.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io/api/node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# End of https://www.gitignore.io/api/node
================================================
FILE: .travis.yml
================================================
language: node_js
sudo: false
node_js:
- '8'
- '10'
- '12'
script:
- npm run ci
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2017 David Mark Clements
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.
================================================
FILE: benchmarks/express/excluded.js
================================================
'use strict'
var app = require('express')()
app.get('/', function (req, res) {
res.send('content')
})
module.exports = app
================================================
FILE: benchmarks/express/included.js
================================================
'use strict'
var app = require('express')()
var protect = require('../..')('express')
app.use(protect)
app.get('/', function (req, res) {
res.send('content')
})
module.exports = app
================================================
FILE: benchmarks/express/index.js
================================================
'use strict'
var autocannon = require('autocannon')
var included = require('./included')
var excluded = require('./excluded')
console.log('express with overload protection:')
included = included.listen(3000)
var instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
included.close()
console.log('\nexpress without overload protection:')
excluded = excluded.listen(3000)
instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
excluded.close()
})
autocannon.track(instance, {renderProgressBar: true})
})
// just render results
autocannon.track(instance, {renderProgressBar: true})
================================================
FILE: benchmarks/http/excluded.js
================================================
'use strict'
var http = require('http')
var server = http.createServer(serve)
function serve (req, res) {
res.end('content')
}
module.exports = server
================================================
FILE: benchmarks/http/included.js
================================================
'use strict'
var http = require('http')
var server = http.createServer(serve)
var protect = require('../..')('http')
function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
}
module.exports = server
================================================
FILE: benchmarks/http/index.js
================================================
'use strict'
var autocannon = require('autocannon')
var included = require('./included')
var excluded = require('./excluded')
console.log('http with overload protection:')
included.listen(3000)
var instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
included.close()
console.log('\nhttp without overload protection:')
excluded.listen(3000)
instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
excluded.close()
})
autocannon.track(instance, {renderProgressBar: true})
})
// just render results
autocannon.track(instance, {renderProgressBar: true})
================================================
FILE: benchmarks/koa/excluded.js
================================================
'use strict'
var Koa = require('koa')
var Router = require('koa-router')
var router = new Router()
var app = new Koa()
router.get('/', async function (ctx) {
ctx.body = 'content'
})
app.use(router.routes())
module.exports = app
================================================
FILE: benchmarks/koa/included.js
================================================
'use strict'
var Koa = require('koa')
var Router = require('koa-router')
var protect = require('../..')('koa')
var router = new Router()
var app = new Koa()
app.use(protect)
router.get('/', async function (ctx) {
ctx.body = 'content'
})
app.use(router.routes())
module.exports = app
================================================
FILE: benchmarks/koa/index.js
================================================
'use strict'
var autocannon = require('autocannon')
var included = require('./included')
var excluded = require('./excluded')
console.log('koa with overload protection:')
included = included.listen(3000)
var instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
included.close()
console.log('\nkoa without overload protection:')
excluded = excluded.listen(3000)
instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
excluded.close()
})
autocannon.track(instance, {renderProgressBar: true})
})
// just render results
autocannon.track(instance, {renderProgressBar: true})
================================================
FILE: benchmarks/restify/excluded.js
================================================
'use strict'
var app = require('restify').createServer({
name: 'myapp',
version: '1.0.0'
})
app.get('/', function (req, res) {
res.send('content')
})
module.exports = app
================================================
FILE: benchmarks/restify/included.js
================================================
'use strict'
var app = require('restify').createServer({
name: 'myapp',
version: '1.0.0'
})
var protect = require('../..')('restify')
app.use(protect)
app.get('/', function (req, res) {
res.send('content')
})
module.exports = app
================================================
FILE: benchmarks/restify/index.js
================================================
'use strict'
var autocannon = require('autocannon')
var included = require('./included')
var excluded = require('./excluded')
console.log('restify with overload protection:')
included = included.listen(3000)
var instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
included.close()
console.log('\nrestify without overload protection:')
excluded = excluded.listen(3000)
instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
pipelining: 1,
duration: 10
}, function () {
instance.stop()
excluded.close()
})
autocannon.track(instance, {renderProgressBar: true})
})
// just render results
autocannon.track(instance, {renderProgressBar: true})
================================================
FILE: demos/express/index.js
================================================
'use strict'
var app = require('express')()
var protect = require('../..')('express')
app.use(protect)
app.get('/', function (req, res) {
res.send('content')
})
app.listen(3000, function () {
var req = require('http').get('http://localhost:3000')
req.on('response', function (res) {
console.log('got status code', res.statusCode)
console.log('retry after', res.headers['retry-after'])
setTimeout(function () {
console.log('protect.overload after load', protect.overload)
var req = require('http').get('http://localhost:3000')
req.on('response', function (res) {
console.log('got status code', res.statusCode)
protect.stop()
process.exit()
}).end()
}, parseInt(res.headers['retry-after'], 10))
}).end()
setImmediate(function () {
console.log('eventLoopDelay after active sleeping', protect.eventLoopDelay)
})
sleep(500)
})
function sleep (msec) {
var start = Date.now()
while (Date.now() - start < msec) {}
}
================================================
FILE: demos/http/index.js
================================================
'use strict'
var http = require('http')
var server = http.createServer(serve)
var protect = require('../..')('http')
function sleep (msec) {
var start = Date.now()
while (Date.now() - start < msec) {}
}
function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
}
server.listen(0, function () {
var req = http.get(server.address())
req.on('response', function (res) {
console.log('got status code', res.statusCode)
console.log('retry after', res.headers['retry-after'])
setTimeout(function () {
console.log('protect.overload after load', protect.overload)
var req = http.get(server.address())
req.on('response', function (res) {
console.log('got status code', res.statusCode)
protect.stop()
server.close()
}).end()
}, parseInt(res.headers['retry-after'], 10))
}).end()
setImmediate(function () {
console.log('eventLoopDelay after active sleeping', protect.eventLoopDelay)
})
sleep(500)
})
================================================
FILE: demos/koa/index.js
================================================
'use strict'
var Koa = require('koa')
var Router = require('koa-router')
var protect = require('../..')('koa')
var router = new Router()
var app = new Koa()
app.use(protect)
router.get('/', async function (ctx) {
ctx.body = 'content'
})
app.use(router.routes())
app.listen(3000, function () {
var req = require('http').get('http://localhost:3000')
req.on('response', function (res) {
console.log('got status code', res.statusCode)
console.log('retry after', res.headers['retry-after'])
setTimeout(function () {
console.log('protect.overload after load', protect.overload)
var req = require('http').get('http://localhost:3000')
req.on('response', function (res) {
console.log('got status code', res.statusCode)
protect.stop()
process.exit()
}).end()
}, parseInt(res.headers['retry-after'], 10))
}).end()
setImmediate(function () {
console.log('eventLoopDelay after active sleeping', protect.eventLoopDelay)
})
sleep(500)
})
function sleep (msec) {
var start = Date.now()
while (Date.now() - start < msec) {}
}
================================================
FILE: demos/restify/index.js
================================================
'use strict'
var app = require('restify').createServer({
name: 'myapp',
version: '1.0.0'
})
var protect = require('../..')('restify')
app.use(protect)
app.get('/', function (req, res) {
res.send('content')
})
app.listen(3000, function () {
var req = require('http').get('http://localhost:3000')
req.on('response', function (res) {
console.log('got status code', res.statusCode)
console.log('retry after', res.headers['retry-after'])
setTimeout(function () {
console.log('protect.overload after load', protect.overload)
var req = require('http').get('http://localhost:3000')
req.on('response', function (res) {
console.log('got status code', res.statusCode)
protect.stop()
process.exit()
}).end()
}, parseInt(res.headers['retry-after'], 10))
}).end()
setImmediate(function () {
console.log('eventLoopDelay after active sleeping', protect.eventLoopDelay)
})
sleep(500)
})
function sleep (msec) {
var start = Date.now()
while (Date.now() - start < msec) {}
}
================================================
FILE: index.js
================================================
'use strict'
var loopbench = require('loopbench')
var frameworks = {
express: require('./lib/express'),
http: require('./lib/http'),
koa: require('./lib/koa'),
restify: require('./lib/restify')
}
var defaults = {
production: process.env.NODE_ENV === 'production',
errorPropagationMode: false,
clientRetrySecs: 1,
sampleInterval: 5,
maxEventLoopDelay: 42,
maxHeapUsedBytes: 0,
maxRssBytes: 0,
logging: false,
logStatsOnReq: false
}
function protect (framework, opts) {
opts = Object.assign({}, defaults, opts)
if (typeof framework === 'undefined') {
throw Error('Please specify a framework')
}
if (!(framework in frameworks)) {
throw Error(opts.integrate + ' not supported.')
}
if (opts.maxRssBytes <= 0 && opts.maxHeapUsedBytes <= 0 && opts.eventLoopDelay <= 0) {
throw Error('At least one threshold (eventLoopDelay, maxHeapUsedBytes, maxRssBytes) should be above 0')
}
if (opts.logStatsOnReq && opts.logging === false) {
throw Error('logStatsOnReq cannot be enabled unless logging is also enabled')
}
var update = (opts.maxEventLoopDelay > 0)
? function update () {
profiler.eventLoopOverload = eventLoopProfiler.overLimit
profiler.eventLoopDelay = eventLoopProfiler.delay
profiler.overload = profiler.eventLoopOverload ||
profiler.heapUsedOverload ||
profiler.rssOverload
}
: function update () {
profiler.overload = profiler.heapUsedOverload || profiler.rssOverload
}
if (opts.maxEventLoopDelay > 0) {
var eventLoopProfiler = loopbench({
sampleInterval: opts.sampleInterval,
limit: opts.maxEventLoopDelay
})
eventLoopProfiler.on('load', update)
eventLoopProfiler.on('unload', update)
}
var maxHeapUsedBytes = opts.maxHeapUsedBytes
var maxRssBytes = opts.maxRssBytes
var timer = (maxHeapUsedBytes > 0 || maxRssBytes > 0) &&
setInterval(checkMemory, opts.sampleInterval).unref()
var profiler = {
overload: false,
eventLoopOverload: false,
heapUsedOverload: false,
rssOverload: false,
eventLoopDelay: 0,
maxEventLoopDelay: opts.maxEventLoopDelay,
maxHeapUsedBytes: opts.maxHeapUsedBytes,
maxRssBytes: opts.maxRssBytes,
stop: stop
}
var integrate = frameworks[framework](opts, profiler)
if (Object.setPrototypeOf) {
Object.setPrototypeOf(profiler, Function.prototype)
Object.setPrototypeOf(integrate, profiler)
} else {
profiler.__proto__ = Function.prototype // eslint-disable-line
integrate.__proto__ = profiler // eslint-disable-line
}
return integrate
function checkMemory () {
var mem = process.memoryUsage()
var heapUsed = mem.heapUsed
var rss = mem.rss
profiler.heapUsedOverload = (maxHeapUsedBytes > 0 && heapUsed > maxHeapUsedBytes)
profiler.rssOverload = (maxRssBytes > 0 && rss > maxRssBytes)
update()
}
function stop () {
if (eventLoopProfiler) eventLoopProfiler.stop()
clearInterval(timer)
}
}
module.exports = protect
================================================
FILE: lib/explain.js
================================================
'use strict'
module.exports = explain
function explain (protect) {
var explanation = 'Server experiencing heavy load: ('
var c = 0
if (protect.eventLoopOverload === true) {
c += 1
explanation += 'event loop'
}
if (protect.heapUsedOverload === true) {
explanation += c > 0 ? ', heap' : 'heap'
c += 1
}
if (protect.rssOverload === true) {
explanation += c > 0 ? ', rss' : 'rss'
}
explanation += ')'
return explanation
}
================================================
FILE: lib/express.js
================================================
'use strict'
module.exports = require('./http')
================================================
FILE: lib/http.js
================================================
'use strict'
var explain = require('./explain')
var OverloadProtectionStats = require('./stats')
module.exports = http
function http (opts, protect) {
var clientRetrySecs = opts.clientRetrySecs
var sendRetryHeader = clientRetrySecs > 0
var logStatsOnReq = opts.logStatsOnReq
var logging = opts.logging
var loggingOn = typeof logging === 'string' || typeof logging === 'function'
var log4jLogging = typeof logging === 'string'
var errorPropagationMode = opts.errorPropagationMode
var production = opts.production
var expose = !production
return overloadProtection
function overloadProtection (req, res, next) {
if (logStatsOnReq) {
const stats = new OverloadProtectionStats(
protect.overload,
protect.eventLoopOverload,
protect.heapUsedOverload,
protect.rssOverload,
protect.eventLoopDelay,
protect.maxEventLoopDelay,
protect.maxHeapUsedBytes,
protect.maxRssBytes
)
if (log4jLogging) req.log && req.log[logging] && req.log[logging](stats)
else logging(stats)
}
if (protect.overload === true) {
res.statusCode = 503
if (sendRetryHeader) res.setHeader('Retry-After', clientRetrySecs)
if (loggingOn) {
if (log4jLogging) req.log && req.log[logging] && req.log[logging](explain(protect))
else logging(explain(protect))
}
if (errorPropagationMode && typeof next === 'function') {
var err = Error(explain(protect))
err.expose = expose
err.statusCode = 503
next(err)
return
}
res.end(production ? 'Service Unavailable' : explain(protect))
if (arguments.length < 3) return true
else return
}
if (arguments.length >= 3 && typeof next === 'function') next()
else return false
}
}
================================================
FILE: lib/koa.js
================================================
'use strict'
module.exports = koa
var explain = require('./explain')
var OverloadProtectionStats = require('./stats')
function koa (opts, protect) {
var clientRetrySecs = opts.clientRetrySecs
var sendRetryHeader = clientRetrySecs > 0
var logStatsOnReq = opts.logStatsOnReq
var logging = opts.logging
var loggingOn = typeof logging === 'string' || typeof logging === 'function'
var log4jLogging = typeof logging === 'string'
var errorPropagationMode = opts.errorPropagationMode
var production = opts.production
var expose = !production
return overloadProtection
function overloadProtection (ctx, next) {
if (logStatsOnReq) {
const stats = new OverloadProtectionStats(
protect.overload,
protect.eventLoopOverload,
protect.heapUsedOverload,
protect.rssOverload,
protect.eventLoopDelay,
protect.maxEventLoopDelay,
protect.maxHeapUsedBytes,
protect.maxRssBytes
)
if (log4jLogging) ctx.log && ctx.log[logging] && ctx.log[logging](stats)
else logging(stats)
}
if (protect.overload === true) {
if (sendRetryHeader) ctx.set('Retry-After', clientRetrySecs)
if (loggingOn) {
if (log4jLogging) ctx.log && ctx.log[logging] && ctx.log[logging](explain(protect))
else logging(explain(protect))
}
if (errorPropagationMode) {
var err = Error(explain(protect))
err.status = 503
err.expose = expose
// if exposing to client in dev,
// we also want to output
// the error in console
if (err.expose) {
ctx.app.emit('error', Error(explain(protect)), ctx)
}
throw err
}
ctx.status = 503
ctx.res.end(production ? 'Service Unavailable' : explain(protect))
return
}
return next()
}
}
================================================
FILE: lib/restify.js
================================================
'use strict'
module.exports = require('./http')
================================================
FILE: lib/stats.js
================================================
function OverloadProtectionStats (overload, eventLoopOverload, heapUsedOverload, rssOverload, eventLoopDelay, maxEventLoopDelay, maxHeapUsedBytes, maxRssBytes) {
this.overload = overload
this.eventLoopOverload = eventLoopOverload
this.heapUsedOverload = heapUsedOverload
this.rssOverload = rssOverload
this.eventLoopDelay = eventLoopDelay
this.maxEventLoopDelay = maxEventLoopDelay
this.maxHeapUsedBytes = maxHeapUsedBytes
this.maxRssBytes = maxRssBytes
}
module.exports = OverloadProtectionStats
================================================
FILE: package.json
================================================
{
"name": "overload-protection",
"version": "1.2.3",
"main": "index.js",
"scripts": {
"test": "tap test",
"lint": "standard",
"ci": "npm run lint && npm run cov",
"cov": "tap --cov test",
"covr": "tap --coverage-report=html test",
"benchmarks": "ls benchmarks | while read l; do node benchmarks/$l; done"
},
"pre-commit": [
"test",
"lint"
],
"keywords": [
"load shedding",
"overload protection",
"production monitoring",
"503",
"Service Unavailable",
"Server Unavailable",
"HTTP 503",
"heavy load",
"load",
"protection",
"shedding",
"express",
"fastify",
"http",
"koa",
"restify"
],
"author": "David Mark Clements <huperekchuno@googlemail.com>",
"license": "MIT",
"devDependencies": {
"autocannon": "^4.4.1",
"express": "^4.17.1",
"koa": "^2.11.0",
"koa-router": "^7.4.0",
"pre-commit": "^1.2.2",
"restify": "^8.5.1",
"standard": "^10.0.3",
"tap": "^10.7.2"
},
"dependencies": {
"loopbench": "^1.2.0"
},
"directories": {
"lib": "lib",
"test": "test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/davidmarkclements/overload-protection.git"
},
"bugs": {
"url": "https://github.com/davidmarkclements/overload-protection/issues"
},
"homepage": "https://github.com/davidmarkclements/overload-protection#readme",
"description": "Load detection and shedding capabilities for http, express, restify and koa"
}
================================================
FILE: readme.md
================================================
# overload-protection
Load detection and shedding capabilities for http, express, restify, and koa
[](https://travis-ci.org/davidmarkclements/overload-protection)
[](https://coveralls.io/github/davidmarkclements/overload-protection)
[](https://standardjs.com)
## About
`overload-protection` provides integration for your framework of choice.
If a threshold is crossed for a given metric, `overload-protection`
will send an HTTP 503 Service Unavailable response, with (by default)
a `Retry-After` header, instructing the client (e.g. a browser or load balancer) to
retry after a given amount of seconds.
Current supported metrics are:
* event loop delay (is the JavaScript thread blocking too long)
* Used Heap Memory
* Total Resident Set Size
For a great explanation of Used Heap Memory vs Resident Set Size see
Daniel Khans article at <https://www.dynatrace.com/blog/understanding-garbage-collection-and-hunting-memory-leaks-in-node-js>
## Usage
Create a config object for your thresholds (and other `overload-protection`)
options.
```js
const protectCfg = {
production: process.env.NODE_ENV === 'production', // if production is false, detailed error messages are exposed to the client
clientRetrySecs: 1, // Retry-After header, in seconds (0 to disable) [default 1]
sampleInterval: 5, // sample rate, milliseconds [default 5]
maxEventLoopDelay: 42, // maximum detected delay between event loop ticks [default 42]
maxHeapUsedBytes: 0, // maximum heap used threshold (0 to disable) [default 0]
maxRssBytes: 0, // maximum rss size threshold (0 to disable) [default 0]
errorPropagationMode: false, // dictate behavior: take over the response
// or propagate an error to the framework [default false]
logging: false, // set to string for log level or function to pass data to
logStatsOnReq: false // set to true to log stats on every requests
}
```
Then pass the framework we're integrating with along with the configuration object.
For instance with Express we would do:
```js
const app = require('express')()
const protect = require('overload-protection')('express', protectCfg)
app.use(protect)
```
With middleware based frameworks, always put the `overload-protection` middleware
first. In default mode this means `overload-protection` will take over the response
and prevent any other middleware from executing (thus taking further potential pressure off
of the process).
Restify, and Koa all work in much the same way, call the `overload-protection`
module with the name of the framework, a config object and pass the resulting `protect`
instance to `app.use` – e.g. Koa would be:
```js
const Koa = require('koa')
const protect = require('overload-protection')('koa', protectCfg)
const app = new Koa()
app.use(protect)
```
For pure core HTTP the `overload-protection` instance can be called
at the top of the request handler function. With two arguments (just `req` and `res`)
the function will return `true` if protection/shedding has been provided, or `false`
if not. If `overload-protection` *has* taken over (the `true` case), then we should
exit the function and do no further work:
```js
const http = require('http')
const protect = require('overload-protection')('http', protectCfg)
http.createServer(function (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
```
With three arguments (the third argument being a callback), the rest of the
work should be done within the supplied callback.
```js
const http = require('http')
const protect = require('overload-protection')('http', protectCfg)
http.createServer(function (req, res) {
protect(req, res, function () {
// when errorPropagationMode mode is false, will *only*
// be called if load shedding didn't occur
// (if it was true we'd need to check for an Error object as first arg)
res.end('content')
})
})
```
## Installation
```sh
npm install overload-protection --save
```
## Tests
```sh
npm install
npm test
```
## Benchmark
The overhead of using `overload-protection` is minimal, run the benchmarks to conduct
comparative profiling of using `overload-protection` versus not using it for each supported framework.
```sh
npm run benchmarks
```
## API
### require('overload-protection') => (framework, opts) => instance
The `framework` argument is non-optional. It's a string and may be one of:
* express
* koa
* restify
* http
The `opts` argument is optional, as are all properties.
Options (particularly thresholds) are quite sensitive and highly relevant on
a case by case basis. Possible options are as follows:
#### production: process.env.NODE_ENV === 'production'
The `production` option determines whether the client receives an error message
detailing the surpassed threshold(s). (It may also be used in future for other such
good practices or performance trade-offs).
#### clientRetrySecs: 1
By default, `overload-protection` will add a header to the 503 response
called `Retry-After`. It's up to the client to honour this header, which
instructs the client on how many seconds to wait between retries.
Defaults to 1 seconds.
#### sampleInterval: 5
In order to establish whether a threshold has been crossed, the metrics
are sampled at a regular interval. The interval defaults to 5 milliseconds.
#### maxEventLoopDelay: 42
Synchronous work causes the event loop to freeze, when this happens
an interval timer (which is our sampler) will be delayed by the amount
of time the event loop was stalled for while the thread processed synchronous
work. We can measure this with timestamp comparison. This option sets a threshold
for the maximum amount of stalling between intervals we'll accept before our
service begins responding with 503 codes to requests. Defaults to 42 milliseconds.
When set to 0 this threshold will be disabled.
#### maxHeapUsedBytes: 0
Disabled by default (set to 0), this defines maximum V8 (Node's JavaScript engine) used heap size.
If the Used Heap size exceeds the threshold the server will begin return 503 error codes
until it crosses back under the threshold.
See <https://www.dynatrace.com/blog/understanding-garbage-collection-and-hunting-memory-leaks-in-node-js>
for more info on Used Heap from a V8 context.
#### maxRssBytes: 0
Disabled by default (set to 0) maximum process Resident Set Size. If
the RSS exceeds the threshold the server will begin return 503 error codes
until it crosses back under the threshold.
#### errorPropagationMode: false
**This is relevant to middleware integration only**
By default, `overload-protection` will handle and end the response,
without calling any subsequent configured middleware. The point here
is to avoid any further processing for an already (by definition)
over loaded process.
However, it could be argued, from a puritanical perspective, that middleware
should defer to the framework and that any HTTP code of 500 or above should
be generated by propagating an error through the framework.
This option prevents `overload-protection` from manually ended the response and
instead generates an `Error` object (with additional properties as per [`http-errors`](https://github.com/jshttp/http-errors) as used by Express and Koa)
and propagates it through the framework (either by throwing it in Koa, or passing through the `next` callback).
#### logging: false
The `logging` option can be set to a string or a function.
If `logging` is set to a string, the string should indicate the desired log
level for notifying that a 503 response was given. When `logging` is a string
a request bound Log4j-style logger is assumed. This means the `req` object (or the `ctx` object in the case of Koa)
should have a `log` object which contains methods corresponding to log levels. So if `logging`
was set to `warn` (`logging: 'warn'`) then `req.log.warn` is expected to be present
and be a function. A number of logging libraries follow this pattern, such as
[`bunyan-express`](http:/npm.im/bunyan-express) and all of the [`pino`](http://npm.im/pino)
middleware loggers ([`express-pino-logger`](http://npm.im/express), [`koa-pino-logger`](http://npm.im/koa-pino-logger),
[`restify-pino-logger`](http://npm.im/restify-pino-logger), [`pino-http`](http://npm.im/pino-http)).
If the application isn't using a request bound Log4j-style logger, the `logging`
option can be set to a function which receives a log message. This function is
then responsible for writing the log. We could also simply set it to one of
the console methods, e.g. `logging: console.warn`.
This is primarily for usage when `errorPropagationMode` is `false`. If `errorPropagationMode`
is set to `true`, we may want to instead log once the error has propagated to a handler.
#### logStatsOnReq: false
Set `logStatsOnReq` to `true` log the profiled stats on every request. In order to use this option, the `logging` option must not be `false`. Bear in mind that using this option will
add extra pressure on the event loop in itself, so use with caution.
### instance.overload
The returned instance (which in many cases is passed as middleware to `app.use`),
has an `overload` property. This begins as `false`. If any of the thresholds have
been passed this will be set to `true`. Once all metrics are below their thresholds
this would become `false` again.
This allows for any heavy load detection required outside of a framework.
### instance.eventLoopOverload
The returned instance (which in many cases is passed as middleware to `app.use`),
has an `eventLoopOverload` property. This begins as `false`. If the `maxEventLoopDelay`
threshold is passed this will be set to `true`. Once it's below the configured threshold
this would become `false` again.
This allows for any event loop delay detection necessary outside of a framework.
### instance.heapUsedOverload
The returned instance (which in many cases is passed as middleware to `app.use`),
has a `heapUsedOverload` property. This begins as `false`. If the `maxHeapUsedBytes`
threshold is passed this will be set to `true`. Once it's below the configured threshold
this would become `false` again.
This allows for any heap used threshold detection necessary outside of a framework.
### instance.rssOverload
The returned instance (which in many cases is passed as middleware to `app.use`),
has a `rssOverload` property. This begins as `false`. If the `maxRssBytes`
threshold is passed this will be set to `true`. Once it's below the configured threshold
this would become `false` again.
This allows for any heap used threshold detection necessary outside of a framework.
### instance.eventLoopDelay
The delay in milliseconds (with additional decimal precision) since the last sample.
If `maxEventLoopDelay` is 0, the event loop is not measured, so `eventLoopDelay` will always
be 0 in that case.
### instance.maxEventLoopDelay
Corresponds to the `opts.maxEventLoopDelay` option.
### instance.maxHeapUsedBytes
Corresponds to the `opts.maxHeapUsedBytes` option.
### instance.maxRssBytes
Corresponds to the `opts.maxRssBytes` option.
## Dependencies
- [loopbench](https://github.com/mcollina/loopbench): Benchmark your event loop
## Dev Dependencies
- [autocannon](https://github.com/mcollina/autocannon): Fast HTTP benchmarking tool written in Node.js
- [express](https://github.com/expressjs/express): Fast, unopinionated, minimalist web framework
- [koa](https://github.com/koajs/koa): Koa web app framework
- [koa-router](https://github.com/alexmingoia/koa-router): Router middleware for koa. Provides RESTful resource routing.
- [pre-commit](https://github.com/observing/pre-commit): Automatically install pre-commit hooks for your npm modules.
- [restify](https://github.com/restify/node-restify): REST framework
- [standard](https://github.com/standard/standard): JavaScript Standard Style
- [tap](https://github.com/tapjs/node-tap): A Test-Anything-Protocol library
## License
MIT
## Acknowledgements
Kindly sponsored by [nearForm](http://nearform.com)
================================================
FILE: test/index.js
================================================
'use strict'
var test = require('tap').test
var protect = require('../')
test('throws if framework is unspecified', function (t) {
t.throws(function () {
protect()
})
t.end()
})
test('throws if framework is not supported', function (t) {
t.throws(function () {
protect('not a thing')
})
t.end()
})
test('throws if all thresholds are disabled (set to 0)', function (t) {
t.throws(function () {
protect('http', {
eventLoopDelay: 0,
maxRssBytes: 0,
maxHeapUsedBytes: 0
})
})
t.end()
})
test('throws if logStatsOnReq is true but logging is false', function (t) {
t.throws(function () {
protect('http', {
logStatsOnReq: true
})
})
t.end()
})
test('instance.stop ceases sampling', function (t) {
var sI = global.setInterval
var cI = global.clearInterval
var mock = {unref: function () { return mock }}
global.setInterval = function () { return mock }
global.clearInterval = function (ref) {
t.is(ref, mock)
global.setInterval = sI
global.clearInterval = cI
t.end()
}
var instance = protect('http')
instance.stop()
})
test('sampleInterval option sets the sample rate', function (t) {
var sI = global.setInterval
var cI = global.clearInterval
var sampleRate = 88
var mock = {unref: function () { return mock }}
global.setInterval = function (fn, n) {
t.is(n, sampleRate)
return mock
}
global.clearInterval = function (ref) {
t.is(ref, mock)
global.setInterval = sI
global.clearInterval = cI
t.end()
}
var instance = protect('http', {
sampleInterval: sampleRate
})
instance.stop()
})
test('exposes maxEventLoopDelay option on instance', function (t) {
var value = 9999
var instance = protect('http', {
maxEventLoopDelay: value
})
t.is(instance.maxEventLoopDelay, value)
instance.stop()
t.end()
})
test('exposes maxHeapUsedBytes option on instance', function (t) {
var value = 9999
var instance = protect('http', {
maxHeapUsedBytes: value
})
t.is(instance.maxHeapUsedBytes, value)
instance.stop()
t.end()
})
test('exposes maxRssBytes option on instance', function (t) {
var value = 9999
var instance = protect('http', {
maxRssBytes: value
})
t.is(instance.maxRssBytes, value)
instance.stop()
t.end()
})
test('instance.eventLoopDelay indicates the delay between samples', function (t) {
var delay = 50
var instance = protect('http')
var start = Date.now()
// "sleep" with while blocking is imprecise, particularly in turbofan,
// throwing in a Buffer.alloc to compensate
while (Date.now() - start <= delay) { Buffer.alloc(1e9) }
setImmediate(function () {
t.is(instance.eventLoopDelay > delay, true)
instance.stop()
t.end()
})
})
test('instance.eventLoopOverload is true when maxEventLoopDelay threshold is breached', function (t) {
var delay = 50
var instance = protect('http', {
sampleInterval: 5,
maxEventLoopDelay: 10
})
var start = Date.now()
while (Date.now() - start < delay) {}
setImmediate(function () {
t.is(instance.eventLoopOverload, true)
instance.stop()
t.end()
})
})
test('instance.eventLoopOverload is false when returning under maxEventLoopDelay threshold', function (t) {
var delay = 50
var instance = protect('http', {
sampleInterval: 5,
maxEventLoopDelay: 10
})
var start = Date.now()
while (Date.now() - start < delay) {}
setImmediate(function () {
setTimeout(function () {
t.is(instance.eventLoopOverload, false)
instance.stop()
t.end()
}, 10)
})
})
test('instance.eventLoopOverload is always false when maxEventLoopDelay is 0 (maxHeapUsedBytes enabled)', function (t) {
var delay = 50
var instance = protect('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxHeapUsedBytes: 10
})
var start = Date.now()
while (Date.now() - start < delay) {}
setImmediate(function () {
t.is(instance.eventLoopOverload, false)
instance.stop()
t.end()
})
})
test('instance.eventLoopOverload is always false when maxEventLoopDelay is 0 (maxRssBytes enabled)', function (t) {
var delay = 50
var instance = protect('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 10
})
var start = Date.now()
while (Date.now() - start < delay) {}
setImmediate(function () {
t.is(instance.eventLoopOverload, false)
instance.stop()
t.end()
})
})
test('instance.overload is true if instance.eventLoopOverload is true', function (t) {
var delay = 50
var instance = protect('http', {
sampleInterval: 5,
maxEventLoopDelay: 1
})
var start = Date.now()
while (Date.now() - start < delay) {}
setImmediate(function () {
t.is(instance.eventLoopOverload, true)
t.is(instance.overload, instance.eventLoopOverload)
instance.stop()
t.end()
})
})
test('instance.heapUsedOverload is true when maxHeapUsedBytes threshold is breached', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxHeapUsedBytes: 10
})
setTimeout(function () {
t.is(instance.heapUsedOverload, true)
process.memoryUsage = memoryUsage
instance.stop()
t.end()
}, 6)
})
test('instance.heapUsedOverload is false when returning under maxHeapUsedBytes threshold', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxHeapUsedBytes: 10
})
setTimeout(function () {
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 2,
external: 99
}
}
setTimeout(function () {
t.is(instance.heapUsedOverload, false)
process.memoryUsage = memoryUsage
instance.stop()
t.end()
}, 6)
}, 6)
})
test('instance.heapUsedOverload is always false when maxHeapUsedBytes is 0', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxHeapUsedBytes: 0
})
setTimeout(function () {
t.is(instance.heapUsedOverload, false)
process.memoryUsage = memoryUsage
instance.stop()
t.end()
}, 6)
})
test('instance.overload is true if instance.heapUsedOverload is true', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxHeapUsedBytes: 10
})
setTimeout(function () {
t.is(instance.heapUsedOverload, true)
t.is(instance.overload, instance.heapUsedOverload)
instance.stop()
process.memoryUsage = memoryUsage
t.end()
}, 6)
})
test('instance.rssOverload is true when maxRssBytes threshold is breached', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxRssBytes: 10
})
setTimeout(function () {
t.is(instance.rssOverload, true)
process.memoryUsage = memoryUsage
instance.stop()
t.end()
}, 6)
})
test('instance.rssOverload is false when returning under maxRssBytes threshold', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxHeapUsedBytes: 10
})
setTimeout(function () {
process.memoryUsage = function () {
return {
rss: 2,
heapTotal: 9999,
heapUsed: 2,
external: 99
}
}
setTimeout(function () {
t.is(instance.rssOverload, false)
instance.stop()
process.memoryUsage = memoryUsage
t.end()
}, 6)
}, 6)
})
test('instance.rssOverload is always false when maxRssBytes is 0', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxRssBytes: 0
})
setTimeout(function () {
t.is(instance.rssOverload, false)
instance.stop()
process.memoryUsage = memoryUsage
t.end()
}, 6)
})
test('instance.overload is true if instance.rssOverload is true', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var instance = protect('http', {
sampleInterval: 5,
maxRssBytes: 10
})
setTimeout(function () {
t.is(instance.rssOverload, true)
t.is(instance.overload, instance.rssOverload)
instance.stop()
process.memoryUsage = memoryUsage
t.end()
}, 6)
})
if (Object.setPrototypeOf) {
test('Supports legacy JS (__proto__)', function (t) {
var setPrototypeOf = Object.setPrototypeOf
delete Object.setPrototypeOf
var instance = protect('http')
// overload wouldn't be in instance if __proto__ wasn't set
t.is('overload' in instance, true)
t.end()
Object.setPrototypeOf = setPrototypeOf
})
}
if (!Object.setPrototypeOf) {
test('Supports modern/future JS (Object.setPrototypeOf)', function (t) {
Object.setPrototypeOf = function (o, proto) {
o.__proto__ = proto // eslint-disable-line
}
var instance = protect('http')
// overload wouldn't be in instance if __proto__ wasn't set
t.is('overload' in instance, true)
t.end()
delete Object.setPrototypeOf
})
}
================================================
FILE: test/integration/express/index.js
================================================
'use strict'
var http = require('http')
var express = require('express')
var protection = require('../../..')
var test = require('tap').test
function sleep (msec) {
var start = Date.now()
while (Date.now() - start < msec) {}
}
test('sends 503 when event loop is overloaded, per maxEventLoopDelay', function (t) {
var protect = protection('express', {
maxEventLoopDelay: 1
})
var app = express()
app.use(protect)
var server = http.createServer(function (req, res) {
sleep(500)
app(req, res)
})
server.listen(3000, function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
protect.stop()
server.close()
t.end()
}).end()
})
})
test('sends 503 when heap used threshold is passed, as per maxHeapUsedBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxHeapUsedBytes: 40
})
var app = express()
app.use(protect)
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends 503 when heap used threshold is passed, as per maxRssBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var app = express()
app.use(protect)
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends Retry-After header as per clientRetrySecs', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 22
})
var app = express()
app.use(protect)
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is(res.headers['retry-after'], '22')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('does not set Retry-After header when clientRetrySecs is 0', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 0
})
var app = express()
app.use(protect)
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is('retry-after' in res.headers, false)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('errorPropagationMode:false (default)', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var app = express()
app.use(protect)
app.use(function () {
t.fail()
})
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('errorPropagationMode:true', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var app = express()
app.use(protect)
app.use(function (err, req, res, next) {
t.ok(err)
t.is(err.statusCode, 503)
res.end('err message')
})
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in default mode, production:false leads to high detail client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var app = express()
app.use(protect)
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in default mode, production:true leads to standard 503 client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var app = express()
app.use(protect)
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Service Unavailable')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in errorPropagationMode production:false sets expose:true on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var app = express()
app.use(protect)
app.use(function (err, req, res, next) {
t.ok(err)
t.is(err.expose, true)
res.end('err message')
})
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in errorPropagationMode production:true sets expose:false on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var app = express()
app.use(protect)
app.use(function (err, req, res, next) {
t.ok(err)
t.is(err.expose, false)
res.end('err message')
})
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('resumes usual operation once load pressure is reduced under threshold', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var app = express()
app.use(protect)
app.get('/', function (req, res) { res.end('content') })
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
process.memoryUsage = function () {
return {
rss: 10,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
setTimeout(function () {
http.get('http://localhost:3000').on('response', function (res) {
t.is(res.statusCode, 200)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}, 6)
}).end()
}, 6)
})
})
test('if logging option is a string, when overloaded, writes log message using req.log as per level in string', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
logging: 'warn'
})
var app = express()
app.use(function (req, res, next) {
req.log = {
warn: function (msg) {
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
}
next()
})
app.use(protect)
app.get('/', function (req, res) { res.end('content') })
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logging option is a function, when overloaded calls the function with heavy load message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('express', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
logging: function (msg) {
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
})
var app = express()
app.use(protect)
app.get('/', function (req, res) { res.end('content') })
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and if logging option is a string, writes log message using req.log as per level in string for every request', function (t) {
var protect = protection('express', {
logging: 'info',
logStatsOnReq: true
})
t.plan(1)
var app = express()
app.use(function (req, res, next) {
req.log = {
info: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
}
next()
})
app.use(protect)
app.get('/', function (req, res) { res.end('content') })
var server = http.createServer(app)
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and logging option is a function, calls the function with stats on every request', function (t) {
var protect = protection('express', {
logStatsOnReq: true,
logging: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
})
var app = express()
app.use(protect)
app.get('/', function (req, res) { res.end('content') })
var server = http.createServer(app)
server.listen(3001, function () {
setTimeout(function () {
http.get('http://localhost:3001').end()
}, 6)
})
})
================================================
FILE: test/integration/http/index.js
================================================
'use strict'
var http = require('http')
var protection = require('../../..')
var test = require('tap').test
function sleep (msec) {
var start = Date.now()
while (Date.now() - start < msec) {}
}
test('sends 503 when event loop is overloaded, per maxEventLoopDelay', function (t) {
var protect = protection('http', {
maxEventLoopDelay: 1
})
var server = http.createServer(function serve (req, res) {
sleep(500)
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
protect.stop()
server.close()
t.end()
}).end()
})
})
test('sends 503 when heap used threshold is passed, as per maxHeapUsedBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxHeapUsedBytes: 40
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends 503 when heap used threshold is passed, as per maxRssBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends Retry-After header as per clientRetrySecs', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 22
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is(res.headers['retry-after'], '22')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('does not set Retry-After header when clientRetrySecs is 0', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 0
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is('retry-after' in res.headers, false)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('callback api with errorPropagationMode false (default)', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var server = http.createServer(function serve (req, res) {
protect(req, res, function () {
t.fail() // should never be called
res.end('content')
})
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('callback api with errorPropagationMode true', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var server = http.createServer(function serve (req, res) {
protect(req, res, function (err) {
t.ok(err)
t.is(err.statusCode, 503)
res.end('err message')
})
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in default mode, production:false leads to high detail client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in default mode, production:true leads to standard 503 client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Service Unavailable')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in errorPropagationMode production:false sets expose:true on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var server = http.createServer(function serve (req, res) {
protect(req, res, function (err) {
t.ok(err)
t.is(err.expose, true)
res.end('err message')
})
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in errorPropagationMode production:true sets expose:false on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var server = http.createServer(function serve (req, res) {
protect(req, res, function (err) {
t.ok(err)
t.is(err.expose, false)
res.end('err message')
})
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('resumes usual operation once load pressure is reduced under threshold', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
process.memoryUsage = function () {
return {
rss: 10,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
setTimeout(function () {
http.get('http://localhost:3000').on('response', function (res) {
t.is(res.statusCode, 200)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}, 6)
}).end()
}, 6)
})
})
test('(callback api) resumes usual operation once load pressure is reduced under threshold', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var server = http.createServer(function serve (req, res) {
protect(req, res, function () {
res.end('content')
})
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
process.memoryUsage = function () {
return {
rss: 10,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
setTimeout(function () {
http.get('http://localhost:3000').on('response', function (res) {
t.is(res.statusCode, 200)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}, 6)
}).end()
}, 6)
})
})
test('if logging option is a string, when overloaded, writes log message using req.log as per level in string', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 1,
maxRssBytes: 40,
maxHeapUsedBytes: 40,
logging: 'warn'
})
var server = http.createServer(function serve (req, res) {
req = {
log: {
warn: function (msg) {
t.is(msg, 'Server experiencing heavy load: (event loop, heap, rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
}
}
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
sleep(500)
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logging option is a function, when overloaded calls the function with heavy load message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('http', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
logging: function (msg) {
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and if logging option is a string, writes log message using req.log as per level in string for every request', function (t) {
var protect = protection('http', {
logging: 'info',
logStatsOnReq: true
})
t.plan(1)
var server = http.createServer(function serve (req, res) {
req = {
log: {
info: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
}
}
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and logging option is a function, calls the function with stats on every request', function (t) {
var protect = protection('http', {
logStatsOnReq: true,
logging: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
})
var server = http.createServer(function serve (req, res) {
if (protect(req, res) === true) return
res.end('content')
})
server.listen(3002, function () {
setTimeout(function () {
http.get('http://localhost:3002').end()
}, 6)
})
})
================================================
FILE: test/integration/koa/index.js
================================================
'use strict'
var http = require('http')
var Koa = require('koa')
var Router = require('koa-router')
var protection = require('../../..')
var test = require('tap').test
function block (n) {
while (n--) { JSON.parse(JSON.stringify(require('../../../package.json'))) }
}
test('sends 503 when event loop is overloaded, per maxEventLoopDelay', function (t) {
var protect = protection('koa', {
maxEventLoopDelay: 1
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
var req = http.get('http://localhost:3000')
block(50000)
req.on('response', function (res) {
t.is(res.statusCode, 503)
protect.stop()
server.close()
t.end()
}).end()
})
})
test('sends 503 when heap used threshold is passed, as per maxHeapUsedBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxHeapUsedBytes: 40
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends 503 when rss threshold is passed, as per maxRssBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends Retry-After header as per clientRetrySecs', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 22
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is(res.headers['retry-after'], '22')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('does not set Retry-After header when clientRetrySecs is 0', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 0
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is('retry-after' in res.headers, false)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('errorPropagationMode:false (default)', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var app = new Koa()
app.use(protect)
app.use(function (ctx, next) {
t.fail()
return next()
})
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('errorPropagationMode:true', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var app = new Koa()
app.on('error', function () {}) // silence error log output
app.use(function (ctx, next) {
return next().catch(function (err) {
t.ok(err)
t.is(err.status, 503)
throw err
})
})
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in default mode, production:false leads to high detail client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in default mode, production:true leads to standard 503 client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Service Unavailable')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in errorPropagationMode production:false sets expose:true on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var app = new Koa()
app.on('error', function () {}) // silence error log output
app.use(function (ctx, next) {
return next().catch(function (err) {
t.ok(err)
t.is(err.expose, true)
throw err
})
})
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in errorPropagationMode production:true sets expose:false on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var app = new Koa()
app.on('error', function () {}) // silence error log output
app.use(function (ctx, next) {
return next().catch(function (err) {
t.ok(err)
t.is(err.expose, false)
throw err
})
})
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('resumes usual operation once load pressure is reduced under threshold', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var app = new Koa()
var router = new Router()
app.use(protect)
router.get('/', function (ctx, next) {
ctx.body = 'content'
return next()
})
app.use(router.routes())
var server = app.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
process.memoryUsage = function () {
return {
rss: 10,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
setTimeout(function () {
http.get('http://localhost:3000').on('response', function (res) {
t.is(res.statusCode, 200)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}, 6)
}).end()
}, 6)
})
})
test('if logging option is a string, when overloaded, writes log message using req.log as per level in string', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
logging: 'warn'
})
var app = new Koa()
app.use(function (ctx, next) {
ctx.log = ctx.req.log = {
warn: function (msg) {
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
}
return next()
})
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logging option is a function, when overloaded calls the function with heavy load message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('koa', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
logging: function (msg) {
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
})
var app = new Koa()
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and if logging option is a string, writes log message using req.log as per level in string for every request', function (t) {
var protect = protection('koa', {
logging: 'info',
logStatsOnReq: true
})
t.plan(1)
var app = new Koa()
app.use(function (ctx, next) {
ctx.log = ctx.req.log = {
info: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
}
return next()
})
app.use(protect)
var server = app.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and logging option is a function, calls the function with stats on every request', function (t) {
var protect = protection('koa', {
logStatsOnReq: true,
logging: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
})
var app = new Koa()
app.use(protect)
var server = app.listen(3001, function () {
setTimeout(function () {
http.get('http://localhost:3001').end()
}, 6)
})
})
================================================
FILE: test/integration/restify/index.js
================================================
'use strict'
var http = require('http')
var restify = require('restify')
var protection = require('../../..')
var test = require('tap').test
function block (n) {
while (n--) { JSON.parse(JSON.stringify(require('../../../package.json'))) }
}
test('sends 503 when event loop is overloaded, per maxEventLoopDelay', function (t) {
var protect = protection('restify', {
maxEventLoopDelay: 1
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
var req = http.get('http://localhost:3000')
block(50000)
req.on('response', function (res) {
t.is(res.statusCode, 503)
protect.stop()
server.close()
t.end()
}).end()
})
})
test('sends 503 when heap used threshold is passed, as per maxHeapUsedBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxHeapUsedBytes: 40
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends 503 when heap used threshold is passed, as per maxRssBytes', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('sends Retry-After header as per clientRetrySecs', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 22
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is(res.headers['retry-after'], '22')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('does not set Retry-After header when clientRetrySecs is 0', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
clientRetrySecs: 0
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
t.is('retry-after' in res.headers, false)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('errorPropagationMode:false (default)', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.use(function () {
t.fail()
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('errorPropagationMode:true', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.use(function (err, req, res, next) {
t.ok(err)
t.is(err.statusCode, 503)
res.end('err message')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in default mode, production:false leads to high detail client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in default mode, production:true leads to standard 503 client response message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: false
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
res.once('data', function (msg) {
msg = msg.toString()
t.is(msg, 'Service Unavailable')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}).end()
}, 6)
})
})
test('in errorPropagationMode production:false sets expose:true on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
production: false,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.use(function (err, req, res, next) {
t.ok(err)
t.is(err.expose, true)
res.end('err message')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('in errorPropagationMode production:true sets expose:false on error object', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
production: true,
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
errorPropagationMode: true
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.use(function (err, req, res, next) {
t.ok(err)
t.is(err.expose, false)
res.end('err message')
})
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}).end()
}, 6)
})
})
test('resumes usual operation once load pressure is reduced under threshold', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
var req = http.get('http://localhost:3000')
req.on('response', function (res) {
t.is(res.statusCode, 503)
process.memoryUsage = function () {
return {
rss: 10,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
setTimeout(function () {
http.get('http://localhost:3000').on('response', function (res) {
t.is(res.statusCode, 200)
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
})
}, 6)
}).end()
}, 6)
})
})
test('if logging option is a string, when overloaded, writes log message using req.log as per level in string', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
logging: 'warn'
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(function (req, res, next) {
req.log = {
warn: function (msg) {
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
}
next()
})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logging option is a function, when overloaded calls the function with heavy load message', function (t) {
var memoryUsage = process.memoryUsage
process.memoryUsage = function () {
return {
rss: 99999,
heapTotal: 9999,
heapUsed: 999,
external: 99
}
}
var protect = protection('restify', {
sampleInterval: 5,
maxEventLoopDelay: 0,
maxRssBytes: 40,
logging: function (msg) {
t.is(msg, 'Server experiencing heavy load: (rss)')
server.close()
protect.stop()
process.memoryUsage = memoryUsage
t.end()
}
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and if logging option is a string, writes log message using req.log as per level in string for every request', function (t) {
var protect = protection('restify', {
logging: 'info',
logStatsOnReq: true
})
t.plan(1)
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(function (req, res, next) {
req.log = {
info: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
}
next()
})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3000, function () {
setTimeout(function () {
http.get('http://localhost:3000').end()
}, 6)
})
})
test('if logStatsOnReq is true and logging option is a function, calls the function with stats on every request', function (t) {
var protect = protection('restify', {
logStatsOnReq: true,
logging: function (msg) {
t.same(Object.keys(msg), [
'overload',
'eventLoopOverload',
'heapUsedOverload',
'rssOverload',
'eventLoopDelay',
'maxEventLoopDelay',
'maxHeapUsedBytes',
'maxRssBytes'
])
server.close()
protect.stop()
t.end()
}
})
var server = restify.createServer({name: 'myapp', version: '1.0.0'})
server.use(protect)
server.get('/', function (req, res) { res.end('content') })
server.listen(3001, function () {
setTimeout(function () {
http.get('http://localhost:3001').end()
}, 6)
})
})
gitextract_e7w9qjtr/
├── .gitignore
├── .travis.yml
├── LICENSE
├── benchmarks/
│ ├── express/
│ │ ├── excluded.js
│ │ ├── included.js
│ │ └── index.js
│ ├── http/
│ │ ├── excluded.js
│ │ ├── included.js
│ │ └── index.js
│ ├── koa/
│ │ ├── excluded.js
│ │ ├── included.js
│ │ └── index.js
│ └── restify/
│ ├── excluded.js
│ ├── included.js
│ └── index.js
├── demos/
│ ├── express/
│ │ └── index.js
│ ├── http/
│ │ └── index.js
│ ├── koa/
│ │ └── index.js
│ └── restify/
│ └── index.js
├── index.js
├── lib/
│ ├── explain.js
│ ├── express.js
│ ├── http.js
│ ├── koa.js
│ ├── restify.js
│ └── stats.js
├── package.json
├── readme.md
└── test/
├── index.js
└── integration/
├── express/
│ └── index.js
├── http/
│ └── index.js
├── koa/
│ └── index.js
└── restify/
└── index.js
SYMBOL INDEX (16 symbols across 15 files)
FILE: benchmarks/http/excluded.js
function serve (line 6) | function serve (req, res) {
FILE: benchmarks/http/included.js
function serve (line 7) | function serve (req, res) {
FILE: demos/express/index.js
function sleep (line 39) | function sleep (msec) {
FILE: demos/http/index.js
function sleep (line 7) | function sleep (msec) {
function serve (line 12) | function serve (req, res) {
FILE: demos/koa/index.js
function sleep (line 44) | function sleep (msec) {
FILE: demos/restify/index.js
function sleep (line 42) | function sleep (msec) {
FILE: index.js
function protect (line 24) | function protect (framework, opts) {
FILE: lib/explain.js
function explain (line 5) | function explain (protect) {
FILE: lib/http.js
function http (line 8) | function http (opts, protect) {
FILE: lib/koa.js
function koa (line 7) | function koa (opts, protect) {
FILE: lib/stats.js
function OverloadProtectionStats (line 1) | function OverloadProtectionStats (overload, eventLoopOverload, heapUsedO...
FILE: test/integration/express/index.js
function sleep (line 9) | function sleep (msec) {
FILE: test/integration/http/index.js
function sleep (line 8) | function sleep (msec) {
FILE: test/integration/koa/index.js
function block (line 8) | function block (n) {
FILE: test/integration/restify/index.js
function block (line 9) | function block (n) {
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (110K chars).
[
{
"path": ".gitignore",
"chars": 990,
"preview": "\n# Created by https://www.gitignore.io/api/node\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-erro"
},
{
"path": ".travis.yml",
"chars": 80,
"preview": "language: node_js\nsudo: false\nnode_js:\n- '8'\n- '10'\n- '12'\nscript:\n- npm run ci\n"
},
{
"path": "LICENSE",
"chars": 1087,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2017 David Mark Clements\n\nPermission is hereby granted, free of charge, to any pers"
},
{
"path": "benchmarks/express/excluded.js",
"chars": 128,
"preview": "'use strict'\n\nvar app = require('express')()\n\napp.get('/', function (req, res) {\n res.send('content')\n})\n\nmodule.export"
},
{
"path": "benchmarks/express/included.js",
"chars": 188,
"preview": "'use strict'\n\nvar app = require('express')()\nvar protect = require('../..')('express')\n\napp.use(protect)\n\napp.get('/', f"
},
{
"path": "benchmarks/express/index.js",
"chars": 785,
"preview": "'use strict'\nvar autocannon = require('autocannon')\n\nvar included = require('./included')\nvar excluded = require('./excl"
},
{
"path": "benchmarks/http/excluded.js",
"chars": 156,
"preview": "'use strict'\n\nvar http = require('http')\nvar server = http.createServer(serve)\n\nfunction serve (req, res) {\n res.end('c"
},
{
"path": "benchmarks/http/included.js",
"chars": 236,
"preview": "'use strict'\n\nvar http = require('http')\nvar server = http.createServer(serve)\nvar protect = require('../..')('http')\n\nf"
},
{
"path": "benchmarks/http/index.js",
"chars": 757,
"preview": "'use strict'\nvar autocannon = require('autocannon')\n\nvar included = require('./included')\nvar excluded = require('./excl"
},
{
"path": "benchmarks/koa/excluded.js",
"chars": 235,
"preview": "'use strict'\n\nvar Koa = require('koa')\nvar Router = require('koa-router')\n\nvar router = new Router()\nvar app = new Koa()"
},
{
"path": "benchmarks/koa/included.js",
"chars": 291,
"preview": "'use strict'\n\nvar Koa = require('koa')\nvar Router = require('koa-router')\nvar protect = require('../..')('koa')\n\nvar rou"
},
{
"path": "benchmarks/koa/index.js",
"chars": 777,
"preview": "'use strict'\nvar autocannon = require('autocannon')\n\nvar included = require('./included')\nvar excluded = require('./excl"
},
{
"path": "benchmarks/restify/excluded.js",
"chars": 180,
"preview": "'use strict'\n\nvar app = require('restify').createServer({\n name: 'myapp',\n version: '1.0.0'\n})\n\napp.get('/', function "
},
{
"path": "benchmarks/restify/included.js",
"chars": 240,
"preview": "'use strict'\n\nvar app = require('restify').createServer({\n name: 'myapp',\n version: '1.0.0'\n})\nvar protect = require('"
},
{
"path": "benchmarks/restify/index.js",
"chars": 785,
"preview": "'use strict'\nvar autocannon = require('autocannon')\n\nvar included = require('./included')\nvar excluded = require('./excl"
},
{
"path": "demos/express/index.js",
"chars": 1005,
"preview": "'use strict'\n\nvar app = require('express')()\nvar protect = require('../..')('express')\n\napp.use(protect)\n\napp.get('/', f"
},
{
"path": "demos/http/index.js",
"chars": 1014,
"preview": "'use strict'\n\nvar http = require('http')\nvar server = http.createServer(serve)\nvar protect = require('../..')('http')\n\nf"
},
{
"path": "demos/koa/index.js",
"chars": 1107,
"preview": "'use strict'\n\nvar Koa = require('koa')\nvar Router = require('koa-router')\nvar protect = require('../..')('koa')\n\nvar rou"
},
{
"path": "demos/restify/index.js",
"chars": 1057,
"preview": "'use strict'\n\nvar app = require('restify').createServer({\n name: 'myapp',\n version: '1.0.0'\n})\nvar protect = require('"
},
{
"path": "index.js",
"chars": 3015,
"preview": "'use strict'\n\nvar loopbench = require('loopbench')\n\nvar frameworks = {\n express: require('./lib/express'),\n http: requ"
},
{
"path": "lib/explain.js",
"chars": 462,
"preview": "'use strict'\n\nmodule.exports = explain\n\nfunction explain (protect) {\n var explanation = 'Server experiencing heavy load"
},
{
"path": "lib/express.js",
"chars": 48,
"preview": "'use strict'\nmodule.exports = require('./http')\n"
},
{
"path": "lib/http.js",
"chars": 1821,
"preview": "'use strict'\n\nvar explain = require('./explain')\nvar OverloadProtectionStats = require('./stats')\n\nmodule.exports = http"
},
{
"path": "lib/koa.js",
"chars": 1843,
"preview": "'use strict'\nmodule.exports = koa\n\nvar explain = require('./explain')\nvar OverloadProtectionStats = require('./stats')\n\n"
},
{
"path": "lib/restify.js",
"chars": 48,
"preview": "'use strict'\nmodule.exports = require('./http')\n"
},
{
"path": "lib/stats.js",
"chars": 514,
"preview": "function OverloadProtectionStats (overload, eventLoopOverload, heapUsedOverload, rssOverload, eventLoopDelay, maxEventLo"
},
{
"path": "package.json",
"chars": 1519,
"preview": "{\n \"name\": \"overload-protection\",\n \"version\": \"1.2.3\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"tap test\",\n "
},
{
"path": "readme.md",
"chars": 12345,
"preview": "# overload-protection \n\nLoad detection and shedding capabilities for http, express, restify, and koa\n\n[.test\nvar protect = require('../')\n\ntest('throws if framework is unspecified', fun"
},
{
"path": "test/integration/express/index.js",
"chars": 14652,
"preview": "'use strict'\n\nvar http = require('http')\nvar express = require('express')\n\nvar protection = require('../../..')\nvar test"
},
{
"path": "test/integration/http/index.js",
"chars": 16209,
"preview": "'use strict'\n\nvar http = require('http')\n\nvar protection = require('../../..')\nvar test = require('tap').test\n\nfunction "
},
{
"path": "test/integration/koa/index.js",
"chars": 14320,
"preview": "'use strict'\nvar http = require('http')\nvar Koa = require('koa')\nvar Router = require('koa-router')\nvar protection = req"
},
{
"path": "test/integration/restify/index.js",
"chars": 15564,
"preview": "'use strict'\n\nvar http = require('http')\nvar restify = require('restify')\n\nvar protection = require('../../..')\nvar test"
}
]
About this extraction
This page contains the full source code of the davidmarkclements/overload-protection GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (101.2 KB), approximately 28.0k tokens, and a symbol index with 16 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.