A multipurpose, full-featured, middleware-oriented and hackable HTTP/S and WebSocket proxy with powerful built-in features such as versatile routing layer, traffic interceptor and replay to multiple backends, built-in balancer, traffic retry/backoff logic, hierarchical configuration, among others. Built for node.js/io.js.
rocky can be fluently used programmatically or via command-line interface. It's framework agnostic, but you can optionally plug in with connect/express apps.
To get started, take a look to how does it work, basic usage, middleware layer and examples.
Note: retry feature is temporary not available in latest node.js versions.
- Features
- When rocky can be useful?
- Installation
- Benchmark
- About
- Middleware layer
- Command-line
- Programmatic API
- Special thanks
- Full-featured HTTP/S proxy (backed by http-proxy)
- Supports WebSocket protocol proxy (replay not supported yet)
- Able to replay traffic to multiple backends (concurrently or sequentially)
- Able to intercept HTTP requests and responses and modify them on-the-fly
- Featured built-in path based router with params matching
- Built-in load balancer
- Built-in HTTP traffic retry/backoff
- Nested configuration per global/route scopes and forward/replay phases
- Hierarchical middleware layer supporting different HTTP traffic flow phases
- Easily integrable with connect/express via middleware
- Able to run as standalone HTTP/S server (no connect/express, uses
httpmodule) - Compatible with most of the existent connect/express middleware
- Powerful programmatic control supporting dynamic configurations and zero-downtime
- Supports both concurrent and sequential HTTP traffic flow modes
- Small hackable core designed for extensibility
- Fluent, elegant and evented programmatic API
- Provides a command-line interface with declarative configuration file
- Handles properly
gzipresponses, especially when intercepting payloads
- As intermediate proxy for service migrations (e.g: APIs)
- Replaying traffic to one or multiple backends
- As reverse proxy to forward traffic to one o multiple servers.
- As Man-in-the-Middle proxy intercepting and transforming the request/response on-the-fly
- As intermediate HTTP proxy adapter for external services integrations
- As HTTP API gateway
- As standard reverse HTTP proxy with dynamic routing
- As security proxy layer
- As dynamic HTTP load balancer with programmatic control
- As embedded HTTP proxy in your node.js app
- As HTTP cache or log server
- As SSL terminator proxy
- As HTTP proxy for performance testing
- As traditional forward HTTP proxy (e.g: Squid)
- For HTTP session manipulation and debugging
- For HTTP traffic recording and inspection
- For A/B testing
- For fuzz testing (see toxy)
- As intermediate test server intercepting and generating random/fake responses
- And whatever a programmatic HTTP proxy can be useful to
npm install rocky --saveSee benchmark/README.md for detailed benchmark results.
- 0.1.x - First version. Initially released at
25.06.2015. Beta - 0.2.x - Released at
07.07.2015. Major features and stability improvements. - 0.3.x - Released at
24.07.2015. Production-focused version. - 0.4.x - Released at
02.10.2015. Introduces WebSocket support and other minor features. Stable & actively maintained. Recommended version.
|==============|
| HTTP clients |
|==============|
||||
|==============|
| HTTP proxy | -> Via the built-in HTTP server or via connect/express
|~~~~~~~~~~~~~~|
| Rocky Router | -> The built-in featured router matches the proper route
|~~~~~~~~~~~~~~|
| Middleware | -> Dispatch the hierarchical middleware layer
|==============|
|| |
(duplex) // \ (one-way)
// \
/----------\ /----------\ /----------\
| target | | replay 1 | -> | replay 2 | (*N)
\----------/ \----------/ \----------/
- toxy - Hackable HTTP proxy to simulate server failures and network conditions.
- balboa - Simple, hackable HTTP forward proxy.
Open an issue or send a PR to add your project!
One of the most powerful features in rocky is its build-in domain specific middleware, based on connect/express middleware.
The middleware layer provides a simple and consistent way to augment the proxy functionality very easily, allowing you to attach third-party middleware (also known as plugins) to cover specific tasks which acts between different phases of the proxy, for instance handling incoming/outgoing traffic.
rocky middleware layer has the same interface as connect/express middleware, and it's mostly compatible with existent middleware (see express example).
rocky supports multiple middleware hierarchies:
- global - Dispatched on every incoming request matched by the router
- route - Dispatched only at route scope
rocky introduces multiple types of middleware layers based on the same interface and behavior of connect/express middleware.
This was introduced in order to achieve in a more responsive way multiple traffic flows in the specific scope
and behavior nature of a programmatic HTTP proxy with traffic replay.
Those flows are intrinsically correlated but might be handled in a completely different way. The goal is to allowing you to handle them accordingly, acting in the middle of those phases to augment some functionality or react to some event with better accuracy.
Supported types of middleware:
- Scope:
global - Description: Dispatched on every matched route.
- Notation:
.use([path], function (req, res, next))
- Scope:
global,route - Description: Dispatched before forwarding an incoming request.
- Notation:
.useForward(function (req, res, next))
- Scope:
global,route - Description: Dispatched before starting each replay request.
- Notation:
.useReplay(function (req, res, next))
- Scope:
global,route - Description: Dispatched on server response. Only applicable in
forwardtraffic. - Notation:
.useResponse(function (req, res, next))
- Scope:
global - Description: Dispatched on every matched param on any route.
- Notation:
.useParam(function (req, res, next))
Middleware functions are always executed in FIFO order. The following diagram represents the internal incoming request flow and how the different middleware layers are involved on it:
↓ ( Incoming request ) ↓
↓ ||| ↓
↓ ---------------- ↓
↓ | Router | ↓ --> Match a route, dispatching its middleware if required
↓ ---------------- ↓
↓ ||| ↓
↓ --------------------- ↓
↓ | Global middleware | ↓ --> Dispatch on every incoming request (router, param)
↓ --------------------- ↓
↓ ||| ↓
↓ / \ ↓
↓ / \ ↓
↓ / \ ↓
↓ [ Forward ] [ Replay ] ↓ --> Dispatch both middleware in separated flows (route forward and replay)
↓ \ / ↓
↓ \ / ↓
↓ \ / ↓
↓ ------------------- ↓
↓ | HTTP dispatcher | ↓ --> Send requests over the network (concurrently or sequentially)
↓ ------------------- ↓
Middleware layer behaves and has the same interface as connect/express.
In other words, you can create or use middleware as you already know with the typical notation function(req, res, next)
As a kind of inversion of control, rocky exposes a tiny API in every http.ClientRequest passed via the middleware layer:
- req.rocky
object- .options
object- Exposes the configuration options for the current request. - .proxy
Rocky- Exposes the rocky instance. Use only for hacking purposes! - .route
Route- Exposes the current running route. Only available inroutetype middleware
- .options
- req.stopReplay
boolean- Optional field internally checked byrockyto stop the request replay process.
- res.rocky
object- .options
object- Exposes the configuration options for the current request. - .proxy
Rocky- Exposes the rocky instance. Use only for hacking purposes! - .route
Route- Exposes the current running route. Only available inroutetype middleware
- .options
Example replacing the target server URL:
rocky()
.get('/users/:name')
.forward('http://old.server.net')
.use(function (req, res, next) {
if (req.params.name === 'admin') {
// Overwrite the target URL only for this user
req.rocky.options.target = 'http://new.server.net'
}
next()
})- consul - Dynamic service discovery and balancing using Consul
- vhost - vhost based proxy routing for rocky
- version - HTTP API version based routing (uses http-version)
Note that you can use any other existent middleware plug in rocky as part of your connect/express app.
Additionally, rocky provides some built-in middleware functions that you can plug in different types of middleware.
For command-line usage, you must install rocky-cli
npm install -g rocky-cli
Start rocky HTTP proxy server
Usage: rocky [options]
Options:
--help, -h Show help [boolean]
--config, -c File path to TOML config file
--port, -p rocky HTTP server port
--forward, -f Default forward server URL
--replay, -r Define a replay server URL
--route, -t Define one or multiple routes, separated by commas
--key, -k Path to SSL key file
--cert, -e Path to SSL certificate file
--secure, -s Enable SSL certification validation
--balance, -b Define server URLs to balance between, separated by commas
--mute, -m Disable HTTP traffic log in stdout [boolean]
--debug, -d Enable debug mode [boolean]
-v, --version Show version number [boolean]
Examples:
rocky -c rocky.toml \
-f http://127.0.0.1:9000 \
-r http://127.0.0.1Passing the config file:
rocky --config rocky.toml --port 8080
Reading config from stdin:
cat rocky.toml | rocky --port 8080
Transparent rocky.toml file discovery in current and higher directories:
rocky --port 8080
Alternatively rocky can find the config file passing the ROCKY_CONFIG environment variable:
ROCKY_CONFIG=path/to/rocky.toml rocky --port 8080
Or for simple configurations you can setup a proxy without a config file, defining the routes via flag:
rocky --port --forward http://server --route "/download/*, /images/*, /*"
Default configuration file name: rocky.toml
The configuration file must be declared in TOML language
port = 8080
forward = "http://google.com"
replay = ["http://duckduckgo.com"]
[ssl]
cert = "server.crt"
key = "server.key"
["/users/:id"]
method = "all"
forward = "http://new.server"
["/oauth"]
method = "all"
forward = "http://auth.server"
["/*"]
method = "GET"
forward = "http://old.server"
["/download/:file"]
method = "GET"
timeout = 5000
balance = ["http://1.file.server", "http://2.file.server"]
["/photo/:name"]
method = "GET"
replayAfterForward = true
[[replay]]
target = "http://old.server"
forwardHost = true
[[replay]]
target = "http://backup.server"Example using Express
var rocky = require('rocky')
var express = require('express')
// Set up the express server
var app = express()
// Set up the rocky proxy
var proxy = rocky()
// Default proxy config
proxy
.forward('http://new.server')
.replay('http://old.server')
.replay('http://log.server')
.options({ forwardHost: true })
// Configure the routes to forward/replay
proxy
.get('/users/:id')
proxy
.get('/download/:file')
.balance(['http://1.file.server', 'http://2.file.server'])
// Plug in the rocky middleware
app.use(proxy.middleware())
// Old route (won't be called since it will be intercepted by rocky)
app.get('/users/:id', function () { /* ... */ })
app.listen(3000)Example using the built-in HTTP server
var rocky = require('rocky')
var proxy = rocky()
// Default proxy config
proxy
.forward('http://new.server')
.replay('http://old.server', { replayOriginalBody: true })
.options({ forwardHost: true })
.on('proxy:error', function (err) {
console.error('Error:', err)
})
.on('proxyReq', function (proxyReq, req, res, opts) {
console.log('Proxy request:', req.url, 'to', opts.target)
})
.on('proxyRes', function (proxyRes, req, res) {
console.log('Proxy response:', req.url, 'with status', res.statusCode)
})
// Configure the routes to forward/replay
proxy
.get('/users/:id')
// Overwrite the path
.toPath('/profile/:id', { id: '0123' })
// Add custom headers
.headers({
'Authorization': 'Bearer 0123456789'
})
proxy
.get('/search')
// Overwrite the forward URL for this route
.forward('http://another.server')
// Use a custom middleware for validation purposes
.use(function (req, res, next) {
if (req.headers['Authorization'] !== 'Bearer 012345678') {
res.statusCode = 401
return res.end()
}
next()
})
// Intercept and transform the response body before sending it to the client
.transformResponseBody(function (req, res, next) {
// Get the body buffer and parse it (assuming it's a JSON)
var body = JSON.parse(res.body.toString())
// Compose the new body
var newBody = JSON.stringify({ salutation: 'hello ' + body.hello })
// Send the new body in the request
next(null, newBody)
})
proxy.listen(3000)For more usage cases, take a look to the examples
Supported configuration params:
- forward
string- Default forward URL - debug
boolean- Enable debug mode. Defaultfalse - target
string- <url string to be parsed with the url module - replay
array<string|object>- Optional replay server URLs. You can use thereplay()method to configure it - ws
boolean- Enable WebSocket proxy mode. - balance
array<url>- Define the URLs to balance. Via API you should use thebalance()method - timeout
number- Timeout for request socket - proxyTimeout
number- Timeout for proxy request socket - retry
object- Enable retry/backoff logic for forward/replay traffic. See allowed params. Default:null - replayRetry
object- Enable retry logic for replay traffic with custom options. Default:null - agent
object- object to be passed to http(s).request. See node.jshttpsdocs - ssl
object- object to be passed to https.createServer()- cert
string- Path to SSL certificate file - key
string- Path to SSL key file
- cert
- ws
boolean- true/false, if you want to proxy websockets - xfwd
boolean- true/false, adds x-forward headers - secure
boolean- true/false, verify SSL certificate - toProxy
boolean- true/false, explicitly specify if we are proxying to another proxy - prependPath
boolean- true/false, Default: true - specify whether you want to prepend the target's path to the proxy path - ignorePath
boolean- true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request - localAddress
boolean- <Local interface string to bind for outgoing connections - changeOrigin
boolean- <true/false, Default: false - changes the origin of the host header to the target URL - auth
string- Basic authentication i.e. 'user:password' to compute an Authorization header. - hostRewrite
string- rewrites the location hostname on (301/302/307/308) redirects, Default: null. - autoRewrite
boolean- rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false. - protocolRewrite
string- rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null. - forwardOriginalBody
boolean- Only valid for forward request. Forward the original body instead of the transformed one. - replayOriginalBody
boolean- Only valid for replay request. Forward the original body instead of the transformed one. - router
object- Specific router params- strict
boolean- Whenfalsetrailing slashes are optional (default:false) - caseSensitive
boolean- Whentruethe routing will be case sensitive. (default:false) - mergeParams
boolean- Whentrueanyreq.paramspassed to the router will be merged into the router'sreq.params. (default:false)
- strict
Creates a new rocky instance with the given options.
You can pass any of the allowed params at configuration level and any supported http-proxy options
Aliases: target, forwardTo
Define a default target URL to forward the request
Alias: replayTo
Add a server URL to replay the incoming request
opts param provide specific replay options, overwritting the parent options.
Note: replay feature is only valid for HTTP traffic.
Define/overwrite rocky server options.
You can pass any of the supported options by http-proxy.
Define the proxy protocol operation mode.
Supported options are: http, ws
Alias: useIncoming
Use the given middleware to handle all http methods on the given path, defaulting to the root path.
Alias: param()
Maps the specified path parameter name to a specialized param-capturing middleware.
The middleware stack is the same as .use().
Note: this middleware is only valid for HTTP traffic.
Use a middleware for all the incoming traffic in the HTTP replay phase. This middleware stack can be useful to differ between forward/replay traffic, applying separated flows of middleware.
Note: this middleware is only valid for HTTP traffic.
Use a middleware for all the incoming traffic only for the HTTP request forward phase.
For most cases you will only use .use(), but for particular modifications only for the forwarded traffic, this middleware can be useful.
Note: this middleware is only valid for HTTP traffic.
Alias: useOutgoing
Use a middleware for the outgoing response traffic of the forwarded request.
This middleware stack is useful to handle intercept and modify server responses before sending it to the end client in the other side of the proxy.
Note: this middleware is only valid for HTTP traffic.
Use a WebSocket specific middleware. Middleware chain will be executed on every incoming WebSocket connection.
Use a custom middleware for a specific phase. Supported phase names are: forward, 'replay'.
This method is used internally, however it's also public since it could be useful
for dynamic middleware configurations instead of using the shortcut methods such as: useReplay or useForward.
Define a set of URLs to balance between with a simple round-robin like scheduler.
Disable replay logic.
Enable and define a custom retry logic as global configuration.
See Route#retry for details.
Subscribe to a proxy event. See support events here
Remove an event by its handler function. See support events here
Remove an event by its handler function. See support events here
Remove all the subscribers to the given event. See support events here
Return: Function(req, res, next)
Return a connect/express compatible middleware
Raw HTTP request/response handler.
Starts a HTTP proxy server in the given port
Close the HTTP proxy server, if exists.
Shortcut to rocky#server.close(cb)
Return: Route
Add a route handler for the given path for all HTTP methods
Return: Route
Return: Route
Configure a new route the given path with GET method
Return: Route
Configure a new route the given path with POST method
Return: Route
Configure a new route the given path with PUT method
Return: Route
Configure a new route the given path with DELETE method
Return: Route
Configure a new route the given path with PATCH method
Return: Route
Configure a new route the given path with HEAD method
Return: Route
Route all the incoming traffic to the default target.
This is a shortcut to rocky#all('/*').
Note: you must call this method only when you already defined other routes.
Parse and expose the query params in http.IncomingMessage object via req.query = Object.
Additionally you can pass an object with additional params to add or a middleware function(req, res, next) to work in details with query params.
Add/extend custom headers to the incoming request before forward/replay it.
Define a custom timeout for forward/replay traffic in milliseconds.
Internal router instance
HTTP/HTTPS server instance.
Only present if listen() was called starting the built-in server.
Exposes the MiddlewarePool instance.
