25 Aug 2013

HTTP Proxy - node.js

An HTTP proxy with custom routing logic that conditionally forwards to different ports based upon the requested host and path name.

For our HTTP proxy we use the http-proxy module, which provides a robust proxy implementation.

Install http-proxy and url:

$ sudo npm install http-proxy url

Then define the proxy as a node.js instance (we'll name the file proxy-server.js):

var httpProxy = require("http-proxy");
var url = require("url");

httpProxy.createServer(function(req, res, proxy) {

  var hostname = req.headers.host.split(":")[0];
  var pathname = url.parse(req.url).pathname;

  // Options for the outgoing proxy request.
  var options = { host: hostname };

  // Routing logic
  if(hostname == "127.0.0.1") {
    options.port = 8083;
  } else if(pathname == "/upload") {
    options.port = 8082;
    options.path = "/"; 
  } else {
    options.port = 8081;
  }
  // (add more conditional blocks here)

  proxy.proxyRequest(req, res, options);

}).listen(8080);

console.log("Proxy listening on port 8080");

All incoming requests on port 8080 will be received by the proxy and considered by the routing logic. The url module is used to parse and extract the pathname, which is tested along with the hostname to determine the forwarding target.

Requests with 127.0.0.1 as the host name will be forwarded to localhost:8083. Requests that do not have 127.0.0.1 as the host but have the path /upload are forwarded to localhost:8082. Any other request will be forwarded the port localhost:8083.

Extending the example to match against other host and path combinations should be trivial. If required the port, protocol (http or https) and other attributes such as remote IP address can be used in determining the target.

Before testing the proxy define 3 additional node instances, listening on ports 8081, 8082 and 8083. You can add this to the bottom of proxy-server.js:

// We simulate the 3 target applications

var http = require("http");



http.createServer(function(req, res) {

  res.end("Request received on 8081");

}).listen(8081);



http.createServer(function(req, res) {

  res.end("Request received on 8082");

}).listen(8082);



http.createServer(function(req, res) {

  res.end("Request received on 8083");

}).listen(8083);

Start the proxy by running in the terminal:

$ node proxy-server.js
Proxy listening on port 8080

Now assert the routing works as expected using your browser.

This should forward to the server listening on port 8081:

http://localhost:8080/anything
Request received on 8081

The /upload path will forward to 8082:

http://localhost:8080/upload
Request received on 8082

Entering 127.0.0.1 as the host name forwards to 8083:

http://127.0.0.1:8080/anything
Request received on 8083

localhost and 127.0.0.1 are used for example only. In a production environment you would more likely be dealing with nice server names. You could consider adding entries to your local /etc/hosts for testing purposes, for example:

127.0.0.1    server1.com
127.0.0.1    images.server2.com
127.0.0.1    upload.server2.com