Comparing version 0.1.5 to 0.1.6
@@ -14,2 +14,4 @@ var http = require('http') | ||
.post('/users/:id') | ||
// Replay traffic for the given route | ||
.replay('http://localhost:3002', { forwardOriginalBody: true }) | ||
// Add incoming traffic middleware to intercept and mutate the request | ||
@@ -70,2 +72,14 @@ .use(function (req, res, next) { | ||
// Replay server | ||
http.createServer(function (req, res) { | ||
// Check if we have a auth | ||
if (req.headers['authorization'] !== 'Bearer 0123456789') { | ||
res.writeHead(401) | ||
return res.end() | ||
} | ||
res.writeHead(204) | ||
res.end() | ||
}).listen(3002) | ||
// Client test request | ||
@@ -72,0 +86,0 @@ supertest('http://localhost:3000') |
@@ -42,11 +42,8 @@ var http = require('http') | ||
}) | ||
.on('replay:proxyReq', function (proxyReq, req, res, opts) { | ||
console.log('Replay request:', req.url, 'to', opts.target) | ||
.on('replay:start', function (params, opts, req) { | ||
console.log('Replay request:', params, '=>', opts.target) | ||
}) | ||
.on('replay:proxyRes', function (proxyRes, req, res) { | ||
console.log('Replay response:', req.url, 'with status', res.statusCode) | ||
.on('replay:error', function (err, req) { | ||
console.log('Replay error:', err, req.url) | ||
}) | ||
.on('replay:error', function (err, req, res) { | ||
console.log('Replay error:', err) | ||
}) | ||
@@ -53,0 +50,0 @@ proxy.listen(3000) |
@@ -32,4 +32,4 @@ var _ = require('lodash') | ||
function setProxyHeaders(req, res, next) { | ||
req.headers['X-Powered-By'] = 'rocky HTTP proxy' | ||
req.headers['x-powered-by'] = 'rocky HTTP proxy' | ||
next() | ||
} |
@@ -21,3 +21,3 @@ module.exports = function transformRequestBody(middleware) { | ||
var body = req.read(length) | ||
req.body = req.originalBody = body | ||
req.body = req._originalBody = body | ||
middleware(req, res, finisher) | ||
@@ -36,2 +36,4 @@ } | ||
if (body) { | ||
var length = req.headers['content-length'] | ||
if (+length) req._originalBodyLength = length | ||
req.headers['content-length'] = body.length | ||
@@ -42,6 +44,6 @@ req.push(body, encoding) | ||
// Now close the stream with EOF | ||
// Close the stream with EOF | ||
req.push(null) | ||
req.resume() | ||
req.resume() | ||
next() | ||
@@ -48,0 +50,0 @@ } |
@@ -27,3 +27,3 @@ module.exports = function transformResponseBody(middleware) { | ||
// Expose the body | ||
res.body = res.originalBody = body | ||
res.body = res._originalBody = body | ||
@@ -30,0 +30,0 @@ // Clean references to prevent leaks |
@@ -1,2 +0,1 @@ | ||
var _ = require('lodash') | ||
var router = require('router') | ||
@@ -6,2 +5,3 @@ var Route = require('./route') | ||
var routeHandler = require('./handler') | ||
var assign = require('lodash').assign | ||
var Emitter = require('events').EventEmitter | ||
@@ -27,4 +27,7 @@ | ||
Rocky.prototype.replay = function (url) { | ||
this.replays.push.apply(this.replays, arguments) | ||
Rocky.prototype.replay = function (url, opts) { | ||
if (typeof url === 'string') { | ||
url = { target: url } | ||
} | ||
this.replays.push(assign({}, url, opts)) | ||
return this | ||
@@ -34,3 +37,3 @@ } | ||
Rocky.prototype.options = function (opts) { | ||
_.assign(this.opts, opts) | ||
assign(this.opts, opts) | ||
return this | ||
@@ -37,0 +40,0 @@ } |
102
lib/route.js
@@ -1,8 +0,9 @@ | ||
var _ = require('lodash') | ||
var http = require('http') | ||
var https = require('https') | ||
var HttpProxy = require('http-proxy') | ||
var midware = require('midware') | ||
var parseUrl = require('url').parse | ||
var replay = require('./replay') | ||
var middlewares = require('./middlewares') | ||
var debug = require('debug')('rocky:route') | ||
var assign = require('lodash').assign | ||
@@ -12,7 +13,7 @@ module.exports = Route | ||
function Route(path) { | ||
this.opts = {} | ||
this.replays = [] | ||
this.path = path | ||
this.mw = midware() | ||
this.proxy = new HttpProxy() | ||
this.opts = {} | ||
this.replays = [] | ||
this.path = path | ||
this.mw = midware() | ||
this.proxy = new HttpProxy() | ||
} | ||
@@ -34,8 +35,11 @@ | ||
Route.prototype.options = function (opts) { | ||
_.assign(this.opts, opts) | ||
assign(this.opts, opts) | ||
return this | ||
} | ||
Route.prototype.replay = function (url) { | ||
this.replays.push.apply(this.replays, arguments) | ||
Route.prototype.replay = function (url, opts) { | ||
if (typeof url === 'string') { | ||
url = { target: url } | ||
} | ||
this.replays.push(assign({}, url, opts)) | ||
return this | ||
@@ -91,58 +95,48 @@ } | ||
var route = this | ||
var opts = _.assign({}, options, this.opts) | ||
var opts = assign({}, options, this.opts) | ||
// Dispatch route middleware | ||
this.mw.run(req, res, forward) | ||
// Dispatch the route middleware | ||
this.mw.run(req, res, forwarder) | ||
function forward(err) { | ||
if (err) { return } | ||
// Balance, if required | ||
var balance = opts.balance | ||
if (balance) { | ||
opts.target = balance.shift() | ||
balance.push(opts.target) | ||
function forwarder(err) { | ||
if (err) { | ||
return route.proxy.emit('error', err, req, res) | ||
} | ||
// Forward the request to the main target | ||
if (opts.target) { | ||
if (opts.forwardHost) { | ||
req.headers.host = parseUrl(opts.target).host | ||
} | ||
// Forward the request | ||
proxyRequest(req, res, route, opts) | ||
debug('FORWARD [%s] %s => %s', req.method, req.url, opts.target) | ||
route.proxy.web(req, res, opts) | ||
} | ||
// And replay it, if necessary | ||
replayRequest(req, route, opts) | ||
} | ||
} | ||
// Replay the request, if necessary | ||
var replays = route.replays.length | ||
? route.replays | ||
: opts.replays | ||
function proxyRequest(req, res, route, opts) { | ||
// Balance the request, if required | ||
var balance = opts.balance | ||
if (balance) { | ||
opts.target = balance.shift() | ||
balance.push(opts.target) | ||
} | ||
replays.forEach(replay) | ||
if (!opts.target) { return } | ||
if (opts.forwardHost) { | ||
req.headers.host = parseUrl(opts.target).host | ||
} | ||
function replay(url) { | ||
var proxy = new HttpProxy() | ||
var res = new http.ServerResponse(req) | ||
var options = _.assign(opts, { target: url }) | ||
// Forward the request to the main target | ||
route.proxy.web(req, res, opts) | ||
} | ||
debug('REPLAY [%s] %s => %s', req.method, req.url, url) | ||
function replayRequest(req, route, opts) { | ||
var replays = route.replays.length | ||
? route.replays | ||
: opts.replays | ||
proxy.once('proxyReq', function (proxyReq, req, res, opts) { | ||
route.proxy.emit('replay:proxyReq', proxyReq, req, res, opts) | ||
replays | ||
.map(function (replay) { | ||
return assign({}, opts, replay) | ||
}) | ||
proxy.once('proxyRes', function (proxyRes, req, res) { | ||
route.proxy.emit('replay:proxyRes', proxyRes, req, res) | ||
}) | ||
proxy.once('error', function (err, req, res) { | ||
route.proxy.emit('replay:error', err, req, res) | ||
}) | ||
if (opts.forwardHost) { | ||
req.headers.host = parseUrl(url).host | ||
} | ||
proxy.web(req, res, options) | ||
} | ||
.forEach(replay(req, route)) | ||
} |
{ | ||
"name": "rocky", | ||
"version": "0.1.5", | ||
"version": "0.1.6", | ||
"description": "Pluggable and middleware-oriented full featured HTTP/S proxy router", | ||
@@ -27,3 +27,3 @@ "repository": "h2non/rocky", | ||
"engines": { | ||
"node": ">= 0.10" | ||
"node": ">= 0.12" | ||
}, | ||
@@ -34,3 +34,3 @@ "scripts": { | ||
"dependencies": { | ||
"debug": "^2.2.0", | ||
"findup-sync": "^0.2.1", | ||
"http-proxy": "^1.11.1", | ||
@@ -47,2 +47,3 @@ "lodash": "^3.9.3", | ||
"express": "^4.13.0", | ||
"fw": "^0.1.2", | ||
"http-version": "^0.1.0", | ||
@@ -49,0 +50,0 @@ "mocha": "^2.2.5", |
@@ -12,2 +12,4 @@ # 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) | ||
Requires nodejs +0.12 or iojs +1.6 | ||
## Features | ||
@@ -100,3 +102,3 @@ | ||
--help, -h Show help [boolean] | ||
--config, -c File path to TOML config file [required] | ||
--config, -c File path to TOML config file | ||
--port, -p rocky HTTP server port | ||
@@ -120,2 +122,3 @@ --forward, -f Default forward server URL | ||
Passing the config file: | ||
``` | ||
@@ -125,2 +128,12 @@ rocky --config rocky.toml --port 8080 --debug | ||
Reading config from `stdin`: | ||
``` | ||
cat rocky.toml | rocky --port 8080 --debug | ||
``` | ||
Transparent `rocky.toml` file discovery in the current and higher directories: | ||
``` | ||
rocky --port 8080 | ||
``` | ||
### Configuration | ||
@@ -152,2 +165,3 @@ | ||
- **protocolRewrite** `boolean` - rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null. | ||
- **forwardOriginalBody** `boolean` - Only valid for **replay** request. Forward the original body instead of the transformed one | ||
@@ -213,3 +227,3 @@ #### Configuration file | ||
// Plug in the rocky middleware | ||
app.use(proxy.rr()) | ||
app.use(proxy.middleware()) | ||
@@ -231,3 +245,3 @@ // Old route (won't be called since it will be intercepted by rocky) | ||
.forward('http://new.server') | ||
.replay('http://old.server') | ||
.replay('http://old.server', { forwardOriginalBody: true }) | ||
.options({ forwardHost: true }) | ||
@@ -285,6 +299,8 @@ | ||
#### rocky#replay(...url) | ||
#### rocky#replay(url, [ opts ]) | ||
Add a server URL to replay the incoming request | ||
`opts` param provide specific replay [options](#configuration), overwritting the parent options. | ||
#### rocky#options(options) | ||
@@ -396,6 +412,8 @@ | ||
#### route#replay(...url) | ||
#### route#replay(url, [ opts ]) | ||
Overwrite replay servers for the current route. | ||
`opts` param provide specific replay [options](#configuration), overwritting the parent options. | ||
#### route#balance(...urls) | ||
@@ -421,4 +439,3 @@ | ||
Experimental request body interceptor and transformer middleware for the given route. | ||
This allows you to change, replace or map the response body sent from the target server before sending it to the client. | ||
This allows you to intercept and replace or transform the response body recieved from the client before sending it to the target. | ||
@@ -450,4 +467,3 @@ The middleware must a function accepting the following arguments: `function(req, res, next)` | ||
Experimental response body interceptor and transformer middleware for the given route. | ||
This allows you to change, replace or map the response body sent from the target server before sending it to the client. | ||
This allows you to intercept and replace or transform the response body received from the target server before sending it to the client. | ||
@@ -494,4 +510,3 @@ The middleware must a function accepting the following arguments: `function(req, res, next)` | ||
- **error** `err, req, res` - Fired when the forward request fails | ||
- **replay:proxyReq** `opts, proxyReq, req, res` - Fired when a replay request starts | ||
- **replay:proxyRes** `opts, proxyRes, req, res` - Fired when a replay server respond | ||
- **replay:start** `params, opts, req` - Fired before a replay request starts | ||
- **replay:error** `opts, err, req, res` - Fired when the replay request fails | ||
@@ -498,0 +513,0 @@ |
@@ -252,4 +252,4 @@ const fs = require('fs') | ||
.forward(targetUrl) | ||
.replay(replayUrl) | ||
.replay(replayUrl) | ||
.replay({ target: replayUrl, forwardOriginalBody: true }) | ||
.replay({ target: replayUrl, forwardOriginalBody: true }) | ||
.listen(ports.proxy) | ||
@@ -265,3 +265,3 @@ | ||
replay = createReplayServer(assert) | ||
replay = createReplayServer(assertReplay) | ||
server = createTestServer(assert) | ||
@@ -272,13 +272,23 @@ | ||
.type('application/json') | ||
.send('{"hello": "world"}') | ||
.send('{"hello":"world"}') | ||
.expect(200) | ||
.expect('Content-Type', 'application/json') | ||
.expect({'hello': 'world'}) | ||
.end(done) | ||
.end(end) | ||
function end(err) { | ||
setTimeout(function () { done(err) }, 50) | ||
} | ||
function assert(req, res) { | ||
expect(req.url).to.be.equal('/payload') | ||
expect(res.statusCode).to.match(/200|204/) | ||
expect(res.statusCode).to.be.equal(200) | ||
expect(req.body).to.be.equal('{"salutation":"hello world"}') | ||
} | ||
function assertReplay(req, res) { | ||
expect(req.url).to.be.equal('/payload') | ||
expect(res.statusCode).to.be.equal(204) | ||
expect(req.body).to.be.equal('{"hello":"world"}') | ||
} | ||
}) | ||
@@ -290,3 +300,3 @@ | ||
server = createTestServer(assert) | ||
replay = createReplayServer(assert) | ||
replay = createReplayServer(assertReplay) | ||
@@ -316,6 +326,11 @@ proxy.get('/test') | ||
expect(req.url).to.be.equal('/test') | ||
expect(res.statusCode).to.match(/200|204/) | ||
expect(spy.calledTwice).to.be.true | ||
expect(res.statusCode).to.be.equal(200) | ||
expect(req.headers['x-test']).to.be.equal('rocky') | ||
} | ||
function assertReplay(req, res) { | ||
expect(req.url).to.be.equal('/test') | ||
expect(res.statusCode).to.be.equal(204) | ||
expect(req.headers['x-test']).to.be.equal('rocky') | ||
} | ||
}) | ||
@@ -322,0 +337,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
65273
35
1475
552
9
22
+ Addedfindup-sync@^0.2.1
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedfindup-sync@0.2.1(transitive)
+ Addedglob@4.3.5(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedminimatch@2.0.10(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removeddebug@^2.2.0