Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

rocky

Package Overview
Dependencies
Maintainers
1
Versions
57
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rocky - npm Package Compare versions

Comparing version 0.3.10 to 0.3.11

examples/replay.js

3

examples/body-buffer.js

@@ -33,2 +33,5 @@ var http = require('http')

req.body = newBody
// Continue processing the request
next()
})

@@ -35,0 +38,0 @@

@@ -21,2 +21,9 @@ var http = require('http')

})
.use(function (req, res, next) {
if (req.params.id.length < 2) {
console.log('Invalid ID param')
}
next()
})
// Add middleware to transform the response

@@ -56,2 +63,4 @@ .transformRequestBody(function transformer(req, res, next) {

proxy.all('/*')
proxy.listen(3000)

@@ -88,3 +97,3 @@

res.writeHead(204)
res.end()
res.end('Hello from replay server')
}).listen(3002)

@@ -91,0 +100,0 @@

18

examples/connect.js

@@ -6,8 +6,10 @@ var http = require('http')

var app = connect()
var migrate = rocky()
var proxy = rocky()
migrate
// Configure the proxy and routes
proxy
.forward('http://localhost:3001')
migrate.get('/users/:id')
proxy
.get('/*')
.replay('http://localhost:3002')

@@ -22,3 +24,4 @@ .replay('http://localhost:3002')

app.use(migrate.middleware())
// Plug in rocky middleware in connect
app.use(proxy.middleware())
app.listen(3000)

@@ -29,2 +32,3 @@

.use(function (req, res) {
console.log('Target server reached')
res.end('Hello World!')

@@ -37,3 +41,3 @@ })

.use(function (req, res) {
console.log('Not found')
console.log('Replay server reached')
res.statusCode = 404

@@ -45,4 +49,4 @@ res.end()

// Test request
http.get('http://localhost:3000/users/pepe', function (res) {
console.log('Status:', res.statusCode)
http.get('http://localhost:3000/', function (res) {
console.log('Response status:', res.statusCode)

@@ -49,0 +53,0 @@ res.on('data', function (chunk) {

@@ -15,8 +15,17 @@ var fs = require('fs')

// Forward to HTTPS server
proxy
.get('/image/*')
.options({ secure: false })
.host('httpbin.org')
.forward('https://httpbin.org')
// Forward to plain HTTP server
proxy
.forward('http://localhost:3001')
.all('/*')
proxy.listen(3000)
console.log('HTTPS server listening on port:', 3000)
proxy.listen(3443)
console.log('HTTPS server listening on port:', 3443)
console.log('Open: https://localhost:3443/image/jpeg')

@@ -23,0 +32,0 @@ // Target server

const Dispatcher = require('./dispatcher')
const toArray = require('./helpers').toArray

@@ -6,4 +7,4 @@ exports = module.exports = routeHandler

const middleware = exports.middleware = [
'forward',
'replay',
'forward',
'replay',
'response'

@@ -39,3 +40,3 @@ ]

route.on(event, function () {
const args = Array.apply(null, arguments)
const args = toArray(arguments)
rocky.emit.apply(rocky, [ event ].concat(args))

@@ -42,0 +43,0 @@ })

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

var server = opts.ssl
const server = opts.ssl
? https.createServer(opts.ssl, handler)

@@ -25,12 +25,19 @@ : http.createServer(handler)

if (err) {
err.status = 500
rocky.emit('server:error', err, req, res)
return errors.replyWithError(err, res)
return serverError(rocky, err, req, res)
}
err = new Error('Route not configured')
rocky.emit('route:missing', req, res)
errors.replyWithError(err, res)
notFound(rocky, req, res)
}
}
}
function notFound(rocky, req, res) {
var err = new Error('Route not configured: ' + req.url)
rocky.emit('route:missing', req, res)
errors.replyWithError(err, res)
}
function serverError(rocky, err, req, res) {
err.status = +err.status || 500
rocky.emit('server:error', err, req, res)
errors.replyWithError(err, res)
}
const rawBody = require('raw-body')
const typer = require('media-typer')
const common = require('./common')
const helpers = require('../helpers')

@@ -11,4 +11,4 @@ module.exports = function transformRequestBody(middleware, filter) {

// Apply the request filter is necessary
if (filter && !common.filterRequest(filter, req)) {
// Apply request filter, if defined
if (filter && !helpers.filterRequest(filter, req)) {
return next()

@@ -15,0 +15,0 @@ }

@@ -1,11 +0,11 @@

const common = require('./common')
const helpers = require('../helpers')
module.exports = function transformResponseBody(middleware, filter) {
return function (req, res, next) {
// Apply the request filter is necessary
if (filter && !common.filterRequest(filter, res)) {
// Apply request filter, if defined
if (filter && !helpers.filterRequest(filter, res)) {
return next()
}
// If body field is already present, just continue with it
// If body is already present, just continue with it
if (res.body) {

@@ -24,5 +24,8 @@ return middleware(req, res, next)

// Listen for client connection close
req.on('close', onClose)
// Wrap native http.OutgoingMessage methods
res.writeHead = function (code, message, headers) {
headArgs = Array.apply(null, arguments)
headArgs = helpers.toArray(arguments)
}

@@ -52,3 +55,3 @@

// Expose the body
// Expose the current body buffer
res.body = res._originalBody = body

@@ -61,11 +64,9 @@

// Restore and clean references
cleanupAndRestore()
// Restore native methods
restore()
// Trigger response middleware stack
middleware(req, res, finisher(cb))
}
// Listen for client connection close
req.on('close', onClose)
function onClose() {

@@ -76,13 +77,2 @@ closed = true

function cleanupAndRestore() {
// Restore methods
res.writeHead = _writeHead
res.write = _write
res.end = _end
// Clean references to prevent leaks
buf = body = _end = _write = headArgs = null
req.removeListener('close', onClose)
}
function finisher(cb) {

@@ -106,6 +96,9 @@ return function (err, body, encoding) {

// Expose the new body int he response object, if exists
// Expose the new body in the response object
if (body) res.body = body
// Send EOF
// Clean references to prevent leaks
cleanup()
// Finally, send EOF
res.end(cb)

@@ -115,2 +108,19 @@ }

function cleanupAndRestore() {
restore()
cleanup()
}
function restore() {
res.writeHead = _writeHead
res.write = _write
res.end = _end
_end = _write = null
}
function cleanup() {
buf = body = length = headArgs = null
req.removeListener('close', onClose)
}
next()

@@ -117,0 +127,0 @@ }

@@ -28,5 +28,3 @@ const midware = require('midware')

var middleware = this.pool[name]
if (!middleware) {
return done()
}
if (!middleware) return done()

@@ -33,0 +31,0 @@ middleware.run.apply(null, args)

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

const common = require('../common')
const helpers = require('../helpers')
const errors = require('../errors')

@@ -20,3 +20,3 @@ const retry = require('../retry')

// Clone request object to avoid side-effects
var forwardReq = common.cloneRequest(req, opts)
var forwardReq = helpers.cloneRequest(req, opts)

@@ -23,0 +23,0 @@ // Run the forward phase middleware

const http = require('http')
const https = require('https')
const parseUrl = require('url').parse
const common = require('../common')
const helpers = require('../helpers')
const retry = require('../retry')

@@ -22,8 +22,8 @@ const FakeResponse = require('../http/response')

replayStrategy(targets, opts, replayer, next)
chooseReplayStrategy(targets, opts, replayer, next)
}
function replayStrategy(replays, opts, replayer, done) {
function chooseReplayStrategy(replays, opts, replayer, done) {
if (opts.replaySequentially) {
common.eachSeries(replays, replayer, done)
helpers.eachSeries(replays, replayer, done)
} else {

@@ -55,3 +55,3 @@ replays.forEach(replayer)

var replayRes = new FakeResponse
var replayReq = common.cloneRequest(req, opts)
var replayReq = helpers.cloneRequest(req, opts)
replayReq.rocky.isReplay = true

@@ -160,3 +160,3 @@

// Write data to request body
// Write the property data in the request body
const body = opts.replayOriginalBody

@@ -163,0 +163,0 @@ ? req._originalBody

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

const eachSeries = require('./common').eachSeries
exports.passes = require('./passes')
const eachSeries = require('./helpers').eachSeries

@@ -25,1 +24,3 @@ exports.sequentially = function (args, done) {

}
exports.passes = require('./passes')

@@ -10,9 +10,17 @@ const retry = require('retry')

var _end = res.end
// Set methods to noop
res.writeHead = function () {}
res.end = function () {}
// Create the retrier
var op = retry.operation(opts)
op.attempt(function () { task(handler) })
// Do the first attempt
op.attempt(runTask)
function runTask() {
task(handler)
}
function handler(err, proxyRes) {

@@ -24,4 +32,5 @@ if (op.retry(failed(err, proxyRes))) {

// Restore native methods
res.end = _end
res.writeHead = _writeHead
res.end = _end
res = options = null

@@ -28,0 +37,0 @@ done(err)

const router = require('router')
const Route = require('./route')
const Base = require('./base')
const MwPool = require('./mwpool')
const routeHandler = require('./handler')
const createServer = require('./http/server')
const assign = require('lodash').assign
const MwPool = require('./mwpool')
const toArray = require('./helpers').toArray

@@ -64,3 +65,3 @@ module.exports = Rocky

Rocky.prototype[method] = function (path) {
const args = [ method ].concat(Array.apply(null, arguments))
const args = [ method ].concat(toArray(arguments))
return this.route.apply(this, args)

@@ -67,0 +68,0 @@ }

@@ -12,2 +12,3 @@ const midware = require('midware')

this.path = path
this.unregistered = false
}

@@ -14,0 +15,0 @@

{
"name": "rocky",
"version": "0.3.10",
"description": "Full-featured, middleware-oriented HTTP proxy with traffic replay and intercept",
"version": "0.3.11",
"description": "Full-featured, middleware-oriented HTTP proxy supporting traffic replay and intercept",
"repository": "h2non/rocky",

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

"devDependencies": {
"async": "^1.4.2",
"chai": "^3.0.0",

@@ -44,0 +43,0 @@ "connect": "^3.4.0",

@@ -5,3 +5,3 @@ # rocky [![Build Status](https://api.travis-ci.org/h2non/rocky.svg?branch=master&style=flat)](https://travis-ci.org/h2non/rocky) [![Code Climate](https://codeclimate.com/github/h2non/rocky/badges/gpa.svg)](https://codeclimate.com/github/h2non/rocky) [![NPM](https://img.shields.io/npm/v/rocky.svg)](https://www.npmjs.org/package/rocky) ![Downloads](https://img.shields.io/npm/dm/rocky.svg)

**Pluggable**, **full-featured** and **middleware-oriented** **HTTP/S proxy** with versatile **routing layer**, **traffic interceptor and replay** to multiple backends, **built-in balancer**, **hierarchical configuration**, optional traffic **retry/backoff** logic and [more](#features).
**Pluggable**, **full-featured** and **middleware-oriented** **HTTP/S proxy** with versatile **routing layer**, **traffic interceptor and replay** to multiple backends, **built-in balancer**, **hierarchical route-based configuration**, traffic **retry/backoff** logic and [more](#features).
Built for [node.js](http://nodejs.org)/[io.js](https://iojs.org). Compatible with [connect](https://github.com/senchalabs/connect)/[express](http://expressjs.com).

@@ -26,2 +26,3 @@

- [How does it work?](#how-does-it-work)
- [Projects using rocky](#projects-using-rocky)
- [Middleware layer](#middleware-layer)

@@ -68,3 +69,3 @@ - [Hierarchies](#hierarchies)

- As HTTP proxy for service migrations (e.g: APIs)
- Replaying traffic to one or multiple backends
- Replaying traffic to one or multiple backends (like using [hop-by-hop](https://en.wikipedia.org/wiki/Hop-by-hop_transport) mechanism)
- As reverse proxy to forward traffic to a specified server.

@@ -76,7 +77,11 @@ - As HTTP traffic interceptor transforming the request/response on the fly

- As security proxy layer
- As HTTP load balancer with full programmatic control
- 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
- For HTTP session manipulation and debugging
- For HTTP traffic recording and inspection
- For A/B testing
- For fuzz testing (see [toxy](https://github.com/h2non/toxy))
- As intermediate test server intercepting and generating random/fake responses

@@ -97,3 +102,3 @@ - And whatever a programmatic HTTP proxy can be useful to

`rocky` was originally created to become an useful tool to assist during a backend migration strategy, later on it was extended and improved to cover so many other [scenarios](#when-rocky-could-be-useful).
`rocky` was originally created to become an useful tool to assist during a backend migration strategy, later on it was extended and improved to cover so many other [scenarios](#when-rocky-can-be-useful).

@@ -104,9 +109,7 @@ `rocky` goal is to provide a full-featured and hackable programmatic HTTP proxy to build other services on it, like you can do with web services using express or connect.

`rocky` design is driven by keeping versatility and extensibility in mind with a small core and code base.
Extensibility is covered via the middleware layer, which is the core and more powerful feature of `rocky`.
`rocky` design is influenced by the [UNIX philosophy](http://www.catb.org/esr/writings/taoup/html/ch01s06.html), especially by the rules of composition, modularity and simplicity. As result, `rocky` tend to be a lightweight package with small core designed for extensibility.
Via the middleware you can completely rely on a consistent control flow when performing actions with the HTTP traffic flow, such as modifying, pausing or stopping it, even for both incoming/outgoing flows.
This allows you to plug in intermediate jobs with your own custom logic which will be executed among different phases of the HTTP flow live cycle inside `rocky`.
The most important feature in rocky is its nested multi phase middleware layer. Via the middleware layer you can rely in a consistent control flow when performing actions with the HTTP traffic flow, such as modifying, pausing or stopping it, even for both incoming and outgoing traffic flows.
`rocky` middleware layer is based on `connect` middleware.
This allows you to plug in intermediate jobs with your custom logic which will be executed among different phases of the HTTP flow live cycle.

@@ -149,5 +152,11 @@ ### Stability

### Projects using rocky
- [toxy](https://github.com/h2non/toxy) - Hackable HTTP proxy to simulare server failures and network conditions
Are you using rocky in your project? Open an issue or send a PR!
## Middleware layer
One of the more powerful features in `rocky` is its build-in domain specific middleware.
One of the more powerful features in `rocky` is its build-in domain specific middleware, based on `connect/express` middleware.

@@ -255,3 +264,3 @@ 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.

.use(function (req, res, next) {
if (req.param.name === 'admin') {
if (req.params.name === 'admin') {
// Overwrite the target URL only for this user

@@ -520,6 +529,6 @@ req.rocky.options.target = 'http://new.server.net'

- **changeOrigin** `boolean` - <true/false, Default: false - **changes** the origin of the host header to the target URL
- **auth** `boolean` - Basic authentication i.e. 'user:password' to compute an Authorization header.
- **hostRewrite** `boolean` - rewrites the location hostname on (301/302/307/308) redirects, Default: null.
- **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** `boolean` - rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
- **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.

@@ -526,0 +535,0 @@ - **replayOriginalBody** `boolean` - Only valid for **replay** request. Forward the original body instead of the transformed one.

const path = require('path')
const fs = require('fs')
const async = require('async')
const eachSeries = require('../lib/helpers').eachSeries
const expect = require('chai').expect

@@ -17,3 +17,3 @@ const spawn = require('child_process').spawn

var files = fs.readdirSync(examplesDir)
async.eachSeries(files, function (file, next) {
eachSeries(files, function (file, next) {
if (~ignore.indexOf(file)) return next()

@@ -20,0 +20,0 @@

@@ -12,4 +12,4 @@ const sinon = require('sinon')

const route = new Emitter
const length = handler.events.length
route.useFor = function () {}
const length = handler.events.length

@@ -16,0 +16,0 @@ handler(rocky, route)

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

const port = 8088
const port = 9098

@@ -11,0 +11,0 @@ suite('replay', function () {

@@ -427,2 +427,3 @@ const fs = require('fs')

res.setHeader('x-custom', '1.0')
res.removeHeader('content-type')
assert(req, res)

@@ -437,5 +438,6 @@ next()

.expect(200)
.expect('Content-Type', 'application/json')
.expect({ 'hello': 'world' })
.end(function () {})
.expect('{"hello":"world"}')
.end(function (err) {
if (err) done(err)
})

@@ -445,2 +447,3 @@ function assert(req, res) {

expect(res.getHeader('x-custom')).to.be.equal('1.0')
expect(res.getHeader('content-type')).to.not.exist
expect(res.statusCode).to.be.equal(200)

@@ -785,3 +788,3 @@ expect(res.body.toString()).to.be.equal('{"hello":"world"}')

expect(res.statusCode).to.be.equal(502)
expect(res.body.message).to.be.equal('Route not configured')
expect(res.body.message).to.match(/^Route not configured/i)
done()

@@ -788,0 +791,0 @@ }

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc