Socket
Socket
Sign inDemoInstall

toxy

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

toxy - npm Package Compare versions

Comparing version 0.1.0-alpha.0 to 0.1.0

examples/balancer.js

33

index.js

@@ -7,2 +7,6 @@ const Toxy = require('./lib/toxy')

/**
* API factory
*/
function toxy(opts) {

@@ -12,9 +16,23 @@ return new Toxy(opts)

toxy.Poison = require('./lib/poison')
/**
* Expose internal modules as static members
*/
toxy.Rule = require('./lib/rule')
toxy.Base = require('./lib/base')
toxy.Directive = require('./lib/directive')
toxy.VERSION = require('./package.json').version
toxy.Rocky = require('rocky').Rocky
toxy.poisons =
Toxy.prototype.poisons = Object.create(null)
/**
* Expose current version
*/
toxy.VERSION = require('./package.json').version
/**
* Expose built-in poisons
*/
toxy.poisons = Toxy.prototype.poisons = Object.create(null)
poisons.forEach(function (poison) {

@@ -26,5 +44,8 @@ Toxy.prototype.poisons[poison.name] = function () {

toxy.rules =
Toxy.prototype.rules = Object.create(null)
/**
* Expose built-in rules
*/
toxy.rules = Toxy.prototype.rules = Object.create(null)
rules.forEach(function (rule) {

@@ -31,0 +52,0 @@ Toxy.prototype.rules[rule.name] = function () {

33

lib/directive.js

@@ -1,2 +0,2 @@

const midware = require('midware')
const Rule = require('./rule')

@@ -6,25 +6,28 @@ module.exports = Directive

function Directive(directive) {
if (typeof directive !== 'function') {
throw new TypeError('First argument must be a function')
}
Rule.call(this)
this.enabled = true
this.directive = directive
this._rules = midware()
}
Directive.prototype.isEnabled = function () {
return this.disabled !== false
}
Directive.prototype = Object.create(Rule.prototype)
Directive.prototype.disable = function () {
return this.disabled = true
return this.enabled = false
}
Directive.prototype.enable = function () {
return this.disabled = false
return this.enabled = true
}
Directive.prototype.rule = function (rule) {
var r = new Directive(rule)
this._rules(r.handler())
Directive.prototype.isEnabled = function () {
return this.enabled
}
Directive.prototype.rule =
Directive.prototype.filter = function (rule) {
if (!(rule instanceof Directive)) {
rule = new Directive(rule)
}
this._rules(rule.handler())
return this

@@ -37,3 +40,3 @@ }

function handler(req, res, next) {
if (self.disabled === true) return next()
if (self.enabled === false) return next()
self._rules.run(req, res, handle.bind(self))

@@ -40,0 +43,0 @@

module.exports = [
require('./delay'),
require('./inject'),
require('./abort'),
require('./latency'),
require('./throttle'),

@@ -6,0 +6,0 @@ require('./timeout'),

module.exports = function rateLimit(opts) {
opts = opts || {}
var limit = +opts.limit || 1
var limit = +opts.limit || 10
var threshold = +opts.threshold || 1000

@@ -5,0 +5,0 @@ var message = opts.message || 'Too many requests'

@@ -10,4 +10,5 @@ const defaultDelay = 5000

var _end = res.end
var _setHeader = res.setHeader
var _writeHead = res.writeHead
var resproto = Object.getPrototypeOf(res)
var _setHeader = resproto.setHeader
var _writeHead = resproto.writeHead

@@ -45,3 +46,3 @@ res.setHeader = function (header, value) {

_res = _setHeader = null
_writeHead = args = null
_writeHead = args = resproto = null
}

@@ -48,0 +49,0 @@ }

@@ -5,3 +5,4 @@ const common = require('../common')

opts = opts || {}
var threshold = +opts.threshold || 100
var threshold = +opts.threshold || 1000
var chunkSize = (+opts.chunk || +opts.bps) || 1024

@@ -23,8 +24,8 @@

req.push = function (data, encoding) {
if (ended) return
if (data === null) return writeStream()
if (data === null) return writeChunks()
common.sliceBuffer(chunkSize, data, encoding, buf)
}
function writeStream() {
function writeChunks() {
if (ended) return
ended = true

@@ -35,4 +36,5 @@ common.eachSeries(buf, pushDefer, end)

function end() {
if (closed) return
cleanup()
if (!closed) req.push(null)
req.push(null)
}

@@ -39,0 +41,0 @@

const rocky = require('rocky')
const Base = rocky.Base
const RockyBase = rocky.Base
const Rule = require('./rule')
const rules = require('./rules')
const poisons = require('./poisons')
const Directive = require('./directive')
const Poison = require('./poison')
module.exports = rocky.Rocky
Base.prototype.poison =
Base.prototype.usePoison = function (poison) {
if (poison instanceof Poison) {
this._poisons(poison.handler())
return poison
RockyBase.prototype.rule =
RockyBase.prototype.filter = Directive.prototype.rule
RockyBase.prototype.withRule =
RockyBase.prototype.poisonRule =
RockyBase.prototype.poisonFilter = function (rule) {
if (this.lastPoison) {
this.lastPoison.rule(rule)
}
var p = new Poison(poison)
this._poisons(p.handler())
this.lastPoison = p
return this
}
Base.prototype.rule =
Base.prototype.filter = Directive.prototype.rule
Base.prototype.poisonRule =
Base.prototype.poisonFilter = function (rule) {
if (!this.lastPoison) return this
this.lastPoison.rule(rule)
return this
RockyBase.prototype.enable = function (poison) {
return this._callMethod(this._poisons, 'enable', poison)
}
Base.prototype.disable = function (poison) {
return stackCallProxy(this._poisons.stack, 'disable', poison)
RockyBase.prototype.disable = function (poison) {
return this._callMethod(this._poisons, 'disable', poison)
}
Base.prototype.enable = function (poison) {
return stackCallProxy(this._poisons.stack, 'enable', poison)
RockyBase.prototype.remove = function (poison) {
return this._remove(this_poisons, poison)
}
Base.prototype.enableRule = function (rule) {
return stackCallProxy(this._rules.stack, 'enable', rule)
RockyBase.prototype.isEnabled = function (poison) {
return this._callMethod(this._poisons, 'isEnabled', poison)
}
Base.prototype.disableRule = function (rule) {
return stackCallProxy(this._rules.stack, 'disable', rule)
}
Base.prototype.isPoisonEnabled = function (poison) {
return stackCallProxy(this._poisons.stack, 'isEnabled', poison)
}
Base.prototype.isRuleEnabled = function (rule) {
return stackCallProxy(this._rules.stack, 'isEnabled', rule)
}
Base.prototype.disableAll = function () {
RockyBase.prototype.disableAll =
RockyBase.prototype.disablePoisons = function () {
return this._disableAll(this._poisons)
}
Base.prototype.disableAllRules = function () {
return this._disableAll(this._rules)
RockyBase.prototype.poisons =
RockyBase.prototype.getPoisons = function () {
return this._getAll(this._poisons)
}
Base.prototype._disableAll = function (mw) {
mw.stack.forEach(function (fn) {
fn.$of.disable()
})
RockyBase.prototype.flush =
RockyBase.prototype.flushPoisons = function () {
this._poisons.stack.splice(0)
return this
}
Base.prototype.getPoisons = function () {
return this._poisons.stack.map(function (fn) {
return fn.$of
})
}
RockyBase.prototype.poison =
RockyBase.prototype.usePoison = function (poison) {
if (!(poison instanceof Directive)) {
poison = new Directive(poison)
}
Base.prototype.getRules = function () {
return this._rules.stack.map(function (fn) {
return fn.$of
})
}
Base.prototype.flushAll =
Base.prototype.flushAllPoisons = function () {
this._poisons.stack.splice(0)
this._poisons(poison.handler())
this.lastPoison = poison
return this
}
Base.prototype.flushAllRules = function () {
this._rules.stack.splice(0)
return this
}
function stackCallProxy(stack, action, name) {
for (var i = 0, l = stack.length; i < l; i += 1) {
if (stack[i].$name === name || stack[i].$of === name) {
return stack[i].$of[action]()
}
}
return false
}
Object.keys(Rule.prototype).forEach(function (key) {
RockyBase.prototype[key] = Rule.prototype[key]
})
module.exports = [
require('./method'),
require('./random'),
require('./match-headers'),
require('./probability'),
require('./headers'),
require('./content-type'),
]

@@ -1,16 +0,14 @@

module.exports = function method(opts) {
if (typeof opts === 'string') {
opts = { method: opts }
module.exports = function method(matchMethod) {
if (typeof matchMethod === 'string') {
matchMethod = [ matchMethod ]
}
opts = opts || {}
opts.method = opts.method || ''
if (!matchMethod) {
matchMethod = [ 'GET' ]
}
return function method(req, res, next) {
var filter = req.method === opts.method.toUpperCase()
|| (Array.isArray(opts.methods)
&& !!~opts.methods.indexOf(req.method))
next(!filter)
var ignore = !!~matchMethod.indexOf(req.method)
next(!ignore)
}
}

@@ -17,6 +17,6 @@ const midware = require('midware')

setupMiddleware(this)
wrapRoute(this)
wrapRouteConstructor(this)
}
function wrapRoute(self) {
function wrapRouteConstructor(self) {
var _route = self.route

@@ -27,3 +27,3 @@ self.route = function (method, path) {

route._poisons = midware()
setupMiddleware(route)
setupMiddleware(route, self)
return route

@@ -35,2 +35,6 @@ }

self.use(function (req, res, next) {
// Expose the toxy instance via the middleware
req.toxy = self
// Run rules middleware validations before apply the poisons
self._rules.run(req, res, function (filter) {

@@ -37,0 +41,0 @@ if (filter === true) return next()

{
"name": "toxy",
"version": "0.1.0-alpha.0",
"description": "Hackable HTTP proxy to simulate multiple server failures and unexpected conditions",
"version": "0.1.0",
"description": "Hackable HTTP proxy to simulate server failure scenarios and unexpected conditions",
"repository": "h2non/toxy",

@@ -41,2 +41,4 @@ "author": "Tomas Aparicio",

"chai": "^3.0.0",
"clone": "^1.0.2",
"express": "^4.13.2",
"mocha": "^2.2.5",

@@ -43,0 +45,0 @@ "sinon": "^1.15.3",

@@ -1,48 +0,94 @@

# toxy [![Build Status](https://api.travis-ci.org/h2non/toxy.svg?branch=master&style=flat)](https://travis-ci.org/h2non/toxy) [![Code Climate](https://codeclimate.com/github/h2non/toxy/badges/gpa.svg)](https://codeclimate.com/github/h2non/toxy) [![NPM](https://img.shields.io/npm/v/toxy.svg)](https://www.npmjs.org/package/toxy)
# toxy [![Build Status](https://api.travis-ci.org/h2non/toxy.svg?branch=master&style=flat)](https://travis-ci.org/h2non/toxy) [![Code Climate](https://codeclimate.com/github/h2non/toxy/badges/gpa.svg)](https://codeclimate.com/github/h2non/toxy) [![NPM](https://img.shields.io/npm/v/toxy.svg)](https://www.npmjs.org/package/toxy) ![Stability](http://img.shields.io/badge/stability-beta-orange.svg?style=flat)
<!--
![Downloads](https://img.shields.io/npm/dm/toxy.svg)
-->
<img align="right" height="180" src="http://s8.postimg.org/ikc9jxllh/toxic.jpg" />
Pluggable and hackable HTTP proxy to simulate multiple server failures and unexpected conditions.
Built for [node.js](http://nodejs.org)/[io.js](https://iojs.org). Powered by [rocky](https://github.com/h2non/rocky)
**toxy** is a **hackable HTTP proxy** to **simulate** server **failure scenarios** and **unexpected conditions**.
It was mainly created for fuzz/evil testing purposes.
It becomes particulary useful in fault tolerant and resilient systems, tipically in service-oriented distributed architectures, where `toxy` may act as intermediate proxy among services.
**toxy** allows you to plug in [poisons](#poisons), optionally filtered by [rules](#rules), which basically can intercept and alter the HTTP flow as you want performing multiple actions in the middle of that process, like for instance limiting the bandwidth, injecting a TCP jitter or replying with a custom error or status code.
Runs in [node.js](http://nodejs.org)/[io.js](https://iojs.org). Compatible with [connect](https://github.com/senchalabs/connect)/[express](http://expressjs.com).
Built on top of [rocky](https://github.com/h2non/rocky), a full-featured, middleware-oriented HTTP/S proxy.
Requires node.js +0.12 or io.js +1.6
**This is a work in progress**
## Contents
## Built-in poisons
- [Features](#features)
- [Introduction](#introduction)
- [Why toxy?](#why-toxy)
- [Concepts](#concepts)
- [How it works](#how-it-works)
- [Usage](#usage)
- [Installation](#installation)
- [Examples](#examples)
- [Poisons](#poisons)
- [Built-in poisons](#build-in-poisons)
- [How to write poisons](#how-to-write-poisons)
- [Rules](#rules)
- [Built-in rules](#built-in-rules)
- [How to write rules](#how-to-write-rules)
- [Programmatic API](#programmatic-api)
- [License](#license)
- [x] [Delay](#delay)
- [x] [Timeout](#timeout)
- [x] [Inject response](#inject-response)
- [x] [Bandwidth](#bandwidth)
- [x] [Rate limit](#rate-limit)
- [x] [Slow read](#slow-read)
- [x] [Slow open](#slow-open)
- [x] [Slow close](#slow-close)
- [x] [Throttle](#throttle)
- [x] [Abort connection](#abort-connection)
## Features
## Built-in rules
- Full-featured HTTP/S proxy (backed by [http-proxy](https://github.com/nodejistu/node-http-proxy))
- Hackable and elegant programmatic API (inspired on connect/express)
- Featured built-in router with nested configuration
- Hierarchical middleware layer (global and route-specific)
- Easily augmentable via middleware (based on connect/express middleware)
- Built-in poisons (bandwidth, error, abort, latency, slow read...)
- Rule-based poisoning (probabilistic, HTTP method, headers, body...)
- Support third-party poisons and rules
- Built-in balancer and traffic intercept via middleware
- Inherits the API and features from [rocky](https://github.com/h2non/rocky)
- Compatible with connect/express (and most of their middleware)
- Runs as standalone HTTP proxy
- [x] [Random](#random)
- [x] [Method](#method)
- [x] [Headers](#headers)
- [x] [Content Type](#content-type)
- [ ] [Query params](#query-params)
- [ ] [Body](#body)
## Introduction
<!--
## How it works
### Why toxy?
```
There're some other similar solutions to `toxy` in the market, but most of them don't provide a proper programmatic control and are not easy to hack, configure and/or extend.
`toxy` provides a powerful hacking-driven solution with a proper low-level interface and programmatic control with an elegant API and the power, simplicity and fun of node.js.
### Concepts
`toxy` introduces two main core directives worth knowing before using it:
**Poisons** are a specific logic encapsulated as middleware to poison an incoming or outgoing HTTP flow (e.g: injecting a latency in the server response). HTTP flow can be poisoned by one or multiple poisons.
**Rules** are a kind of validation filters that can be applied to the whole HTTP flow or to a concrete poison, in order to determine if one or multiple poisons should be enabled or not to poison the HTTP traffic (e.g: a probabilistic calculus).
### How it works
```
-->
↓ ( Incoming request ) ↓
↓ ||| ↓
↓ ---------------- ↓
↓ | Toxy Router | ↓ --> Match a route based on the incoming request
↓ ---------------- ↓
↓ ||| ↓
↓ ---------------- ↓
↓ | Exec Rules | ↓ --> Apply configured rules for the request
↓ ---------------- ↓
↓ ||| ↓
↓ ---------------- ↓
↓ | Exec Poisons | ↓ --> If all rules passed, poisoning the HTTP flow
↓ ---------------- ↓
↓ / \ ↓
↓ \ / ↓
↓ ------------------- ↓
↓ | HTTP dispatcher | ↓ --> Proxy the HTTP traffic for both poisoned or not
↓ ------------------- ↓
```
## Installation
## Usage
### Installation
```

@@ -52,15 +98,26 @@ npm install toxy

## Examples
### Examples
See the [examples](https://github.com/h2non/toxy/tree/master/examples) directory for more use cases
#### Basic poisioning
```js
var toxy = require('toxy')
var poisons = toxy.poisons
var rules = toxy.rules
var proxy = toxy()
var poisons = proxy.poisons
var rules = proxy.rules
proxy
.poison(poisons.delay({ jitter: 500 }))
.forward('http://httpbin.org')
proxy
.poison(poisons.latency({ jitter: 500 }))
.rule(rules.random(50))
.rule(rules.method('GET'))
.poison(poisons.bandwidth({ bps: 1024 }))
.withRule(rules.method('GET'))
proxy.get('/*')
proxy.listen(3000)
```

@@ -70,38 +127,362 @@

#### Delay
Poisons host specific logic which intercepts and mutates, wraps, modify and/or cancel an HTTP transaction in the proxy server.
Poisons can be applied to incoming or outgoing, or even both traffic flows.
#### Timeout
Poisons can be composed and reused for different HTTP scenarios.
Poisons are executed in FIFO order.
### Built-in poisons
- [x] [Latency](#latency)
- [x] [Inject response](#inject-response)
- [x] [Bandwidth](#bandwidth)
- [x] [Rate limit](#rate-limit)
- [x] [Slow read](#slow-read)
- [x] [Slow open](#slow-open)
- [x] [Slow close](#slow-close)
- [x] [Throttle](#throttle)
- [x] [Abort connection](#abort-connection)
- [x] [Timeout](#timeout)
#### Latency
Name: `latency`
Injects response latency
#### Inject response
Name: `inject`
Injects a custom response, intercepting the request before sending it to the target server. Useful to test server originated errors.
#### Bandwidth
Name: `bandwidth`
Limits the amount of bytes sent over the network in a specific threshold time frame.
#### Rate limit
Name: `rateLimit`
Rates the amount of requests received by the proxy in a specific threshold time frame. Designed to test API limits.
#### Slow read
Name: `slowRead`
Reads incoming packets slowly.
#### Slow open
Name: `slowOpen`
Delays the HTTP connection opened status.
#### Slow close
Name: `slowClose`
Delays the EOF signal.
#### Throttle
Name: `throttle`
Restricts the amount of packets sent over the network in a specific threshold time frame.
#### Abort connection
Name: `abort`
Aborts the TCP connection, optionally with a custom error. From the low-level perspective, this will destroy the socket on the server, operating only at TCP level without sending any specific HTTP application level data.
#### Timeout
Name: `timeout`
Defines a response timeout. Useful when forwarding to potentially slow servers.
### How to write poisons
Poisons are implemented as standalone middleware (like in connect/express).
Here's a simple example of a server latency poison:
```js
function latency(delay) {
/**
* We name the function since toxy uses it as identifier to get/disable/remove it in the future
*/
return function latency(req, res, next) {
var timeout = setTimeout(clean, delay)
req.once('close', onClose)
function onClose() {
clearTimeout(timeout)
next('client connection closed')
}
function clean() {
req.removeListener('close', onClose)
next()
}
}
}
// Register and enable the poison
toxy
.get('/foo')
.poison(latency(2000))
```
For a real example, take a look to the [built-in poisons](https://github.com/h2non/toxy/tree/master/lib/poisons) implementation.
## Rules
#### Random
Rules are simple validation filters which inspect an HTTP request and determine, given a certain rules (e.g: method, headers, query params), if the HTTP transaction should be poisoned or not.
Rules are useful to compose, decouple and reuse logic among different scenarios of poisoning. You can also define globally applied rules or nested poison-scope rules only.
Rules are executed in FIFO order. Their evaluation logic is comparable to `Array#every()` in JavaScript: all the rules must match in order to proceed with the poisoning.
### Built-in rules
- [x] [Probability](#Probability)
- [x] [Method](#method)
- [x] [Headers](#headers)
- [x] [Content Type](#content-type)
- [ ] [Body](#body)
#### Probability
Enables the rule by a random probabilistic. Useful for random poisioning.
**Arguments**:
- **percentage** `number` - Percentage of filtering. Default `50`
```js
var rule = toxy.rules.probability(85)
toxy.rule(rule)
```
#### Method
Filters by HTTP method.
**Arguments**:
- **method** `string|array` - Method or methods to filter.
```js
var method = toxy.rules.method(['GET', 'POST'])
toxy.rule(method)
```
#### Headers
Filter by certain headers.
**Arguments**:
- **headers** `object` - Headers to match by key-value pair. `value` can be a string, regexp, `boolean` or `function(headerValue, headerName) => boolean`
```js
var matchHeaders = {
'content-type': /^application/\json/i,
'server': true, // meaning it should be present,
'accept': function (value, key) {
return value.indexOf('text') !== -1
}
}
var rule = toxy.rules.headers(matchHeaders)
toxy.rule(rule)
```
#### Content Type
#### Query params
Filters by content type header. It should be present
**Arguments**:
- **value** `string|regexp` - Header value to match.
```js
var rule = toxy.rules.contentType('application/json')
toxy.rule(rule)
```
#### Body
`To do`
### How to write rules
Rules are simple middleware functions that resolve asyncronously with a `boolean` value to determine if a given HTTP transaction should be ignored when poisoning.
Here's an example of a simple rule matching the HTTP method to determine if:
```js
function method(matchMethod) {
/**
* We name the function since it's used by toxy to identify the rule to get/disable/remove it in the future
*/
return function method(req, res, next) {
var shouldIgnore = req.method !== matchMethod
next(shouldIgnore)
}
}
// Register and enable the rule
toxy
.get('/foo')
.rule(method('GET'))
.poison(/* ... */)
```
## Programmatic API
`toxy` API is completely built on top the [rocky API](https://github.com/h2non/rocky#programmatic-api). In other words, you can use any of the methods, features and middleware layer natively provided by `rocky`.
### toxy([ options ])
Create a new `toxy` proxy.
For supported `options`, please see rocky [documentation](https://github.com/h2non/rocky#configuration)
```js
var toxy = require('toxy')
toxy({ forward: 'http://server.net', timeout: 30000 })
toxy
.get('/foo')
.poison(toxy.poisons.latency(1000))
.withRule(toxy.rules.contentType('json'))
.forward('http://foo.server')
toxy
.post('/bar')
.poison(toxy.poisons.bandwidth({ bps: 1024 }))
.withRule(toxy.rules.probability(50))
.forward('http://bar.server')
toxy.all('/*')
```
#### toxy#get(path, [ middleware... ])
Return: `ToxyRoute`
#### toxy#post(path, [ middleware... ])
Return: `ToxyRoute`
#### toxy#put(path, [ middleware... ])
Return: `ToxyRoute`
#### toxy#patch(path, [ middleware... ])
Return: `ToxyRoute`
#### toxy#delete(path, [ middleware... ])
Return: `ToxyRoute`
#### toxy#head(path, [ middleware... ])
Return: `ToxyRoute`
#### toxy#all(path, [ middleware... ])
Return: `ToxyRoute`
#### toxy#forward(url)
#### toxy#balance(urls)
#### toxy#replay(url)
#### toxy#use(middleware)
#### toxy#useResponse(middleware)
#### toxy#useReplay(middleware)
#### toxy#poison(poison)
Alias: `usePoison`
#### toxy#rule(rule)
Alias: `useRule`
#### toxy#withRule(rule)
Aliases: `poisonRule`, `poisonFilter`
#### toxy#enable(poison)
#### toxy#disable(poison)
#### toxy#remove(poison)
Return: `boolean`
#### toxy#isEnabled(poison)
Return: `boolean`
#### toxy#disableAll()
Alias: `disablePoisons`
#### toxy#poisons()
Return: `array<Directive>` Alias: `getPoisons`
#### toxy#flush()
Alias: `flushPoisons`
#### toxy#enableRule(rule)
#### toxy#disableRule(rule)
#### toxy#removeRule(rule)
Return: `boolean`
#### toxy#disableRules()
#### toxy#isRuleEnabled(rule)
Return: `boolean`
#### toxy#rules()
Return: `array<Directive>` Alias: `getRules`
#### toxy#flushRules()
### ToxyRoute
Toxy route has, indeed, the same interface as `Toxy` global interface, but further actions you perform againts the API will only be applicable at route-level. In other words: good news, you already know the API.
This example will probably clarify possible doubts:
```js
var toxy = require('toxy')
var proxy = toxy()
// Now using the global API
proxy
.poison(toxy.poisons.bandwidth({ bps: 1024 }))
.rule(toxy.rules.method('GET'))
// Now create a route
var route = proxy.get('/foo')
// Now using the ToxyRoute interface
route
.poison(toxy.poisons.bandwidth({ bps: 512 }))
.rule(toxy.rules.contentType('json'))
```
### Directive(middlewareFn)
A convenient wrapper internally used for poisons and rules.
Normally you don't need to know this interface, but for hacking purposes or more low-level actions might be useful.
#### Directive#enable()
Return: `boolean`
#### Directive#disable()
Return: `boolean`
#### Directive#isEnabled()
Return: `boolean`
#### Directive#rule(rule)
Alias: `filter`
#### Directive#handler()
Return: `function(req, res, next)`
## License
MIT - Tomas Aparicio
const http = require('http')
const clone = require('clone')
const expect = require('chai').expect

@@ -13,6 +14,8 @@ const bandwidth = require('../..').poisons.bandwidth

var lastWrite = Date.now()
var origProto = res.__proto__
res = clone.clonePrototype(res)
res.__proto__.write = function (buffer, encoding, next) {
expect(buffer).to.have.length(opts.bps)
expect(Date.now() - lastWrite).to.be.least(opts.threshold)
expect(Date.now() - lastWrite).to.be.least(opts.threshold - 1)
lastWrite = Date.now()

@@ -26,2 +29,3 @@ buf.push(buffer)

expect(buf.join('')).to.be.equal('Hello World')
console.log('')
done()

@@ -28,0 +32,0 @@ }

const sinon = require('sinon')
const clone = require('clone')
const expect = require('chai').expect

@@ -12,6 +13,8 @@ const slowClose = require('../..').poisons.slowClose

var res = {}
res.writeHead = spy
res.end = function (body) {
var res = clone.clonePrototype({})
res.__proto__.writeHead = spy
res.__proto__.end = function (body) {
spy(body)
res.__proto__
end()

@@ -18,0 +21,0 @@ }

const http = require('http')
const sinon = require('sinon')
const clone = require('clone')
const expect = require('chai').expect

@@ -14,2 +15,3 @@ const throttle = require('../..').poisons.throttle

var lastWrite = Date.now()
res = clone.clonePrototype(res)

@@ -47,2 +49,3 @@ res.__proto__.write = function (buffer, encoding, next) {

var lastWrite = Date.now()
res = clone.clonePrototype(res)

@@ -69,3 +72,2 @@ res.__proto__.write = function (buffer, encoding, next) {

})
})

@@ -17,2 +17,14 @@ const expect = require('chai').expect

test('partial string', function (done) {
var type = 'json'
var req = { headers: { 'content-type': 'application/json' } }
contentType(type)(req, null, next)
function next(ignore) {
expect(ignore).to.be.false
done()
}
})
test('regexp', function (done) {

@@ -19,0 +31,0 @@ var type = /application\/json/i

@@ -21,3 +21,3 @@ const expect = require('chai').expect

method({ methods: ['POST', 'GET'] })(req, null, next)
method(['POST', 'GET'])(req, null, next)

@@ -24,0 +24,0 @@ function next(ignore) {

@@ -0,5 +1,108 @@

const http = require('http')
const expect = require('chai').expect
const toxy = require('..')
const supertest = require('supertest')
suite('toxy', function () {
test('static members', function () {
expect(toxy.rules).to.be.an('object')
expect(toxy.poisons).to.be.an('object')
expect(toxy.Directive).to.be.a('function')
expect(toxy.VERSION).to.be.a('string')
})
test('use poison', function (done) {
var proxy = toxy()
var called = false
proxy.poison(function delay(req, res, next) {
called = true
setTimeout(next, 5)
})
expect(proxy.isEnabled('delay')).to.be.true
proxy.disable('delay')
expect(proxy.isEnabled('delay')).to.be.false
proxy.enable('delay')
expect(proxy.isEnabled('delay')).to.be.true
proxy._poisons.run(null, null, function () {
expect(called).to.be.true
done()
})
})
test('use rule', function (done) {
var proxy = toxy()
var called = false
proxy.rule(function delay(req, res, next) {
called = true
setTimeout(next, 5)
})
expect(proxy.isRuleEnabled('delay')).to.be.true
proxy.disableRule('delay')
expect(proxy.isRuleEnabled('delay')).to.be.false
proxy.enableRule('delay')
expect(proxy.isRuleEnabled('delay')).to.be.true
proxy._rules.run(null, null, function () {
expect(called).to.be.true
done()
})
})
test('e2e', function (done) {
var proxy = toxy()
var server = createServer(9001, 200)
var timeout = 100
proxy.poison(toxy.poisons.latency(timeout))
proxy.rule(function method(req, res, next) {
next(req.method === 'GET' ? null : true)
})
proxy.forward('http://localhost:9001')
proxy.get('/foo')
proxy.listen(9000)
var init = Date.now()
supertest('http://localhost:9000')
.get('/foo')
.expect(200)
.expect('Content-Type', 'application/json')
.expect({ hello: 'world' })
.end(assert)
function assert(err) {
expect(Date.now() - init).to.be.at.least(timeout - 1)
done(err)
}
})
})
function createServer(port, code, assert) {
var server = http.createServer(function (req, res) {
res.writeHead(code, { 'Content-Type': 'application/json' })
res.write(JSON.stringify({ 'hello': 'world' }))
var body = ''
req.on('data', function (data) {
body += data
})
req.on('end', function () {
req.body = body
end()
})
function end() {
if (assert) assert(req, res)
res.end()
}
})
server.listen(port)
return server
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc