Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
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.
http
module)gzip
responses, especially when intercepting payloadsrocky
can be useful?npm install rocky --save
See benchmark/README.md for detailed benchmark results.
25.06.2015
. Beta07.07.2015
. Major features and stability improvements.24.07.2015
. Production-focused version.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)
\----------/ \----------/ \----------/
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:
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:
global
.use([path], function (req, res, next))
global
, route
.useForward(function (req, res, next))
global
, route
.useReplay(function (req, res, next))
global
, route
forward
traffic..useResponse(function (req, res, next))
global
.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:
object
object
- Exposes the configuration options for the current request.Rocky
- Exposes the rocky instance. Use only for hacking purposes!Route
- Exposes the current running route. Only available in route
type middlewareboolean
- Optional field internally checked by rocky
to stop the request replay process.object
object
- Exposes the configuration options for the current request.Rocky
- Exposes the rocky instance. Use only for hacking purposes!Route
- Exposes the current running route. Only available in route
type middlewareExample 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()
})
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.1
Passing 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:
string
- Default forward URLboolean
- Enable debug mode. Default false
string
- <url string to be parsed with the url modulearray<string|object>
- Optional replay server URLs. You can use the replay()
method to configure itboolean
- Enable WebSocket proxy mode.array<url>
- Define the URLs to balance. Via API you should use the balance()
methodnumber
- Timeout for request socketnumber
- Timeout for proxy request socketobject
- Enable retry/backoff logic for forward/replay traffic. See allowed params. Default: null
object
- Enable retry logic for replay traffic with custom options. Default: null
object
- object to be passed to http(s).request. See node.js https
docsobject
- object to be passed to https.createServer()
string
- Path to SSL certificate filestring
- Path to SSL key fileboolean
- true/false, if you want to proxy websocketsboolean
- true/false, adds x-forward headersboolean
- true/false, verify SSL certificateboolean
- true/false, explicitly specify if we are proxying to another proxyboolean
- true/false, Default: true - specify whether you want to prepend the target's path to the proxy pathboolean
- true/false, Default: false - specify whether you want to ignore the proxy path of the incoming requestboolean
- <Local interface string to bind for outgoing connectionsboolean
- <true/false, Default: false - changes the origin of the host header to the target URLstring
- Basic authentication i.e. 'user:password' to compute an Authorization header.string
- rewrites the location hostname on (301/302/307/308) redirects, Default: null.boolean
- rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false.string
- rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.boolean
- Only valid for forward request. Forward the original body instead of the transformed one.boolean
- Only valid for replay request. Forward the original body instead of the transformed one.object
- Specific router params
boolean
- When false
trailing slashes are optional (default: false
)boolean
- When true
the routing will be case sensitive. (default: false
)boolean
- When true
any req.params
passed to the router will be
merged into the router's req.params
. (default: false
)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.
Aliases: target
, forwardTo
Overwrite forward server for the current route.
Alias: replyTo
Overwrite replay servers for the current route.
opts
param provide specific replay options, overwritting the parent options.
Note: replay feature is only valid for HTTP traffic.
Define a set of URLs to balance between with a simple round-robin like scheduler.
urls
param must be an array of strings.
Disable replay logic for the current route.
Shortcut method to intercept and reply the incoming request.
If used, body
param must be a string
or buffer
Unregister the current route. If the route if matched by the router, it will be ignored, continuing to the next route in the stack.
Define a custom timeout for forward/replay traffic in milliseconds.
Overwrite the request path, defining additional optional params.
Define or overwrite request headers for the current route.
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.
Overwrite the Host
header value when forward the request.
Redirect the incoming request for the current route.
Alias: sequential
Dispatch the replay phase after the forward request ends (either with success or fail status).
Note: this will buffer all the body data. Avoid using it with large payloads
Enable sequential replay process executed in FIFO order: if some replay request fails, the queue is empty and the process will stop
Note: this will buffer all the body data. Avoid using it with large payloads
Enable retry logic for forward traffic. See allowed options here.
You can also define additional retry validations passing an array of function via strategies
field in opts
object argument.
Note: enabling retry logic will forces buffering all the body payload. Be careful when using it with large payloads
var customRetryStrategies = [
function invalidCodes(err, res) {
return !err && [404, 406].indexOf(res.statusCode) !== -1
}
]
rocky()
.get('/download/:id')
.retry({
retries: 3,
factor: 2,
minTimeout: 100,
maxTimeout: 30 * 1000,
randomize: true,
strategies: customRetryStrategies
})
rocky.forward('http://inconsistent-server')
Alias: interceptBody
Intercept and cache in a buffer the request payload data.
Body will be exposed in req.body
.
Note: use it only for small payloads, since the whole body will be buffered in memory
Alias: transformRequestBody()
This method implements a non-instrusive native http.IncomingMessage
stream wrapper that allow you to intercept and transform the request body received from the client before sending it to the target server.
The middleware
argument must a function which accepts the following arguments: function(req, res, next)
The filter
arguments is optional and it can be a string
, regexp
or function(req)
which should return boolean
if the request
passes the filter. The default check value by string
or regexp
test is the Content-Type
header.
In the middleware function must call the next
function, which accepts the following arguments: err, newBody, encoding
You can see an usage example here.
Caution: using this middleware could generate in some scenarios negative performance side-effects, since the whole payload data will be buffered in the heap until it's finished. Don't use it if you need to handle large payloads.
The body will be exposed as raw Buffer
or String
on both properties body
and originalBody
in http.ClientRequest
:
rocky
.post('/users')
.transformRequest(function (req, res, next) {
// Get the body buffer and parse it (assuming it's a JSON)
var body = JSON.parse(req.body.toString())
// Compose the new body
var newBody = JSON.stringify({ salutation: 'hello ' + body.hello })
// Set the new body
next(null, newBody, 'utf8')
}, function (req) {
// Custom filter
return /application\/json/i.test(req.headers['content-type'])
})
Alias: transformResponseBody()
This method implements a non-instrusive native http.RequestResponse
stream wrapper that allow you to intercept and transform the response body received from the target server before sending it to the client.
The middleware
argument must a function which accepts the following arguments: function(req, res, next)
The filter
arguments is optional and it can be a string
, regexp
or function(res)
which should return boolean
if the request
passes the filter. The default check value by string
or regexp
test is the Content-Type
header.
In the middleware function must call the next
function, which accepts the following arguments: err, newBody, encoding
You can see an usage example here.
Caution: using this middleware could generate in some scenarios negative performance side-effects since the whole payload data will be buffered in the heap until it's finished. Don't use it if you need to handle large payloads.
The body will be exposed as raw Buffer
or String
on both properties body
and originalBody
in http.ClientResponse
:
rocky
.post('/users')
.transformResponse(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 })
// Set the new body
next(null, newBody, 'utf8')
}, function (res) {
// Custom filter
return /application\/json/i.test(res.getHeader('content-type'))
})
Overwrite default proxy options for the current route. You can pass any supported option by http-proxy
Alias: useIncoming
Use a middleware for the incoming traffic for the current route for both replay/forward phases.
Use a middleware for current route incoming traffic in the HTTP replay phase. This middleware stack can be useful to differ between forward/replay traffic, applying separated flows of middleware.
Use a middleware for current route 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.
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
.
Subscribes to a specific event for the given route. Useful to intercept the status or modify the options on-the-fly
opts, proxyReq, req, res
- Fired when the request forward startsopts, proxyRes, req, res
- Fired when the target server respondreq, res
- Fired when the proxy receives the response from the servererr, req, res
- Fired when the proxy request failserr, req, res
- Fired before perform a retry request attemptparams, opts, req
- Fired before a replay request startsopts, err, req, res
- Fired when a replay request failsparams, opts, req
- Fired when a replay request endsparams, opts, req
- Fired when a replay request process is stoppederr, req, res
- Fired before perform a retry request attempt for replay trafficerr, req, res
- Fired on server middleware error. Only available if running as standalone HTTP serverreq, res
- Fired on missing route. Only available if running as standalone HTTP serverFor more information about events, see the events fired by http-proxy
Subscribes to a specific event for the given route, and unsubscribes after dispatched
Remove an event by its handler function in the current route
Exposes the MiddlewarePool instance used for the route scope.
Expose the built-in internal middleware functions.
You can reuse them as standard middleware in different ways, like this:
rocky()
.all('/*')
.use(rocky.middleware.headers({
'Authorization': 'Bearer 0123456789'
}))
.useReplay(rocky.middleware.host('replay.server.net'))
Intercept and optionally transform/replace the request body before forward it to the target server.
See rocky#transformRequestBody for more details.
Intercept and optionally transform/replace the response body from the server before send it to the client.
See rocky#transformResponseBody for more details.
Overrites the request URL path of the incoming request before forward/replay it.
Add/extend custom headers to the incoming request before forward/replay it.
Add/extend custom query string params to the incoming request.
Overwrite the Host
header before forwarding/replaying the request. Useful for some scenarios (e.g Heroku).
Shortcut method to reply the intercepted request from the middleware, with optional headers
and body
data.
Shortcut method to redirect the current request.
Accessor for the Route module
Accessor for the Base module
Expose protocol-specific modules.
Accessor for the http-proxy API
Middleware pool abstraction layer used internally by rocky
.
See the midware-pool package for details.
Current rocky package semver
MIT - Tomas Aparicio
FAQs
Full-featured, middleware-oriented, hackable HTTP and WebSocket proxy
We found that rocky demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.