Name: node-moruga
Owner: racker
Description: HTTP proxy for API unit-testing and debugging
Created: 2012-08-23 21:08:40.0
Updated: 2013-11-07 18:20:38.0
Pushed: 2012-09-21 18:44:42.0
Homepage: null
Size: 132
Language: JavaScript
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
Moruga is a spider genus, a district in Trinidad, the hottest pepper in the world, and a transparent HTTP proxy for API unit-testing and debugging.
A few things remain to be done, but Moruga is far enough along to be useful.
Moruga requires Node.js and NPM.
To install Moruga as a binary in your PATH, run this in your console:
npm install moruga -g
Run moruga without any parameters to view available options.
ga
ga -u http://duckduckgo.com -f filters.example.js -v
ga -u http://duckduckgo.com -f filters.example.js --ssl-key=server-key.pem --ssl-cert=server-cert.pem
Moruga comes with two built-in filters. The first is a request/response logger, which is enabled with the -v option on the command line. Currently, the build-in logger only outputs headers, but adding an option to write out message bodies.
The second built-in filter is a handler for the custom X-Moruga-Control header. Using this header, you can trigger specific actions for each request. This is useful for writing unit tests.
The built-in X-Moruga-Control handler recognizes the following directives:
ort-circuit, status=(\d+)$/
pty-reply, wait-sec=(\d+)$/
uncate-body, location=(one-off|beginning|middle)$/
For example, to test response handling in your code for a particular HTTP status code, include this header line in the client's request:
ruga-Control: short-circuit, status=403
To cause Moruga to drop the connection after 2 seconds without returning anything:
ruga-Control: empty-reply, wait-sec=2
Or, to return only about half the body:
ruga-Control: truncate-body, location=middle
Alternatively, use `location=one-off
to return all but the last byte of the response body, or ``
location=beginning``` to return an empty body.
Moruga uses the popular Connect library to create a filter pipeline for proxied HTTP requests. Each filter contains a human-readable name, URL path to match on, and an action. A custom actions may terminate the filter pipeline and return its own response, or allow processing to continue down the pipe.
For example, if I want to short-circuit every request to '/chunky-bacon' in order to express my approval of a certain type of breakfast meat, the following filter will do the trick:
me: 'Chunky Bacon',
th: '/chunky-bacon',
Connect-compatible middleware function
tion: function(req, res, next) {
res.writeHead(200, {'X-Short-Circuit': true});
res.end('Soooooo chunky.');
// Uncomment if you want to allow remaining filters
// to run, but usually you won't do this after
// calling res.end()
// next();
The path may be a string or a RegEx-compatible object. In the latter case, the only requirment is that the object expose a test function that returns a truthy value for a successful match.
Here is another filter that matches all URLs except the root path, logs a message, and passes control to the next filter in the pipeline, if any.
me: 'Noop',
th: /^\/.+/,
tion: function(req, res, next) {
console.log('noop');
// Pass control to the next filter in the pipeline, if any
next();
And, finally, a more complex example showing how you can trigger different behaviors from a unit-test using a custom header:
me: 'Handler for X-Moruga-Control',
th: /^\/.*/,
tion: function(req, res, next) {
var control = req.headers['x-moruga-control'];
if (!control) {
next();
return;
}
var match = /^short-circuit, status=(\d+)/.exec(control);
if (match) {
var code = parseInt(match[1]);
res.writeHead(code, {'X-Short-Circuit': true});
res.end();
return;
}
next();
Moruga can load custom filters from a filter module file. The module simply needs to export an array named filters, containing a list of filter objects.
Note: Filters are installed in the pipeline in the same order as they appear in the array.
An example filters module:
his is a regular Node module, so you can do anything you like
util = require('util');
rts.filters = [
name: 'Chunky Bacon',
path: '/chunky-bacon',
// Connect middleware
action: function(req, res, next) {
res.writeHead(200, {'X-Short-Circuit': true});
res.end('Soooooo chunky.');
}
name: 'Breakfast',
path: new RegExp('/(bacon|eggs|ham|sausage|pancakes|toast|juice|milk|coffee|spam|/)+$', 'i'),
// Connect middleware
action: function(req, res, next) {
res.writeHead(200, {'X-Short-Circuit': true});
res.end("Let's eat!");
}
name: '503 on initial auth and randomly thereafter',
path: /^\/v\d+.\d+\/agent\/auth$/i,
action: function(req, res, next) {
var userAgent = req.headers['user-agent'];
// Return 503 10% of the time
var trigger = Math.random() > 0.90;
if (trigger || !this._authedByAgent[userAgent]) {
this._authedByAgent[userAgent] = true;
res.writeHead(503, {'X-Short-Circuit': true});
res.end();
}
else {
next();
}
},
_authedByAgent: {}