home nodejitsu.com


Vote on HN

About the author

Name
Charlie McConnell
avianflu
Av1anFlu
Location
San Francisco, CA

About this article

Date Released:
Thursday, Jun 9 2011
Code Examples:
  • websocketproxy.js

Also by this author

  • on our recent downtime
  • http proxy middlewares

About nodejitsu

nodejitsu is a node.js application hosting company from new york city.

we are community leaders in the node.js project.

we build awesome open-source projects that you love and use.

Follow @nodejitsu

Proxying HTTP and Websockets in Node

Here at Nodejitsu, we believe strongly in supporting the thriving open-source community around us, and one of our more popular open-source tools is node-http-proxy. It is full-featured, robust, and extremely easy to use - not to mention battle-hardened by ongoing production use at nodejitsu.com. The request that loaded this page, for example, was routed to our blog by http-proxy.

This guide is geared toward beginners and people who are unfamiliar with reverse HTTP proxying, websocket proxing, load balancing, virtual host configuration, request forwarding, and other web proxying concepts - those who already know what they're doing and just want to see the syntax should skip down to the sample code.


When should I use http-proxy?

Let's go over a very basic use case: you have one server, but you have more than one application hosted, each of which needs to handle requests for a specific domain.

The solution is to set up a proxy that listens for connections on one public port (i.e. 80), and then proxies the requests it receives to the proper application (each on its own non-public port) based on the hostname in each request.

But that sounds complicated!

Not for you it isn't! The http-proxy API is extremely simple, and you'll be proxying requests in no time at all. First, though, you'll need to install http-proxy. The recommended installation method is via npm. If you don't have npm yet:

 curl http://npmjs.org/install.sh | sh

Once npm is installed (or if you have it already) the simplest installation is as follows:

 cd myapp
 npm install http-proxy

This will install http-proxy to myapp/node_modules/http-proxy - once that's done, it's time to write some code! Let's look at the simplest kind of proxy first:

var http = require('http'),
    httpProxy = require('http-proxy');
//
// Create a basic proxy server in one line of code...
//
// This listens on port 8000 for incoming HTTP requests 
// and proxies them to port 9000
httpProxy.createServer(9000, 'localhost').listen(8000);

//
// ...and a simple http server to show us our request back.
//
http.createServer(function (req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
  res.end();
}).listen(9000);

Wait... really? Just one line of code?

Yes, really: one line of code to send all the requests that hit localhost:9000 to the waiting http server at localhost:8000, without requiring you to expose localhost:8000 directly.

This doesn't solve our problem, though: this code will proxy all the requests to the same place, rather than routing them by hostname. Don't worry: adding the extra functionality we need will be easy.

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

//
// Just set up your options...
//
var options = {
  hostnameOnly: true,
  router: {
    'domainone.com': '127.0.0.1:9000',
    'domaintwo.net': '127.0.0.1:9001',
    'domainthree.org': '127.0.0.1:9002'
  }
}

//
// ...and then pass them in when you create your proxy.
//
var proxyServer = httpProxy.createServer(options).listen(80);

That's all the code you need to route to different ports/apps based on domain. It's also possible to leave out the hostnameOnly option and route based on path as well:

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

//
//Leave out the hostnameOnly field this time, or set it to false...
//
var options = {
  router: {
    'domainone.com/appone': '127.0.0.1:9000',
    'domainone.com/apptwo': '127.0.0.1:9001',
    'domaintwo.net/differentapp': '127.0.0.1:9002'
  }
}


//
//...and then pass in your options like last time.
//
var proxyServer = httpProxy.createServer(options).listen(80);

That's it! It's really that easy. Set up your options, and you're only one line of code away from a battle-hardened, production-proven reverse HTTP proxy.

But what if I want to set up custom proxying logic?

No problem - http-proxy still has your back.

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

//
// Create a proxy server with custom application logic
//
httpProxy.createServer(function (req, res, proxy) {
  //
  // Put your custom server logic here
  //
  
  if (req.url.match(/apples/)) {
    req.url = req.url.replace(/apples/, 'pears');
  }

  proxy.proxyRequest(req, res, {
    host: 'localhost',
    port: 9000
  });
}).listen(8000);

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

That example was a little contrived, wasn't it?

Fine, smarty-pants - how's a round-robin load-balancing strategy for you?

var httpProxy = require('http-proxy');
//
// A simple round-robin load balancing strategy.
// 
// First, list the servers you want to use in your rotation.
//
var addresses = [
  {
    host: 'ws1.0.0.0',
    port: 80
  },
  {
    host: 'ws2.0.0.0',
    port: 80
  }
];

httpProxy.createServer(function (req, res, proxy) {
  //
  // On each request, get the first location from the list...
  //
  var target = addresses.shift();

  //
  // ...then proxy to the server whose 'turn' it is...
  //
  proxy.proxyRequest(req, res, target);

  //
  // ...and then the server you just used becomes the last item in the list.
  //
  addresses.push(target);
});

// Rinse; repeat; enjoy.

That was cool, but I'm still not impressed.

Well how about proxying socket.io websockets? Something like this can help socket.io to scale properly:

var sys = require('sys'),
    http = require('http'),
    colors = require('colors'),
    websocket = require('./../vendor/websocket'),
    httpProxy = require('node-http-proxy');

var utils = require('socket.io/lib/socket.io/utils'),
    io = require('socket.io');

//
// Create the target HTTP server...
//
var server = http.createServer(function (req, res) {
  res.writeHead(200);
  res.end();
});
server.listen(8080);

//
// ...now setup socket.io on the target HTTP server to handle websocket requests...
//
var socket = io.listen(server);
socket.on('connection', function (client) {
  sys.debug('Got websocket connection');

  client.on('message', function (msg) {
    sys.debug('Got message from client: ' + msg);
  });

  socket.broadcast('from server');
});

//
// ...next, create a proxy server with node-http-proxy...
//
var proxy = httpProxy.createServer(8080, 'localhost');
proxy.listen(8081);

//
// ...and setup the web socket to wait for connections from our proxy.
//
var ws = new websocket.WebSocket('ws://localhost:8081/socket.io/websocket/', 'borf');

ws.on('open', function () {
  ws.send(utils.encode('from client'));
});

ws.on('message', function (msg) {
  sys.debug('Got message: ' + utils.decode(msg));
});
 

For further information on http-proxy, please visit the node-http-proxy github page.