npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

moruga

v0.1.6

Published

Debugging HTTP proxy

Downloads

10

Readme

Moruga

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.

Installation

Moruga requires Node.js and NPM.

To install Moruga as a binary in your PATH, run this in your console:

sudo npm install moruga -g

Run moruga without any parameters to view available options.

moruga

HTTP Example

moruga -u http://duckduckgo.com -f filters.example.js -v
  • Listen for HTTP requests on all IP addresses, using port 80
  • Import the filters.example.js module and load its filters array
  • Proxy requests to http://duckduckgo.com[PATH_AND_QUERY_STRING]
    • E.g.: http://moruga.example.com/chunky?meat=bacon ---> http://duckduckgo.com/chunky?meat=bacon
  • Print requests/responses to/from the user agent

HTTPS Example

moruga -u http://duckduckgo.com -f filters.example.js --ssl-key=server-key.pem --ssl-cert=server-cert.pem
  • Listen for HTTPS requests on all IP addresses, using port 443
  • Use default list of CAs, including well-known ones like Verisign
  • mport the filters.example.js module and load its filters array
  • Proxy requests to http://duckduckgo.com[PATH_AND_QUERY_STRING]

Built-in Filters

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:

/^short-circuit, status=(\d+)$/
/^empty-reply, wait-sec=(\d+)$/
/^truncate-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:

X-Moruga-Control: short-circuit, status=403

Filter Pipeline

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:

{
  name: 'Chunky Bacon',
  path: '/chunky-bacon',

  // Connect-compatible middleware function
  action: 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.

{
  name: 'Noop',
  path: /^\/.+/,

  action: 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:

{
  name: 'Handler for X-Moruga-Control',
  path: /^\/.*/,
  action: 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();
  }
}

Custom Filters module

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:

// This is a regular Node module, so you can do anything you like
var util = require('util');

exports.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: {}
  }
]