Nodejitsu

Save time managing and deploying your node.js app. Code faster with jitsu and npm

Node-HTTP-Proxy, Middlewares, and You

About the author

Name
Location
Worldwide
nodejitsu nodejitsu

When the Node HTTP Proxy was built, its purpose was performance. The concept of a stream in Node lends itself beautifully to working with HTTP requests - HTTP requests are, after all, TCP streams in the first place. One of Node's strengths is its facility for streaming data, and a great deal of performance can be achieved simply by piping the request and response streams to other destinations, and back again. This, therefore, was how the HTTP Proxy was designed - and it does its job well.

The problem here is that the extremely popular middleware style of web application development is often incompatible. If you use a body decoder middleware, for example, every request will need to be buffered in full before the body can be properly decoded. This increases memory usage and reduces performance, compared to simply relaying un-altered requests and responses to other destinations - it all has to be waited for, read into memory, possibly altered, then sent again, adding to both the time and resource cost of each request.


More importantly, though, if any subsequent middleware in your middleware chain tries to listen for 'data' or 'end' events, or otherwise treat the current HTTP connection as a stream, it just won't work, as a buffered request is no longer a stream - it is now just a data object.

Despite these concerns, 'middlewares' are an easy concept to grasp, easy to use, and are likely to remain popular. There are some gains to be had, as well - middlewares are a simple way to add new features to your application stack. If you find yourself wishing for a certain feature, the ability to use or write a middleware to add that feature can save everyone a lot of time, compared to the amount of time a pull request can take to be reviewed and merged. It also allows for conflicting features to exist more easily - finding out that two middlewares won't work well together is a much smaller problem than dealing with successive breaking API changes in a library.

Due to these facts, and the number of requests we've received from users who want to proxy to that style of application, we have added middleware support to Node HTTP Proxy.

Here's a basic example of how to use a middleware - specifically, the connect-gzip middleware. This would gzip-compress every file you serve. It can be a useful trick, at times, when applied to specific file types - replace the /.*/ with /png/, for example.

var http = require('http'),  
    httpProxy = require('http-proxy'),
    gzip = require('connect-gzip');

httpProxy.createServer(  
  gzip.gzip({ matchType: /.*/ }),
  9000, 'localhost'
).listen(8000);

http.createServer(function (req, res) {  
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
  res.end();
}).listen(9000);

The basic idea is that when you call httpProxy.createServer, you can now pass in any middlewares as arguments, in the order in which you'd like them to be used. If one of your middlewares doesn't set your proxy destination for you, pass in the port and host you want to proxy to at the end of the list, as shown above.

Here is another simple middleware usage example, this one using an example middleware by Nodejitsu's Mad-Scientist-in-Residence, Dominic Tarr, called proxy-by-url. Available on npm, this middleware provides a sugar syntax for defining both forward and reverse proxy destinations in the same block, and routing to them based on URL matching.

httpProxy.createServer(  
  require('proxy-by-url')({
  '/github': { port: 80, host: 'github.com' },
  '/nodejitsu': { port: 80, host: 'nodejitsu.com' },
  '/localstuff': { port: 8080, host: 'localhost' } 
  })
).listen(8000);

Now let's take a look at how to make a middleware - even if you don't plan to make one of your own, the structure is very straightforward, and understanding it will help you better understand how middlewares work. This middleware is for very simple logging - it will write the headers of each request to a logfile.

var fs = require('fs'),  
    http = require('http'),
    httpProxy = require('../lib/node-http-proxy');


module.exports = function (logging) {  
  // Code here is run when the middleware is initially loaded.
  var logFile = fs.createWriteStream('./requests.log');

  return function (request, response, next) {
    // Code here is run on each request.
    if (logging) {
      logFile.write(JSON.stringify(request.headers, true, 2));
    }
    next();
  }
}

This middleware can now be used like the others:

httpProxy.createServer(  
  require('./example-middleware')(true),
  8000, 'localhost'
).listen(9000)

In other Node HTTP Proxy news, we at Nodejitsu have also been fortunate enough, in the last several weeks, to receive a number of high-quality patches and bug fixes from isaacs. As a result, the resilience and stability of Node HTTP Proxy have been significantly enhanced.

So the next time you run into isaacs, say thanks! (With beer.)