Socket
Socket
Sign inDemoInstall

toxy

Package Overview
Dependencies
29
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.2 to 0.1.3

examples/poisons.js

30

examples/configuration.js

@@ -16,7 +16,17 @@ const toxy = require('..')

}))
.withRule(rules.method('GET'))
.withRule(rules.probability(50))
.rule(rules.method('GET'))
// Get poison
var poison = proxy.getPoison('inject')
poison.isEnabled() // -> true
// Get rule in the poison scope (nested)
var rule = poison.getRule('probability') // -> Directive
rule.isEnabled() // -> true
rule.disable()
poison.isRuleEnabled('probability') // -> false
// Disable poison
proxy.disable('inject')
poison.disable() // Or: proxy.disable('inject')
proxy.isEnabled('inject') // -> false

@@ -29,7 +39,7 @@

// Get registered poisons
proxy.poisons() // -> [ inject ]
proxy.getPoisons() // -> [ inject ]
// Remove the poison
proxy.remove('inject')
proxy.poisons() // -> []
proxy.getPoisons() // -> []

@@ -39,4 +49,8 @@ // Flush all poisons (not necessary, though)

// Get rule
var rule = proxy.getRule('method')
rule.isEnabled() // -> true
// Disable rule
proxy.disableRule('method')
poison.disable() // Or: proxy.disableRule('method')
proxy.isRuleEnabled('method') // -> false

@@ -49,10 +63,10 @@

// Get registered rules
proxy.rules() // -> [ method, probability ]
proxy.getRules() // -> [ method, probability ]
// Remove the rule
proxy.removeRule('method')
proxy.rules() // -> [ probability ]
proxy.getRules() // -> [ probability ]
// Flush all rules (aka remove all)
proxy.flushFlush()
proxy.flush()

@@ -59,0 +73,0 @@ proxy.all('/*')

@@ -1,39 +0,44 @@

const toxy = require('..')
var toxy = require('..')
var poisons = toxy.poisons
var rules = toxy.rules
const proxy = toxy()
const rules = proxy.rules
const poisons = proxy.poisons
var proxy = toxy()
proxy
.forward('http://httpbin.org')
.rule(rules.probability(50))
.poison(poisons.slowOpen({ delay: 500 }))
var route = proxy.get('/*')
// Register global poisons and rules
proxy
.poison(poisons.latency({ jitter: 100 }))
.rule(rules.probability(25))
route
.poison(poisons.latency({ jitter: 1000 }))
// Register multiple routes
proxy
.get('/download/*')
.poison(poisons.bandwidth({ bps: 1024 }))
.withRule(rules.headers({'Authorization': /^Bearer (.*)$/i }))
route
.poison(poisons.inject({ code: 502, body: 'Error!', headers: { 'X-Toxy-Poison': 'error' } }))
.withRule(rules.probability(20))
proxy
.get('/image/*')
.poison(poisons.bandwidth({ bps: 1024 }))
route
proxy
.all('/ip')
.poison(poisons.rateLimit({ limit: 1, threshold: 1000 }))
.withRule(rules.method(['POST', 'PUT', 'DELETE']))
// And use a different more permissive poison for GET requests
.poison(poisons.rateLimit({ limit: 10, threshold: 1000 }))
.withRule(rules.method('GET'))
// Handle the rest of the traffic
proxy
.all('/*')
.poison(poisons.slowClose({ delay: 1000 }))
.withRule(rules.probability(20))
.poison(poisons.slowRead({ bps: 128 }))
.withRule(rules.probability(50))
route
.poison(poisons.rateLimit({ limit: 2, threshold: 5000 }))
.withRule(rules.probability(20))
route
.poison(poisons.slowRead({ bps: 100 }))
.withRule(rules.probability(35))
route
.poison(poisons.abort())
.poisonRule(rules.probability(5)) // does the same as withRule()
.poisonRule(rules.method('GET'))
proxy.listen(3000)
console.log('Server listening on port:', 3000)
console.log('Test it opening:')
console.log('http://localhost:3000/ip')
console.log('http://localhost:3000/image/jpeg')

@@ -28,3 +28,3 @@ const Toxy = require('./lib/toxy')

toxy.VERSION = require('./package.json').version
toxy.VERSION = require('./package.json').version

@@ -38,3 +38,3 @@ /**

poisons.forEach(function (poison) {
Toxy.prototype.poisons[poison.name] = function () {
toxy.poisons[poison.name] = function () {
return poison.apply(null, arguments)

@@ -51,5 +51,5 @@ }

rules.forEach(function (rule) {
Toxy.prototype.rules[rule.name] = function () {
toxy.rules[rule.name] = function () {
return rule.apply(null, arguments)
}
})

@@ -35,7 +35,16 @@ module.exports = Base

Base.prototype._getDirective = function (mw, name) {
var item = this._searchInStack(mw, name)
if (item) {
return item.$of
}
return null
}
Base.prototype._searchInStack = function (mw, name) {
var stack = mw.stack
for (var i = 0, l = stack.length; i < l; i += 1) {
if (stack[i].$name === name || stack[i].$of === name) {
return stack[i]
var node = stack[i]
if (node.$name === name || node.$of === name) {
return node
}

@@ -42,0 +51,0 @@ }

@@ -22,3 +22,3 @@ exports.isRegExp = function isRegExp(o) {

exports.sliceBuffer = function sliceBuffer(size, buffer, encoding, cache) {
exports.sliceBuffer = function sliceBuffer(size, buffer, encoding, target) {
if (!buffer) return

@@ -30,3 +30,3 @@

for (var i = 0; i < length; i += size) {
cache.push({
target.push({
buffer: buffer.slice(i, i + size),

@@ -33,0 +33,0 @@ encoding: encoding

@@ -37,3 +37,2 @@ const Rule = require('./rule')

var self = this
var directive = this.directive

@@ -47,3 +46,3 @@ function handler(req, res, next) {

if (filter === true) return next()
directive(req, res, next)
self.directive(req, res, next)
}

@@ -50,0 +49,0 @@ }

module.exports = function abort(opts) {
opts = opts || {}
var delay = +opts.delay || 1
return function abort(req, res, next) {
setTimeout(function () {
try { destroy() } catch (e) {}
}, opts.delay)
setTimeout(destroy, delay)
function destroy() {
req.destroy(opts.error)
try {
req.destroy(opts.error)
} catch (e) {
next(e)
}
}
}
}
const throttler = require('./throttle')
module.exports = function bandwidth(opts) {
if (typeof opts === 'number') {
opts = { bps: opts }
}
opts = opts || {}
opts.bps = (+opts.bps || 1024)
opts.bps = +opts.bps || 1024
opts.threshold = +opts.threshold || 1000

@@ -7,0 +11,0 @@

module.exports = [
require('./inject'),
require('./abort'),
require('./latency'),
require('./throttle'),
require('./timeout'),
require('./rate-limit'),
require('./slow-close'),
require('./slow-open'),
require('./slow-read'),
require('./bandwidth')
]
'inject',
'abort',
'latency',
'throttle',
'timeout',
'rate-limit',
'slow-close',
'slow-open',
'slow-read',
'bandwidth'
].map(function (module) {
return require('./' + module)
})
module.exports = function rateLimit(opts) {
opts = opts || {}
var limit = +opts.limit || 10
var code = +opts.code || 429
var threshold = +opts.threshold || 1000
var message = opts.message || 'Too many requests'
var code = +opts.code || 429
var current = Date.now()
var remaining = opts.limit
var remaining = limit

@@ -33,3 +33,2 @@ return function rateLimit(req, res, next) {

}
return remaining -= 1

@@ -36,0 +35,0 @@ }

const rocky = require('rocky')
const RockyBase = rocky.Base
const Rule = require('./rule')

@@ -10,2 +9,4 @@ const rules = require('./rules')

var RockyBase = rocky.Base
RockyBase.prototype.rule =

@@ -32,3 +33,3 @@ RockyBase.prototype.filter = Directive.prototype.rule

RockyBase.prototype.remove = function (poison) {
return this._remove(this_poisons, poison)
return this._remove(this._poisons, poison)
}

@@ -45,3 +46,6 @@

RockyBase.prototype.poisons =
RockyBase.prototype.getPoison = function (poison) {
return this._getDirective(this._poisons, poison)
}
RockyBase.prototype.getPoisons = function () {

@@ -48,0 +52,0 @@ return this._getAll(this._poisons)

@@ -26,7 +26,10 @@ const midware = require('midware')

Rule.prototype.isRuleEnabled = function (poison) {
return this._callMethod(this._rules, 'isEnabled', poison)
Rule.prototype.isRuleEnabled = function (rule) {
return this._callMethod(this._rules, 'isEnabled', rule)
}
Rule.prototype.rules =
Rule.prototype.getRule = function (rule) {
return this._getDirective(this._rules, rule)
}
Rule.prototype.getRules = function () {

@@ -33,0 +36,0 @@ return this._getAll(this._rules)

module.exports = [
require('./body'),
require('./method'),
require('./probability'),
require('./headers'),
require('./content-type'),
]
'body',
'method',
'headers',
'probability',
'content-type',
].map(function (module) {
return require('./' + module)
})
module.exports = function probability(num) {
var percent = +Math.min(+num, 100) || 50
var percent = +Math.min(num, 100) || 50

@@ -7,8 +7,7 @@ return function probability(req, res, next) {

var rand = Math.round(Math.random() * 10)
var perc = Math.round(percent * 0.1)
var rand = Math.round(Math.random() * 100)
var notMatches = rand > percent
var notMatches = rand > perc
next(null, notMatches)
}
}

@@ -10,8 +10,3 @@ const midware = require('midware')

this._poisons = midware()
this._setup()
}
Toxy.prototype = Object.create(Proxy.prototype)
Toxy.prototype._setup = function () {
wrapRouteConstructor(this)

@@ -21,10 +16,17 @@ setupMiddleware(this)

Toxy.prototype = Object.create(Proxy.prototype)
function wrapRouteConstructor(self) {
var _route = self.route
self.route = function (method, path) {
self.route = function () {
var route = _route.apply(self, arguments)
// Create toxy route-evel specific middleware
route._rules = midware()
route._poisons = midware()
// Setup middleware and final route handler
setupMiddleware(route)
reDispatchRoute(route)
return route

@@ -31,0 +33,0 @@ }

{
"name": "toxy",
"version": "0.1.2",
"version": "0.1.3",
"description": "Hackable HTTP proxy to simulate server failure scenarios and unexpected conditions",

@@ -5,0 +5,0 @@ "repository": "h2non/toxy",

@@ -5,10 +5,9 @@ # 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)

**toxy** is a **hackable HTTP proxy** to **simulate** **failure scenarios** and **unexpected conditions**.
**toxy** is a **hackable HTTP proxy** to **simulate** server **failure scenarios** and **unexpected network conditions**, built for [node.js](http://nodejs.org)/[io.js](https://iojs.org).
It was mainly designed for fuzz/evil testing purposes, becoming particulary useful to cover fault tolerant and resiliency capabilities of a system, tipically in service-oriented distributed architectures, where `toxy` may act as intermediate proxy among services.
It was mainly designed for fuzzing/evil testing purposes, becoming particulary useful to cover fault tolerance and resiliency capabilities of a system, especially in [service-oriented](http://microservices.io/) 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 evil actions in the middle of that process, such as limiting the bandwidth, delaying TCP packets, injecting network jitter latency or replying with a custom error or status code.
toxy allows you to plug in [poisons](#poisons), optionally filtered by [rules](#rules), which basically can intercept and alter the HTTP flow as you need, performing multiple evil actions in the middle of that process, such as limiting the bandwidth, delaying TCP packets, injecting network jitter latency 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.
toxy is compatible with [connect](https://github.com/senchalabs/connect)/[express](http://expressjs.com), and it was built on top of [rocky](https://github.com/h2non/rocky), a full-featured, middleware-oriented HTTP proxy.

@@ -53,3 +52,3 @@ Requires node.js +0.12 or io.js +1.6

- Full-featured HTTP/S proxy (backed by [http-proxy](https://github.com/nodejistu/node-http-proxy))
- Full-featured HTTP/S proxy (backed by [rocky](https://github.com/h2non/rocky) and [http-proxy](https://github.com/nodejitsu/node-http-proxy))
- Hackable and elegant programmatic API (inspired on connect/express)

@@ -64,3 +63,3 @@ - Featured built-in router with nested configuration

- Built-in balancer and traffic intercept via middleware
- Inherits the API and features from [rocky](https://github.com/h2non/rocky)
- Inherits API and features from [rocky](https://github.com/h2non/rocky)
- Compatible with connect/express (and most of their middleware)

@@ -73,13 +72,13 @@ - Runs as standalone HTTP proxy

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. Additionally, most of the those solutions are based only on the TCP stack only instead of providing more features to the scope of HTTP applicacion level protocol, like toxy does.
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. Additionally, most of the those solutions are based only on the TCP stack, instead of providing features to the specific domain of the HTTP protocol, like toxy does.
`toxy` provides a powerful hacking-driven and extensible solution with a convenient low-level interface and extensible programmatic control, serveds with a simple and fluent API and the power, simplicity and fun of node.js.
`toxy` provides a powerful hackable and extensible solution with a convenient low-level interface and programmatic features based on simple, concise and fluent API, with the power, simplicity and fun of node.js.
### Concepts
`toxy` introduces two main core directives worth knowing before using it:
`toxy` introduces two core directives that you can plug in the proxy and should knowing before using: poisons and rules.
**Poisons** are the specific logic to infect an incoming or outgoing HTTP flow (e.g: injecting a latency, replying with an error). HTTP flow can be poisoned by one or multiple poisons, and you can plug in poisons at global or route level.
**Poisons** are the specific logic to infect an incoming or outgoing HTTP flow (e.g: injecting a latency, replying with an error). HTTP flow can be poisoned by one or multiple poisons, and poisons can be plugged to infect both global or route level incoming traffic.
**Rules** are a kind of validation filters that are applied to the whole global HTTP flow or to a concrete poison, in order to determine if one or multiple poisons should be enabled or not to infect the HTTP traffic (e.g: match headers, query params, method...).
**Rules** are a kind of validation filters that can be reused and applied to global incoming HTTP traffic, route level traffic or into a specific poison. Their responsability is to determine, via inspecting each incoming HTTP request, if the registered poisons should be enabled or not, and therefore infecting or not the HTTP traffic (e.g: match headers, query params, method, body...).

@@ -92,3 +91,3 @@ ### How it works

↓ ---------------- ↓
↓ | Toxy Router | ↓ --> Match a route based on the incoming request
↓ | Toxy Router | ↓ --> Match the incoming request
↓ ---------------- ↓

@@ -99,5 +98,5 @@ ↓ ||| ↓

↓ ---------------- ↓
↓ ||| ↓
↓ ||| ↓
↓ ---------------- ↓
↓ | Exec Poisons | ↓ --> If all rules passed, poisoning the HTTP flow
↓ | Exec Poisons | ↓ --> If all rules passed, then poison the HTTP flow
↓ ---------------- ↓

@@ -107,3 +106,3 @@ ↓ / \ ↓

↓ ------------------- ↓
↓ | HTTP dispatcher | ↓ --> Proxy the HTTP traffic for both poisoned or not
↓ | HTTP dispatcher | ↓ --> Proxy the HTTP traffic, either poisoned or not
↓ ------------------- ↓

@@ -124,4 +123,2 @@ ```

#### Basic poisioning
```js

@@ -137,10 +134,35 @@ var toxy = require('toxy')

// Register global poisons and rules
proxy
.poison(poisons.latency({ jitter: 500 }))
.rule(rules.random(50))
.rule(rules.probability(25))
// Register multiple routes
proxy
.get('/download/*')
.poison(poisons.bandwidth({ bps: 1024 }))
.withRule(rules.headers({'Authorization': /^Bearer (.*)$/i }))
proxy
.get('/image/*')
.poison(poisons.bandwidth({ bps: 512 }))
proxy
.all('/api/*')
.poison(poisons.rateLimit({ limit: 10, threshold: 1000 }))
.withRule(rules.method(['POST', 'PUT', 'DELETE']))
// And use a different more permissive poison for GET requests
.poison(poisons.rateLimit({ limit: 50, threshold: 1000 }))
.withRule(rules.method('GET'))
proxy.get('/*')
// Handle the rest of the traffic
proxy
.all('/*')
.poison(poisons.slowClose({ delay: 1000 }))
.poison(poisons.slowRead({ bps: 128 }))
.withRule(rules.probability(50))
proxy.listen(3000)
console.log('Server listening on port:', 3000)
console.log('Test it:', 'http://localhost:3000/image/jpeg')
```

@@ -166,3 +188,3 @@

- **options** `object`
- **jitter*+ `number` - Jitter value in miliseconds
- **jitter** `number` - Jitter value in miliseconds
- **max** `number` - Random jitter maximum value

@@ -185,3 +207,3 @@ - **min** `number` - Random jitter minimum value

- **options** `object`
- **code*+ `number` - Response HTTP status code
- **code** `number` - Response HTTP status code
- **headers** `object` - Optional headers to send

@@ -204,10 +226,12 @@ - **body** `mixed` - Optional body data to send

This poison is basically an alias to [throttle](#throttle).
**Arguments**:
- **options** `object`
- **bps*+ `number` - Bytes per seconds
- **threshold** `number` - Threshold time frame in miliseconds
- **bps** `number` - Bytes per second. Default to `1024`
- **threshold** `number` - Limit time frame in miliseconds. Default `1000`
```js
toxy.poison(toxy.poisons.bandwidth({ bps: 1024 }))
toxy.poison(toxy.poisons.bandwidth({ bps: 512 }))
```

@@ -218,5 +242,5 @@

Limits the amount of requests received by the proxy in a specific threshold time frame. Designed to test API limits. Exposes the `X-RateLimit-*` headers.
Limits the amount of requests received by the proxy in a specific threshold time frame. Designed to test API limits. Exposes typical `X-RateLimit-*` headers.
Limits are stored in-memory, meaning they are volalite and therfore flushed on every server stop.
Note that this is very simple rate limit implementation, indeed limits are stored in-memory, therefore are completely volalite. There're a bunch of more featured and consistent rate limiter implementations in [npm](https://www.npmjs.com/search?q=rate+limit) that you can plug in as poison.

@@ -226,9 +250,9 @@ **Arguments**:

- **options** `object`
- **limit*+ `number` - Total amount of request
- **threshold** `number` - Limit threshold time frame in miliseconds.
- **limit** `number` - Total amount of request. Default to `10`
- **threshold** `number` - Limit threshold time frame in miliseconds. Default to `1000`
- **message** `string` - Optional error message when limit reached.
- **code** `number` - HTTP status code when limit reached. Default to 429.
- **code** `number` - HTTP status code when limit reached. Default to `429`.
```js
toxy.poison(toxy.poisons.rateLimit({ limit: 10, threshold: 1000 }))
toxy.poison(toxy.poisons.rateLimit({ limit: 5, threshold: 10 * 1000 }))
```

@@ -268,3 +292,3 @@

Delays the HTTP connection close signal.
Delays the HTTP connection close signal (EOF).

@@ -283,7 +307,9 @@ **Arguments**:

Restricts the amount of packets sent over the network in a specific threshold time frame.**Arguments**:
Restricts the amount of packets sent over the network in a specific threshold time frame.
**Arguments**:
- **options** `object`
- **chunk*+ `number` - Packet chunk size in bytes. Default to `1024`
- **threshold** `object` - Limit threshold time frame in miliseconds. Default to `1000`
- **chunk** `number` - Packet chunk size in bytes. Default to `1024`
- **threshold** `object` - Limit threshold time frame in miliseconds. Default to `100`

@@ -358,3 +384,4 @@ ```js

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 useful to compose, decouple and reuse logic among different scenarios of poisoning.
Rules can be applied to the global, route or even poison scope.

@@ -505,11 +532,29 @@ Rules are executed in FIFO order. Their evaluation logic is equivalent to `Array#every()` in JavaScript: all the rules must pass in order to proceed with the poisoning.

### toxy.poisons `=>` Object
Exposes a map with the built-in poisons.
### toxy.rules `=>` Object
Exposes a map with the built-in rules.
### toxy.VERSION `=>` String
Current toxy semantic version.
#### toxy#get(path, [ middleware... ])
Return: `ToxyRoute`
Register a new route for `GET` method.
#### toxy#post(path, [ middleware... ])
Return: `ToxyRoute`
Register a new route for `POST` method.
#### toxy#put(path, [ middleware... ])
Return: `ToxyRoute`
Register a new route for `PUT` method.
#### toxy#patch(path, [ middleware... ])

@@ -521,69 +566,164 @@ Return: `ToxyRoute`

Register a new route for `DELETE` method.
#### toxy#head(path, [ middleware... ])
Return: `ToxyRoute`
Register a new route for `HEAD` method.
#### toxy#all(path, [ middleware... ])
Return: `ToxyRoute`
Register a new route for any method.
#### toxy#poisons `=>` Object
Exposes a map with the built-in poisons. Prototype alias to `toxy.poisons`
#### toxy#rules `=>` Object
Exposes a map with the built-in poisons. Prototype alias to `toxy.rules`
#### toxy#forward(url)
Define a URL to forward the incoming traffic received by the proxy.
#### toxy#balance(urls)
Forward to multiple servers balancing among them.
For more information, see the [rocky docs](https://github.com/h2non/rocky#programmatic-api)
#### toxy#replay(url)
Define a new replay server.
You can call this method multiple times to define multiple replay servers.
For more information, see the [rocky docs](https://github.com/h2non/rocky#programmatic-api)
#### toxy#use(middleware)
Plug in a custom middleware.
For more information, see the [rocky docs](https://github.com/h2non/rocky#middleware-layer).
#### toxy#useResponse(middleware)
Plug in a response outgoing traffic middleware.
For more information, see the [rocky docs](https://github.com/h2non/rocky#middleware-layer).
#### toxy#useReplay(middleware)
Plug in a replay traffic middleware.
For more information, see the [rocky docs](https://github.com/h2non/rocky#middleware-layer)
#### toxy#middleware()
Return a standard middleware to use with connect/express.
#### toxy#listen(port)
Starts the built-in HTTP server, listening on a specific TCP port.
#### toxy#close([ callback ])
Closes the HTTP server.
#### toxy#poison(poison)
Alias: `usePoison`
Register a new poison.
#### toxy#rule(rule)
Alias: `useRule`
Register a new rule.
#### toxy#withRule(rule)
Aliases: `poisonRule`, `poisonFilter`
Apply a new rule for the latest registered poison.
#### toxy#enable(poison)
Enable a poison by name identifier
#### toxy#disable(poison)
Disable a poison by name identifier
#### toxy#remove(poison)
Return: `boolean`
Remove poison by name identifier.
#### toxy#isEnabled(poison)
Return: `boolean`
Checks if a poison is enabled by name identifier.
#### toxy#disableAll()
Alias: `disablePoisons`
#### toxy#poisons()
Return: `array<Directive>` Alias: `getPoisons`
Disable all the registered poisons.
#### toxy#getPoison(poison)
Return: `Directive|null`
Searchs and retrieves a registered poison in the stack by name identifier.
#### toxy#getPoisons()
Return: `array<Directive>`
Return an array of registered poisons wrapped as `Directive`.
#### toxy#flush()
Alias: `flushPoisons`
Remove all the registered poisons.
#### toxy#enableRule(rule)
Enable a rule by name identifier.
#### toxy#disableRule(rule)
Disable a rule by name identifier.
#### toxy#removeRule(rule)
Return: `boolean`
Remove a rule by name identifier.
#### toxy#disableRules()
Disable all the registered rules.
#### toxy#isRuleEnabled(rule)
Return: `boolean`
#### toxy#rules()
Return: `array<Directive>` Alias: `getRules`
Checks if the given rule is enabled by name identifier.
#### toxy#getRule(rule)
Return: `Directive|null`
Searchs and retrieves a registered rule in the stack by name identifier.
#### toxy#getRules()
Return: `array<Directive>`
Returns and array with the registered rules wrapped as `Directive`.
#### toxy#flushRules()
Remove all the rules.
### 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.
Toxy route has, indeed, the same interface as `Toxy` global interface, it just adds some route level [additional methods](https://github.com/h2non/rocky#routepath).
Further actions you perform againts the `ToxyRoute` API will only be applicable at route-level (nested). In other words: you already know the API.
This example will probably clarify possible doubts:

@@ -596,2 +736,3 @@ ```js

proxy
.forward('http://server.net')
.poison(toxy.poisons.bandwidth({ bps: 1024 }))

@@ -601,3 +742,7 @@ .rule(toxy.rules.method('GET'))

// Now create a route
var route = proxy.get('/foo')
var route = proxy
.get('/foo')
.toPath('/bar') // Route-level API method
.host('server.net') // Route-level API method
.forward('http://new.server.net')

@@ -604,0 +749,0 @@ // Now using the ToxyRoute interface

@@ -16,3 +16,3 @@ const http = require('http')

expect(err).to.be.undefined
expect(Date.now() - init).to.be.at.least(delay)
expect(Date.now() - init).to.be.at.least(delay - 1)
done()

@@ -19,0 +19,0 @@ }

@@ -39,6 +39,6 @@ const http = require('http')

test('close', function (done) {
test('premature close', function (done) {
var req = new http.IncomingMessage
var res = new http.OutgoingMessage
var threshold = 5
var threshold = 10
var spy = sinon.spy()

@@ -63,3 +63,3 @@ var init = Date.now()

function assert() {
expect(Date.now() - init).to.be.within(0, 2)
expect(Date.now() - init).to.be.within(0, 5)
expect(spy.args).to.have.length(3)

@@ -66,0 +66,0 @@ done()

@@ -43,3 +43,3 @@ const http = require('http')

var res = new http.OutgoingMessage
var opts = { chunk: 1024, threshold: 5 }
var opts = { chunk: 1024, threshold: 10 }

@@ -53,3 +53,3 @@ var buf = []

expect(buffer).to.have.length(body.length)
expect(Date.now() - lastWrite).to.be.at.least(opts.threshold)
expect(Date.now() - lastWrite).to.be.at.least(opts.threshold - 1)
buf.push(buffer)

@@ -56,0 +56,0 @@ next()

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

@@ -56,19 +57,68 @@ const toxy = require('..')

test('e2e', function (done) {
test('get directives', function () {
var proxy = toxy()
var server = createServer(9001, 200)
var called = false
proxy.poison(function delay() {})
proxy.rule(function match() {})
expect(proxy.isEnabled('delay')).to.be.true
expect(proxy.isRuleEnabled('match')).to.be.true
var poison = proxy.getPoison('delay')
expect(poison).to.be.an('object')
expect(poison.isEnabled()).to.be.true
poison.disable()
expect(poison.isEnabled()).to.be.false
var rule = proxy.getRule('match')
expect(rule).to.be.an('object')
expect(rule.isEnabled()).to.be.true
rule.disable()
expect(rule.isEnabled()).to.be.false
})
test('flush directives', function () {
var proxy = toxy()
var called = false
proxy.poison(function delay() {})
proxy.rule(function match() {})
expect(proxy.isEnabled('delay')).to.be.true
expect(proxy.isRuleEnabled('match')).to.be.true
proxy.flush()
expect(proxy.isEnabled('delay')).to.be.false
expect(proxy.getPoison('delay')).to.be.null
expect(proxy.getPoisons()).to.have.length(0)
proxy.flushRules()
expect(proxy.isRuleEnabled('match')).to.be.false
expect(proxy.getRule('match')).to.be.null
expect(proxy.getRules()).to.have.length(0)
})
test('basic proxy', function (done) {
var proxy = toxy()
var spy = sinon.spy()
var server = createServer(9081, 200)
var timeout = 100
proxy.poison(toxy.poisons.latency(timeout))
proxy.poison(function delay(req, res, next) {
spy(req, res)
setTimeout(next, timeout)
})
proxy.rule(function method(req, res, next) {
next(req.method === 'GET' ? null : true)
spy(req, res)
next(null, req.method !== 'GET')
})
proxy.forward('http://localhost:9001')
proxy.forward('http://localhost:9081')
proxy.get('/foo')
proxy.listen(9000)
proxy.listen(9080)
var init = Date.now()
supertest('http://localhost:9000')
supertest('http://localhost:9080')
.get('/foo')

@@ -82,2 +132,42 @@ .expect(200)

expect(Date.now() - init).to.be.at.least(timeout - 1)
expect(spy.calledTwice).to.be.true
expect(spy.args[0][0].url).to.be.equal('/foo')
expect(spy.args[0][0].method).to.be.equal('GET')
server.close()
proxy.close(done)
}
})
test('final route handler when no matches', function (done) {
var proxy = toxy()
var spy = sinon.spy()
var server = createServer(9081, 200)
var timeout = 100
proxy.poison(function delay(req, res, next) {
throw 'Should not be called'
})
proxy.rule(function method(req, res, next) {
spy(req, res)
next(null, true)
})
proxy.forward('http://localhost:9081')
proxy.get('/foo')
proxy.listen(9080)
var init = Date.now()
supertest('http://localhost:9080')
.get('/foo')
.expect(200)
.expect('Content-Type', 'application/json')
.expect({ hello: 'world' })
.end(assert)
function assert(err) {
expect(spy.calledOnce).to.be.true
expect(spy.args[0][0].url).to.be.equal('/foo')
expect(spy.args[0][0].method).to.be.equal('GET')
done(err)

@@ -84,0 +174,0 @@ }

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc