{"id":13452217,"url":"https://github.com/h2non/toxy","last_synced_at":"2026-01-14T11:20:44.227Z","repository":{"id":35645451,"uuid":"39920057","full_name":"h2non/toxy","owner":"h2non","description":"Hackable HTTP proxy for resiliency testing and simulated network conditions","archived":true,"fork":false,"pushed_at":"2022-02-17T16:26:15.000Z","size":308,"stargazers_count":2721,"open_issues_count":15,"forks_count":82,"subscribers_count":48,"default_branch":"master","last_synced_at":"2025-11-22T20:12:27.714Z","etag":null,"topics":["failover","failure","http-proxy","network","proxy","reactive","resiliency","retry","simulation","testing"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/h2non.png","metadata":{"files":{"readme":"README.md","changelog":"History.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-29T22:51:42.000Z","updated_at":"2025-11-01T21:50:05.000Z","dependencies_parsed_at":"2022-06-27T14:32:09.503Z","dependency_job_id":null,"html_url":"https://github.com/h2non/toxy","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/h2non/toxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Ftoxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Ftoxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Ftoxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Ftoxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/h2non","download_url":"https://codeload.github.com/h2non/toxy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Ftoxy/sbom","scorecard":{"id":451625,"data":{"date":"2025-08-11","repo":{"name":"github.com/h2non/toxy","commit":"95ff4630e5de16c96d744467e595031382d05c71"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 2/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T08:19:11.776Z","repository_id":35645451,"created_at":"2025-08-19T08:19:11.776Z","updated_at":"2025-08-19T08:19:11.776Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28418236,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:47:48.104Z","status":"ssl_error","status_checked_at":"2026-01-14T10:46:19.031Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["failover","failure","http-proxy","network","proxy","reactive","resiliency","retry","simulation","testing"],"created_at":"2024-07-31T07:01:17.198Z","updated_at":"2026-01-14T11:20:44.204Z","avatar_url":"https://github.com/h2non.png","language":"JavaScript","readme":"# toxy [![Build Status](https://api.travis-ci.org/h2non/toxy.svg?branch=master\u0026style=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) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com)\n\n\u003e **Not actively maintained, may not work with latest node.js runtimes**. If you are interested in maintaining toxy, please open an issue.\n\n**Hackable HTTP proxy** to **simulate** server **failure scenarios**, **systems resiliency testing** and **unexpected network conditions**, built for [node.js](http://nodejs.org).\n\n\u003cimg align=\"right\" height=\"180\" src=\"http://s8.postimg.org/ikc9jxllh/toxic.jpg\" /\u003e\n\nIt was mainly designed for failure resistance testing, when toxy becomes particularly useful in order to cover fault tolerance and resiliency capabilities of a system, especially in [disruption-tolerant networks](https://en.wikipedia.org/wiki/Delay-tolerant_networking) and [service-oriented](http://microservices.io/patterns/index.html) architectures, where toxy may act as MitM proxy among services in order to inject failure.\n\ntoxy allows you to plug in [poisons](#poisons), optionally filtered by [rules](#rules), which essentially 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 network packets, injecting network jitter latency or replying with a custom error or status code.\nIt primarily operates at L7, although it can simulate L3 network conditions.\n\ntoxy can be fluently used [programmatically](#programmatic-api) or via [HTTP API](#http-api).\nIt was built on top of [rocky](https://github.com/h2non/rocky), a full-featured middleware-oriented HTTP proxy, and it's also [pluggable](https://github.com/h2non/toxy/blob/master/examples/express.js) in [connect](https://github.com/senchalabs/connect)/[express](http://expressjs.com) as standard middleware.\n\nRequires node.js +4.\n\n## Contents\n\n- [Features](#features)\n- [Introduction](#introduction)\n  - [Why toxy?](#why-toxy)\n  - [Concepts](#concepts)\n  - [How it works](#how-it-works)\n- [Usage](#usage)\n  - [Installation](#installation)\n  - [Examples](#examples)\n- [Benchmark](#benchmark)\n- [Poisons](#poisons)\n  - [Poisoning scopes](#poisoning-scopes)\n  - [Poisoning phases](#poisoning-phases)\n  - [Built-in poisons](#built-in-poisons)\n    - [Latency](#latency)\n    - [Inject response](#inject-response)\n    - [Bandwidth](#bandwidth)\n    - [Rate limit](#rate-limit)\n    - [Slow read](#slow-read)\n    - [Slow open](#slow-open)\n    - [Slow close](#slow-close)\n    - [Throttle](#throttle)\n    - [Abort connection](#abort-connection)\n    - [Timeout](#timeout)\n  - [How to write poisons](#how-to-write-poisons)\n- [Rules](#rules)\n  - [Built-in rules](#built-in-rules)\n    - [Probability](#probability)\n    - [Time threshold](#time-threshold)\n    - [Method](#method)\n    - [Content Type](#content-type)\n    - [Headers](#headers)\n    - [Response headers](#response-headers)\n    - [Body](#body)\n    - [Response body](#response-body)\n    - [Response status](#response-status)\n  - [Third-party rules](#third-party-rules)\n  - [How to write rules](#how-to-write-rules)\n- [Programmatic API](#programmatic-api)\n- [HTTP API](#http-api)\n  - [Usage](#usage)\n  - [Authorization](#authorization)\n  - [API](#api)\n  - [Programmatic API](#programmatic-api-1)\n- [License](#license)\n\n## Features\n\n- Full-featured HTTP/S proxy (backed by [rocky](https://github.com/h2non/rocky) and [http-proxy](https://github.com/nodejitsu/node-http-proxy))\n- Hackable and elegant programmatic API (inspired on connect/express)\n- Admin HTTP API for external management and dynamic configuration\n- Featured built-in router with nested configuration\n- Hierarchical and composable poisoning with rule based filtering\n- Hierarchical middleware layer (both global and route scopes)\n- Easily augmentable via middleware (based on connect/express middleware)\n- Supports both incoming and outgoing traffic poisoning\n- Built-in poisons (bandwidth, error, abort, latency, slow read...)\n- Rule-based poisoning (probabilistic, HTTP method, headers, body...)\n- Supports third-party poisons and rules\n- Built-in balancer and traffic interceptor via middleware\n- Inherits API and features from [rocky](https://github.com/h2non/rocky)\n- Compatible with connect/express (and most of their middleware)\n- Able to run as standalone HTTP proxy\n\n## Introduction\n\n### Why toxy?\n\nThere're some other similar solutions like `toxy` in the market, but most of them do not provide a proper programmatic control and usually are not easy to hack, configure or are directly closed to extensibility.\n\nFurthermore, the majority of those solutions only operates at TCP L3 level stack instead of providing high-level abstractions to cover common requirements in the specific domain and nature of the HTTP L7 protocol, like toxy tries to provide\n\ntoxy brings a powerful hackable and extensible solution with a convenient abstraction, but without losing a proper low-level interface capabilities to deal with HTTP protocol primitives easily.\n\ntoxy was designed based on the rules of composition, simplicity and extensibility.\nVia its built-in hierarchical domain specific middleware layer you can easily augment toxy features to your own needs.\n\n### Concepts\n\n`toxy` introduces two directives: poisons and rules.\n\n**Poisons** are the specific logic which infects an incoming or outgoing HTTP transaction (e.g: injecting a latency, replying with an error). One HTTP transaction can be poisoned by one or multiple poisons, and those poisons can be also configured to infect both global or route level traffic.\n\n**Rules** are a kind of match validation filters that inspects an HTTP request/response in order to determine, given a certain rules, if the HTTP transaction should be poisoned or not (e.g: if headers matches, query params, method, body...).\nRules can be reused and applied to both incoming and outgoing traffic flows, including different scopes: global, route or poison level.\n\n### How it works\n\n```\n↓  ( Incoming request )  ↓\n↓          |||           ↓\n↓    +-------------+     ↓\n↓    | Toxy Router |     ↓ -\u003e Match the incoming request\n↓    +-------------+     ↓\n↓          |||           ↓\n↓ +--------------------+ ↓\n↓ |   Incoming phase   | ↓ -\u003e The proxy receives the request from the client\n↓ |~~~~~~~~~~~~~~~~~~~~| ↓\n↓ |  ----------------  | ↓\n↓ |  |  Exec Rules  |  | ↓ -\u003e Apply configured rules for the incoming request\n↓ |  ----------------  | ↓\n↓ |        |||         | ↓\n↓ |  ----------------  | ↓\n↓ |  | Exec Poisons |  | ↓ -\u003e If all rules passed, then poison the HTTP flow\n↓ |  ----------------  | ↓\n↓ +~~~~~~~~~~~~~~~~~~~~+ ↓\n↓        /      \\        ↓\n↓        \\      /        ↓\n↓ +--------------------+ ↓\n↓ |  HTTP dispatcher   | ↓ -\u003e Forward the HTTP traffic to the target server, either poisoned or not\n↓ +--------------------+ ↓\n↓        /      \\        ↓\n↓        \\      /        ↓\n↓ +--------------------+ ↓\n↓ |   Outgoing phase   | ↓ -\u003e Receives response from target server\n↓ |~~~~~~~~~~~~~~~~~~~~| ↓\n↓ |  ----------------  | ↓\n↓ |  |  Exec Rules  |  | ↓ -\u003e Apply configured rules for the outgoing request\n↓ |  ----------------  | ↓\n↓ |        |||         | ↓\n↓ |  ----------------  | ↓\n↓ |  | Exec Poisons |  | ↓ -\u003e If all rules passed, then poison the HTTP flow before send it to the client\n↓ |  ----------------  | ↓\n↓ +~~~~~~~~~~~~~~~~~~~~+ ↓\n↓          |||           ↓\n↓ ( Send to the client ) ↓ -\u003e Finally, send the request to the client, either poisoned or not\n```\n\n## Usage\n\n### Installation\n\n```\nnpm install toxy\n```\n\n### Examples\n\nSee [examples](https://github.com/h2non/toxy/tree/master/examples) directory for more use cases.\n\n```js\nvar toxy = require('toxy')\nvar poisons = toxy.poisons\nvar rules = toxy.rules\n\n// Create a new toxy proxy\nvar proxy = toxy()\n\n// Default server to forward incoming traffic\nproxy\n  .forward('http://httpbin.org')\n\n// Register global poisons and rules\nproxy\n  .poison(poisons.latency({ jitter: 500 }))\n  .rule(rules.probability(25))\n\n// Register multiple routes\nproxy\n  .get('/download/*')\n  .forward('http://files.myserver.net')\n  .poison(poisons.bandwidth({ bps: 1024 }))\n  .withRule(rules.headers({'Authorization': /^Bearer (.*)$/i }))\n\n// Infect outgoing traffic only (after the server replied properly)\nproxy\n  .get('/image/*')\n  .outgoingPoison(poisons.bandwidth({ bps: 512 }))\n  .withRule(rules.method('GET'))\n  .withRule(rules.timeThreshold({ duration: 1000, threshold: 1000 * 10 }))\n  .withRule(rules.responseStatus({ range: [ 200, 400 ] }))\n\nproxy\n  .all('/api/*')\n  .poison(poisons.rateLimit({ limit: 10, threshold: 1000 }))\n  .withRule(rules.method(['POST', 'PUT', 'DELETE']))\n  // And use a different more permissive poison for GET requests\n  .poison(poisons.rateLimit({ limit: 50, threshold: 1000 }))\n  .withRule(rules.method('GET'))\n\n// Handle the rest of the traffic\nproxy\n  .all('/*')\n  .poison(poisons.slowClose({ delay: 1000 }))\n  .poison(poisons.slowRead({ bps: 128 }))\n  .withRule(rules.probability(50))\n\nproxy.listen(3000)\nconsole.log('Server listening on port:', 3000)\nconsole.log('Test it:', 'http://localhost:3000/image/jpeg')\n```\n\n## Benchmark\n\nSee [toxy/benchmark](https://github.com/h2non/toxy/tree/master/benchmark) for details.\n\n## Poisons\n\nPoisons host specific logic which intercepts and mutates, wraps, modify and/or cancel an HTTP transaction in the proxy server.\nPoisons can be applied to incoming or outgoing, or even both traffic flows (see [poison phases](#poisoning-phases)).\n\nPoisons can be composed and reused for different HTTP scenarios.\nThey are executed in FIFO order and asynchronously.\n\n### Poisoning scopes\n\n`toxy` has a hierarchical design based on two different scopes: `global` and `route`.\n\n**Global** scope points to all the incoming HTTP traffic received by the proxy server, regardless of the HTTP method or path.\n\n**Route** scope points to any incoming traffic which matches with a specific HTTP verb and URI path.\n\nPoisons can be plugged to both scopes, meaning you can operate with better accuracy and restrict the scope of the poisoning,\nfor instance, you might wanna apply a bandwidth limit poisoning only to\na certain routes, such as `/download` or `/images`.\n\nSee [routes.js](https://github.com/h2non/toxy/blob/master/examples/routes.js) for a featured example.\n\n### Poisoning phases\n\nPoisons can be plugged to incoming or outgoing traffic flows, or even both.\n\n**Incoming** poisoning is applied when the traffic has been received by proxy\nbut it has not been forwarded to the target server yet.\n\n**Outgoing** poisoning refers to the traffic that has been forwarded to the target server and\nwhen proxy receives the response from it, but that response has not been sent to the client yet.\n\nThis means, essentially, that you can plug in your poisons to infect the HTTP traffic\nbefore or after the request is forwarded to the target HTTP server or sent to the client.\n\nThis allows you apply a better and more accurated poisoning based on the request or server response.\nFor instance, given the nature of some poisons, like `inject error`,\nyou may want to enable it according to the target server response (e.g: some header is present or not).\n\nSee [poison-phases.js](https://github.com/h2non/toxy/blob/master/examples/poison-phases.js) for a featured example.\n\n### Built-in poisons\n\n#### Latency\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003elatency\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nInfects the HTTP flow injecting a latency jitter in the response\n\n**Arguments**:\n\n- **options** `object`\n  - **jitter** `number` - Jitter value in milliseconds\n  - **max** `number` - Random jitter maximum value\n  - **min** `number` - Random jitter minimum value\n\n```js\ntoxy.poison(toxy.poisons.latency({ jitter: 1000 }))\n// Or alternatively using a random value\ntoxy.poison(toxy.poisons.latency({ max: 1000, min: 100 }))\n```\n\n#### Inject response\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003einject\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003efalse (only as incoming poison)\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nInjects a custom response, intercepting the request before sending it to the target server.\nUseful to inject errors originated in the server.\n\n**Arguments**:\n\n- **options** `object`\n  - **code** `number` - Response HTTP status code. Default `500`\n  - **headers** `object` - Optional headers to send\n  - **body** `mixed` - Optional body data to send. It can be a `buffer` or `string`\n  - **encoding** `string` - Body encoding. Default to `utf8`\n\n```js\ntoxy.poison(toxy.poisons.inject({\n  code: 503,\n  body: '{\"error\": \"toxy injected error\"}',\n  headers: {'Content-Type': 'application/json'}\n}))\n```\n\n#### Bandwidth\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003ebandwidth\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nLimits the amount of bytes sent over the network in outgoing HTTP traffic for a specific time frame.\n\nThis poison is basically an alias to [throttle](#throttle).\n\n**Arguments**:\n\n- **options** `object`\n  - **bytes** `number` - Amount of chunk of bytes to send. Default `1024`\n  - **threshold** `number` - Packets time frame in milliseconds. Default `1000`\n\n```js\ntoxy.poison(toxy.poisons.bandwidth({ bytes: 512 }))\n```\n\n#### Rate limit\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003erateLimit\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nLimits the amount of requests received by the proxy in a specific threshold time frame. Designed to test API limits. Exposes typical `X-RateLimit-*` headers.\n\nNote that this is very simple rate limit implementation, indeed limits are stored in-memory, therefore are completely volatile.\nThere're a bunch of featured and consistent rate limiter implementations in [npm](https://www.npmjs.com/search?q=rate+limit) that you can plug in as poison. You might be also interested in [token bucket algorithm](http://en.wikipedia.org/wiki/Token_bucket).\n\n**Arguments**:\n\n- **options** `object`\n  - **limit** `number` - Total amount of requests. Default to `10`\n  - **threshold** `number` - Limit time frame in milliseconds. Default to `1000`\n  - **message** `string` - Optional error message when limit is reached.\n  - **code** `number` - HTTP status code when limit is reached. Default to `429`.\n\n```js\ntoxy.poison(toxy.poisons.rateLimit({ limit: 5, threshold: 10 * 1000 }))\n```\n\n#### Slow read\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eslowRead\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nReads incoming payload data packets slowly. Only valid for non-GET request.\n\n**Arguments**:\n\n- **options** `object`\n  - **chunk** `number` - Packet chunk size in bytes. Default to `1024`\n  - **threshold** `number` - Limit threshold time frame in milliseconds. Default to `1000`\n\n```js\ntoxy.poison(toxy.poisons.slowRead({ chunk: 2048, threshold: 1000 }))\n```\n\n#### Slow open\nName: `slowOpen`\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eslowOpen\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nDelays the HTTP connection ready state.\n\n**Arguments**:\n\n- **options** `object`\n  - **delay** `number` - Delay connection in milliseconds. Default to `1000`\n\n```js\ntoxy.poison(toxy.poisons.slowOpen({ delay: 2000 }))\n```\n\n#### Slow close\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eslowClose\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nDelays the HTTP connection close signal (EOF).\n\n**Arguments**:\n\n- **options** `object`\n  - **delay** `number` - Delay time in milliseconds. Default to `1000`\n\n```js\ntoxy.poison(toxy.poisons.slowClose({ delay: 2000 }))\n```\n\n#### Throttle\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003ethrottle\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nRestricts the amount of packets sent over the network in a specific threshold time frame.\n\n**Arguments**:\n\n- **options** `object`\n  - **chunk** `number` - Packet chunk size in bytes. Default to `1024`\n  - **delay** `object` - Data chunk delay time frame in milliseconds. Default to `100`\n\n```js\ntoxy.poison(toxy.poisons.throttle({ chunk: 2048, threshold: 1000 }))\n```\n\n#### Abort connection\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eabort\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003efalse (only as incoming poison)\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nAborts the TCP connection. 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.\n\n**Arguments**:\n\n- **options** `object`\n  - **delay** `number` - Aborts TCP connection after waiting the given milliseconds. Default to `0`\n  - **next** `boolean` - If `true`, the connection will be aborted if the target server takes more than the `delay` param time to reply. Default to `false`\n  - **error** `Error` - Custom internal node.js error to use when destroying the socket. Default to `null`\n\n```js\n// Basic connection abort\ntoxy.poison(toxy.poisons.abort())\n// Abort after a delay\ntoxy.poison(toxy.poisons.abort(1000))\n// In this case, the socket will be closed if\n// the target server takes more than\n// 2 seconds to respond\ntoxy.poison(toxy.poisons.abort({ delay: 2000, next: true }))\n```\n\n#### Timeout\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etimeout\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoisoning Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eReaches the server\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etrue\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nDefines a response timeout. Useful when forward to potentially slow servers.\n\n**Arguments**:\n\n- **milliseconds** `number` - Timeout limit in milliseconds\n\n```js\ntoxy.poison(toxy.poisons.timeout(5000))\n```\n\n### How to write poisons\n\nPoisons are implemented as standard middleware function with the same interface as connect/express middleware.\n\nSome poisons are not trivial to implement so you've to be familiar with node.js [http](https://nodejs.org/api/http.html) module and its API.\n\nHere's a simple example of a server latency poison:\n\n```js\nvar toxy = require('toxy')\n\nfunction customLatencyPoison (delay) {\n  // We name the function since toxy uses it as identifier to get/disable/remove it in the future\n  return function customLatency (req, res, next) {\n    var timeout = setTimeout(process, delay)\n    req.once('close', onClose)\n\n    function onClose () {\n      clearTimeout(timeout)\n      next('client connection closed')\n    }\n\n    function process () {\n      req.removeListener('close', onClose)\n      next()\n    }\n  }\n}\n\nvar proxy = toxy()\n\n// Register and enable the poison\nproxy\n  .get('/foo')\n  .poison(customLatencyPoison(2000))\n```\n\nYou can optionally extend the build-in poisons with your own poisons:\n\n```js\ntoxy.addPoison(customLatency)\n\n// Then you can use it as a built-in poison\nproxy\n  .get('/foo')\n  .poison(toxy.poisons.customLatency)\n```\n\nFor featured real example, take a look to the [built-in poisons](https://github.com/h2non/toxy/tree/master/lib/poisons) implementation.\n\n## Rules\n\nRules are simple validation filters which inspects an incoming or outgoing HTTP traffic in order to determine, given a certain rules (e.g: matches the method, headers, query params, body...), if the current HTTP transaction should be poisoned or not, based on the resolution value of the rule.\n\nRules are useful to compose, decouple and reuse logic among different scenarios of poisoning.\nRules can be applied to global, route or even poison scope, and it also applies to both [phases of poisoning](#poisoning-phases).\n\nRules 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.\n\n### Built-in rules\n\n#### Probability\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eprobability\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nEnables the rule by a random probabilistic. Useful for random poisoning.\n\n**Arguments**:\n\n- **percentage** `number` - Percentage of filtering. Default `50`\n\n```js\nvar rule = toxy.rules.probability(85)\ntoxy.rule(rule)\n```\n\n#### Time threshold\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003etimeThreshold\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nSimple rule to enable poisons based on a specific time threshold and duration.\nFor instance, you can enable a certain poisons during a specific amount of time (e.g: 1 second) within a time threshold (e.g: 1 minute).\n\n**Arguments**:\n\n- **options** `object`\n  - **duration** `number` - Enable time interval in milliseconds. Default to `1000`\n  - **threshold** `number` - Time threshold in milliseconds to wait before re-enable the poisoning. Default to `10000`\n\n```js\n// Enable the poisoning only 100 milliseconds per each 10 seconds\nproxy.rule(toxy.rules.timeThreshold(100))\n// Enable poisoning during 1 second every minute\nproxy.rule(toxy.rules.timeThreshold({ duration: 1000, period: 1000 * 60 }))\n```\n\n#### Method\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003emethod\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nFilters by HTTP method.\n\n**Arguments**:\n\n- **method** `string|array` - Method or methods to filter.\n\n```js\nvar method = toxy.rules.method(['GET', 'POST'])\ntoxy.rule(method)\n```\n\n#### Content Type\n\nFilters by content type header. It should be present\n\n**Arguments**:\n\n- **value** `string|regexp` - Header value to match.\n\n```js\nvar rule = toxy.rules.contentType('application/json')\ntoxy.rule(rule)\n```\n\n#### Headers\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eheaders\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nFilter by request headers.\n\n**Arguments**:\n\n- **headers** `object` - Headers to match by key-value pair. `value` can be a string, regexp, `boolean` or `function(headerValue, headerName) =\u003e boolean`\n\n```js\nvar matchHeaders = {\n  'content-type': /^application/\\json/i,\n  'server': true, // meaning it should be present,\n  'accept': function (value, key) {\n    return value.indexOf('text') !== -1\n  }\n}\n\nvar rule = toxy.rules.headers(matchHeaders)\ntoxy.rule(rule)\n```\n\n#### Response headers\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eresponseHeaders\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eoutgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nFilter by response headers from target server. Same as `headers` rule, but evaluating the outgoing request.\n\n**Arguments**:\n\n- **headers** `object` - Headers to match by key-value pair. `value` can be a `string`, `regexp`, `boolean` or `function(headerValue, headerName) =\u003e boolean`\n\n```js\nvar matchHeaders = {\n  'content-type': /^application/\\json/i,\n  'server': true, // meaning it should be present,\n  'accept': function (value, key) {\n    return value.indexOf('text') !== -1\n  }\n}\n\nvar rule = toxy.rules.responseHeaders(matchHeaders)\ntoxy.rule(rule)\n```\n\n#### Body\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003ebody\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eincoming / outgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nMatch incoming body payload by a given `string`, `regexp` or custom filter `function`.\n\nThis rule is pretty simple, so for complex body matching (e.g: validating against a JSON schema)\nyou should probably write your own rule.\n\n**Arguments**:\n\n- **match** `string|regexp|function` - Body content to match\n- **limit** `string` - Optional. Body limit in human size. E.g: `5mb`\n- **encoding** `string` - Body encoding. Default to `utf8`\n- **length** `number` - Body length. Default taken from `Content-Length` header\n\n```js\nvar rule = toxy.rules.body('\"hello\":\"world\"')\ntoxy.rule(rule)\n\n// Or using a filter function returning a boolean\nvar rule = toxy.rules.body(function contains(body) {\n  return body.indexOf('hello') !== -1\n})\ntoxy.rule(rule)\n```\n\n#### Response body\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eresponseBody\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eoutgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nMatch outgoing body payload by a given `string`, `regexp` or custom filter `function`.\n\n**Arguments**:\n\n- **match** `string|regexp|function` - Body content to match\n- **encoding** `string` - Body encoding. Default to `utf8`\n- **length** `number` - Body length. Default taken from `Content-Length` header\n\n```js\nvar rule = toxy.rules.responseBody('\"hello\":\"world\"')\ntoxy.rule(rule)\n\n// Or using a filter function returning a boolean\nvar rule = toxy.rules.responseBody(function contains(body) {\n  return body.indexOf('hello') !== -1\n})\ntoxy.rule(rule)\n```\n\n#### Response status\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eName\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eresponseStatus\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003ePoison Phase\u003c/b\u003e\u003c/td\u003e\u003ctd\u003eoutgoing\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nEvaluates the response status from the target server.\nOnly applicable to outgoing poisons.\n\n**Arguments**:\n\n- **range** `array` - Pair of status code range to match. Default `[200, 300]`.\n- **lower** `number` - Compare status as `lower than` operation. Default to `null`.\n- **higher** `number` - Compare status as `higher than` operation. Default to `null`.\n- **value** `number` - Status code to match using a strict equality comparison. Default `null`.\n- **include** `array` - Unordered list of status codes to match. Useful to specify custom status. Default `null`\n\n```js\n// Strict evaluation of the status code\ntoxy.rule(toxy.rules.responseBody(200))\n// Using a range of valid status\ntoxy.rule(toxy.rules.responseBody([200, 204]))\n// Using relational comparison\ntoxy.rule(toxy.rules.responseBody({ higher: 199, lower: 400 }))\n// Custom unordered status code to match\ntoxy.rule(toxy.rules.responseBody({ include: [200, 204, 400, 404] }))\n```\n\n### Third-party rules\n\nList of available third-party rules provided by the community. PR are welcome.\n\n- [IP](https://github.com/h2non/toxy-ip) - Enable/disable poisons based on the client IP address (supports CIDR, subnets, ranges...).\n\n### How to write rules\n\nRules are simple middleware functions that resolve asynchronously with a `boolean` value to determine if a given HTTP transaction should be ignored when poisoning.\n\nYour rule must resolve with a `boolean` param calling the `next(err,\nshouldIgnore)` function in the middleware, passing a `true` value if the rule has not matches and should not apply the poisoning, and therefore continuing with the next middleware stack.\n\nHere's an example of a simple rule matching the HTTP method to determine if:\n```js\nvar toxy = require('toxy')\n\nfunction customMethodRule(matchMethod) {\n  /**\n   * We name the function since it's used by toxy to identify the rule to get/disable/remove it in the future\n   */\n  return function customMethodRule(req, res, next) {\n    var shouldIgnore = req.method !== matchMethod\n    next(null, shouldIgnore)\n  }\n}\n\nvar proxy = toxy()\n\n// Register and enable the rule\nproxy\n  .get('/foo')\n  .rule(customMethodRule('GET'))\n  .poison(/* ... */)\n```\n\nYou can optionally extend the build-in rules with your own rules:\n\n```js\ntoxy.addRule(customMethodRule)\n\n// Then you can use it as a built-in poison\nproxy\n  .get('/foo')\n  .rules(toxy.rules.customMethodRule)\n```\n\nFor featured real examples, take a look to the built-in rules [implementation](https://github.com/h2non/toxy/tree/master/lib/rules)\n\n## Programmatic API\n\n`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`.\n\n### toxy([ options ])\n\nCreate a new `toxy` proxy.\n\nFor supported `options`, please see rocky [documentation](https://github.com/h2non/rocky#configuration)\n\n```js\nvar toxy = require('toxy')\n\ntoxy({ forward: 'http://server.net', timeout: 30000 })\n\ntoxy\n  .get('/foo')\n  .poison(toxy.poisons.latency(1000))\n  .withRule(toxy.rules.contentType('json'))\n  .forward('http://foo.server')\n\ntoxy\n  .post('/bar')\n  .poison(toxy.poisons.bandwidth({ bps: 1024 }))\n  .withRule(toxy.rules.probability(50))\n  .forward('http://bar.server')\n\ntoxy\n  .post('/boo')\n  .outgoingPoison(toxy.poisons.bandwidth({ bps: 1024 }))\n  .withRule(toxy.rules.method('GET'))\n  .forward('http://boo.server')\n\ntoxy.all('/*')\n\ntoxy.listen(3000)\n```\n\n#### toxy#get(path, [ middleware... ])\nReturn: `ToxyRoute`\n\nRegister a new route for `GET` method.\n\n#### toxy#post(path, [ middleware... ])\nReturn: `ToxyRoute`\n\nRegister a new route for `POST` method.\n\n#### toxy#put(path, [ middleware... ])\nReturn: `ToxyRoute`\n\nRegister a new route for `PUT` method.\n\n#### toxy#patch(path, [ middleware... ])\nReturn: `ToxyRoute`\n\n#### toxy#delete(path, [ middleware... ])\nReturn: `ToxyRoute`\n\nRegister a new route for `DELETE` method.\n\n#### toxy#head(path, [ middleware... ])\nReturn: `ToxyRoute`\n\nRegister a new route for `HEAD` method.\n\n#### toxy#all(path, [ middleware... ])\nReturn: `ToxyRoute`\n\nRegister a new route for any method.\n\n#### toxy#poisons `=\u003e` Object\n\nExposes a map with the built-in poisons. Prototype alias to `toxy.poisons`\n\n#### toxy#rules `=\u003e` Object\n\nExposes a map with the built-in poisons. Prototype alias to `toxy.rules`\n\n#### toxy#forward(url)\n\nDefine a URL to forward the incoming traffic received by the proxy.\n\n#### toxy#balance(urls)\n\nForward to multiple servers balancing among them.\n\nFor more information, see the [rocky docs](https://github.com/h2non/rocky#programmatic-api)\n\n#### toxy#replay(url)\n\nDefine a new replay server.\nYou can call this method multiple times to define multiple replay servers.\n\nFor more information, see the [rocky docs](https://github.com/h2non/rocky#programmatic-api)\n\n#### toxy#use(middleware)\n\nPlug in a custom middleware.\n\nFor more information, see the [rocky docs](https://github.com/h2non/rocky#middleware-layer).\n\n#### toxy#useResponse(middleware)\n\nPlug in a response outgoing traffic middleware.\n\nFor more information, see the [rocky docs](https://github.com/h2non/rocky#middleware-layer).\n\n#### toxy#useReplay(middleware)\n\nPlug in a replay traffic middleware.\n\nFor more information, see the [rocky docs](https://github.com/h2non/rocky#middleware-layer)\n\n#### toxy#requestBody(middleware)\n\nIntercept incoming request body. Useful to modify it on the fly.\n\nFor more information, see the [rocky docs](https://github.com/h2non/rocky#programmatic-api)\n\n#### toxy#responseBody(middleware)\n\nIntercept outgoing response body. Useful to modify it on the fly.\n\nFor more information, see the [rocky docs](https://github.com/h2non/rocky#programmatic-api)\n\n#### toxy#middleware()\n\nReturn a standard middleware to use with connect/express.\n\n#### toxy#host(host)\n\nOverwrite the `Host` header with a custom value. Similar to `forwardHost` option.\n\n#### toxy#redirect(url)\n\nRedirect traffic to the given URL.\n\n#### toxy#findRoute(routeIdOrPath, [ method ])\n\nFind a route by ID or path and method.\n\n#### toxy#listen(port)\n\nStarts the built-in HTTP server, listening on a specific TCP port.\n\n#### toxy#close([ callback ])\n\nCloses the HTTP server.\n\n#### toxy#poison(poison)\nAlias: `usePoison`, `useIncomingPoison`\n\nRegister a new poison to infect [incoming](#poisoning-phases) traffic.\n\n#### toxy#outgoingPoison(poison)\nAlias: `useOutgoingPoison`, `responsePoison`\n\nRegister a new poison to infect [outgoing](#poisoning-phases) traffic.\n\n#### toxy#rule(rule)\nAlias: `useRule`\n\nRegister a new rule.\n\n#### toxy#withRule(rule)\nAliases: `ifRule`, `whenRule`, `poisonRule`, `poisonFilter`\n\nApply a new rule for the latest registered poison.\n\n#### toxy#enable(poison)\n\nEnable a poison by name identifier\n\n#### toxy#disable(poison)\n\nDisable a poison by name identifier\n\n#### toxy#remove(poison)\nReturn: `boolean`\n\nRemove an incoming traffic poison by name identifier or object reference.\n\n#### toxy#removeOutgoing(poison)\nReturn: `boolean`\n\nRemove an outgoing traffic poison by name identifier or object reference.\n\n#### toxy#isEnabled(poison)\nReturn: `boolean`\n\nChecks if a poison is enabled by name identifier.\n\n#### toxy#disableAll()\nAlias: `disablePoisons`\n\nDisable all the registered poisons.\n\n#### toxy#getPoison(name)\nReturn: `Directive|null`\n\nSearches and retrieves a registered poison in the stack by name identifier.\n\n#### toxy#getIncomingPoison(name)\nReturn: `Directive|null`\n\nSearches and retrieves a registered `incoming` poison in the stack by name identifier.\n\n#### toxy#getOutgoingPoison(name)\nReturn: `Directive|null`\n\nSearches and retrieves a registered `outgoing` poison in the stack by name identifier.\n\n#### toxy#getPoisons()\nReturn: `array\u003cDirective\u003e`\n\nReturn an array of registered poisons.\n\n#### toxy#getIncomingPoisons()\nReturn: `array\u003cDirective\u003e`\n\nReturn an array of registered `incoming` poisons.\n\n#### toxy#getOutgoingPoisons()\nReturn: `array\u003cDirective\u003e`\n\nReturn an array of registered `outgoing` poisons.\n\n#### toxy#flush()\nAlias: `flushPoisons`\n\nRemove all the registered poisons for both incoming and outgoing traffic flows.\n\n#### toxy#enableRule(rule)\n\nEnable a rule by name identifier.\n\n#### toxy#disableRule(rule)\n\nDisable a rule by name identifier.\n\n#### toxy#removeRule(rule)\nReturn: `boolean`\n\nRemove a rule by name identifier.\n\n#### toxy#disableRules()\n\nDisable all the registered rules.\n\n#### toxy#isRuleEnabled(rule)\nReturn: `boolean`\n\nChecks if the given rule is enabled by name identifier.\n\n#### toxy#getRule(rule)\nReturn: `Directive|null`\n\nSearches and retrieves a registered rule in the stack by name identifier.\n\n#### toxy#getRules()\nReturn: `array\u003cDirective\u003e`\n\nReturns and array with the registered rules wrapped as `Directive`.\n\n#### toxy#flushRules()\n\nRemove all the rules.\n\n### toxy.addPoison(name, fn)\n\nExtend built-in poisons.\n\n### toxy.addRule(name, fn)\n\nExtend built-in rules.\n\n### toxy.poisons `=\u003e` Object\n\nExposes a map with the built-in poisons.\n\n### toxy.rules `=\u003e` Object\n\nExposes a map with the built-in rules.\n\n### toxy.VERSION `=\u003e` String\n\nCurrent toxy semantic version.\n\n### ToxyRoute\n\n`ToxyRoute` exposes the same interface as `Toxy` global interface, it just adds some route level [additional methods](https://github.com/h2non/rocky#routepath).\n\nFurther actions you perform against the `ToxyRoute` API will only be applicable at route-level (nested). In other words: you already know the API.\n\nThis example will probably clarify possible doubts:\n```js\nvar toxy = require('toxy')\nvar proxy = toxy()\n\n// Now using the global API\nproxy\n  .forward('http://server.net')\n  .poison(toxy.poisons.bandwidth({ bps: 1024 }))\n  .rule(toxy.rules.method('GET'))\n\n// Now create a route\nvar route = proxy\n  .get('/foo')\n  .toPath('/bar') // Route-level API method\n  .host('server.net') // Route-level API method\n  .forward('http://new.server.net')\n\n// Now using the ToxyRoute interface\nroute\n  .poison(toxy.poisons.bandwidth({ bps: 512 }))\n  .rule(toxy.rules.contentType('json'))\n```\n\n### Directive(middlewareFn)\n\nA convenient wrapper internally used for poisons and rules.\n\nNormally you don't need to know this interface, but for hacking purposes or more low-level actions might be useful.\n\n#### Directive#enable()\nReturn: `boolean`\n\n#### Directive#disable()\nReturn: `boolean`\n\n#### Directive#isEnabled()\nReturn: `boolean`\n\n#### Directive#rule(rule)\nAlias: `filter`\n\n#### Directive#handler()\nReturn: `function(req, res, next)`\n\n## HTTP API\n\nThe `toxy` HTTP API follows the [JSON API](http://jsonapi.org) conventions, including resource based hypermedia linking.\n\n### Usage\n\nFor a featured use case, see the [admin server](examples/admin.js) example.\n\n```js\nconst toxy = require('toxy')\n\n// Create the toxy admin server\nvar admin = toxy.admin({ cors: true })\nadmin.listen(9000)\n\n// Create the toxy proxy\nvar proxy = toxy()\nproxy.listen(3000)\n\n// Add the toxy instance to be managed by the admin server\nadmin.manage(proxy)\n\n// Then configure the proxy\nproxy\n  .forward('http://my.target.net')\n\nproxy\n  .get('/slow')\n  .poison(toxy.poisons.bandwidth({ bps: 1024 }))\n\n// Handle the rest of the traffic\nproxy\n  .all('/*')\n  .poison(toxy.poisons.bandwidth({ bps: 1024 * 5 }))\n\nconsole.log('toxy proxy listening on port:', 3000)\nconsole.log('toxy admin server listening on port:', 9000)\n```\n\nFor more details about the admin programmatic API, see [below](#programmatic-api-1).\n\n### Authorization\n\nThe HTTP API can be protected to unauthorized clients.\nAuthorized clients must define the API key token via `API-Key` or `Authorization` HTTP headers.\n\nTo enable it, you should simple pass the following options to `toxy` admin server:\n\n```js\nconst toxy = require('toxy')\n\nconst opts = { apiKey: 's3cr3t' }\nvar admin = toxy.admin(opts)\n\nadmin.listen(9000)\nconsole.log('protected toxy admin server listening on port:', 9000)\n```\n\n### API\n\n**Hierarchy**:\n\n- **Servers** - Managed `toxy` instances\n  - **Rules** - Globally applied rules\n  - **Poisons** - Globally applied poisons\n    - **Rules** - Poison-specific rules\n  - **Routes** - List of configured routes\n    - **Route** - Object for each specific route\n      - **Rules** - Route-level registered rules\n      - **Poisons** - Route-level registered poisons\n        - **Rules** - Route-level poison-specific rules\n\n#### GET /\n\n### Servers\n\n#### GET /servers\n\n#### GET /servers/:id\n\n### Rules\n\n#### GET /servers/:id/rules\n\n#### POST /servers/:id/rules\nAccepts: `application/json`\n\nExample payload:\n```js\n{\n  \"name\": \"method\",\n  \"options\": \"GET\"\n}\n```\n\n#### DELETE /servers/:id/rules\n\n#### GET /servers/:id/rules/:id\n\n#### DELETE /servers/:id/rules/:id\n\n### Poisons\n\n#### GET /servers/:id/poison\n\n#### POST /servers/:id/poisons\nAccepts: `application/json`\n\nExample payload:\n```js\n{\n  \"name\": \"latency\",\n  \"phase\": \"outgoing\",\n  \"options\": { \"jitter\": 1000 }\n}\n```\n\n#### DELETE /servers/:id/poisons\n\n#### GET /servers/:id/poisons/:id\n\n#### DELETE /servers/:id/poisons/:id\n\n#### GET /servers/:id/poisons/:id/rules\n\n#### POST /servers/:id/poisons/:id/rules\nAccepts: `application/json`\n\nExample payload:\n```js\n{\n  \"name\": \"method\",\n  \"options\": \"GET\"\n}\n```\n\n#### DELETE /servers/:id/poisons/:id/rules\n\n#### GET /servers/:id/poisons/:id/rules/:id\n\n#### DELETE /servers/:id/poisons/:id/rules/:id\n\n### Routes\n\n#### GET /servers/:id/routes\n\n#### POST /servers/:id/routes\nAccepts: `application/json`\n\nExample payload:\n```js\n{\n  \"path\": \"/foo\", // Required\n  \"method\": \"GET\", // use ALL for all the methods\n  \"forward\": \"http://my.server\", // Optional custom forward server URL\n}\n```\n\n#### DELETE /servers/:id/routes\n\n#### GET /servers/:id/routes/:id\n\n#### DELETE /servers/:id/routes/:id\n\n### Route rules\n\n#### GET /servers/:id/routes/:id/rules\n\n#### POST /servers/:id/routes/:id/rules\nAccepts: `application/json`\n\nExample payload:\n```js\n{\n  \"name\": \"method\",\n  \"options\": \"GET\"\n}\n```\n\n#### DELETE /servers/:id/routes/:id/rules\n\n#### GET /servers/:id/routes/:id/rules/:id\n\n#### DELETE /servers/:id/routes/:id/rules/:id\n\n### Route poisons\n\n#### GET /servers/:id/routes/:id/poisons\n\n#### POST /servers/:id/routes/:id/poisons\nAccepts: `application/json`\n\nExample payload:\n```js\n{\n  \"name\": \"latency\",\n  \"phase\": \"outgoing\",\n  \"options\": { \"jitter\": 1000 }\n}\n```\n\n#### DELETE /servers/:id/routes/:id/poisons\n\n#### GET /servers/:id/routes/:id/poisons/:id\n\n#### DELETE /servers/:id/routes/:id/poisons/:id\n\n#### GET /servers/:id/routes/:id/poisons/:id/rules\n\n#### POST /servers/:id/routes/:id/poisons/:id/rules\n\nAccepts: `application/json`\n\nExample payload:\n```js\n{\n  \"name\": \"method\",\n  \"options\": \"GET\"\n}\n```\n\n#### DELETE /servers/:id/routes/:id/poisons/:id/rules\n\n#### GET /servers/:id/routes/:id/poisons/:id/rules/:id\n\n#### DELETE /servers/:id/routes/:id/poisons/:id/rules/:id\n\n### Programmatic API\n\nThe built-in HTTP admin server also provides a simple interface open to extensibility and hacking purposes.\nFor instance, you can plug in additional middleware to the admin server, or register new routes.\n\n#### toxy.admin([ opts ])\nReturns: `Admin`\n\n**Supported options**:\n\n- **apiKey** `string` - Optional API key to protect the server\n- **port** `number` - Optional. TCP port to listen\n- **cors** `boolean` - Enable CORS for web browser access\n- **middleware** `array\u003cfunction\u003e` - Plug in additional middleware\n- **ssl** `object` - Node.js HTTPS server [TLS options](https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener).\n\n##### Admin#listen([ port, host ])\n\nStart listening on the network.\n\n##### Admin#manage(toxy)\n\nManage a `toxy` server instance.\n\n##### Admin#find(toxy)\n\nFind a toxy instance. Accepts toxy server ID or toxy instance.\n\n##### Admin#remove(toxy)\n\nStop managing a toxy instance.\n\n##### Admin#use(...middleware)\n\nRegister a middleware.\n\n##### Admin#param(...middleware)\n\nRegister a param middleware.\n\n##### Admin#get(path, [ ...middleware ])\n\nRegister a GET route.\n\n##### Admin#post(path, [ ...middleware ])\n\nRegister a POST route.\n\n##### Admin#put(path, [ ...middleware ])\n\nRegister a PUT route.\n\n##### Admin#delete(path, [ ...middleware ])\n\nRegister a DELETE route.\n\n##### Admin#patch(path, [ ...middleware ])\n\nRegister a PATCH route.\n\n##### Admin#all(path, [ ...middleware ])\n\nRegister a route accepting any HTTP method.\n\n##### Admin#middleware(req, res, next)\n\nMiddleware to plug in with connect/express.\n\n##### Admin#close(cb)\n\nStop the server.\n\n## License\n\nMIT - Tomas Aparicio\n\n[![views](https://sourcegraph.com/api/repos/github.com/h2non/toxy/.counters/views.svg)](https://sourcegraph.com/github.com/h2non/toxy)\n","funding_links":[],"categories":["Packages","JavaScript","包","目录","Exploitation","testing","Testing","Number"],"sub_categories":["Testing","测试","测试相关","Network"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh2non%2Ftoxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fh2non%2Ftoxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh2non%2Ftoxy/lists"}