node.jsproxynode-http-proxy

node.js node-http-proxy: How to avoid exceptions with WebSocket when target in proxy.web


Using http-proxy (aka node-http-proxy) in node.js, I am having trouble figuring out how to proxy web sockets when the target is determined dynamically (i.e. when processing the request).

Looking at the documentation: https://github.com/nodejitsu/node-http-proxy

There is an example that shows being able to set the target when processing the request:

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

//
// Create a proxy server with custom application logic
//
var proxy = httpProxy.createProxyServer({});

//
// Create your custom server and just call `proxy.web()` to proxy
// a web request to the target passed in the options
// also you can use `proxy.ws()` to proxy a websockets request
//
var server = http.createServer(function(req, res) {
  // You can define here your custom logic to handle the request
  // and then proxy the request.
  proxy.web(req, res, { target: 'http://127.0.0.1:5060' });
});

console.log("listening on port 5050")
server.listen(5050);

There is another example farther down showing support for websockets via proxy.ws(), but it shows the target being set statically rather than depending on the request:

//
// Setup our server to proxy standard HTTP requests
//
var proxy = new httpProxy.createProxyServer({
  target: {
    host: 'localhost',
    port: 9015
  }
});
var proxyServer = http.createServer(function (req, res) {
  proxy.web(req, res);
});

//
// Listen to the `upgrade` event and proxy the
// WebSocket requests as well.
//
proxyServer.on('upgrade', function (req, socket, head) {
  proxy.ws(req, socket, head);
});

proxyServer.listen(8015);

I took the first example and added the proxyServer.on('upgrade'... proxy.ws() ... stuff from the second example in order to get an example that sets the target while processing the request and also supports websockets. HTTP web pages seem to work fine, but it throws an exception when handling a websocket request.

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

//
// Create a proxy server with custom application logic
//
var proxy = httpProxy.createProxyServer({});

//
// Create your custom server and just call `proxy.web()` to proxy
// a web request to the target passed in the options
// also you can use `proxy.ws()` to proxy a websockets request
//
var server = http.createServer(function(req, res) {
  // You can define here your custom logic to handle the request
  // and then proxy the request.
  proxy.web(req, res, { target: 'http://127.0.0.1:5060' });
});

//
// Listen to the `upgrade` event and proxy the
// WebSocket requests as well.
//
server.on('upgrade', function (req, socket, head) {
  proxy.ws(req, socket, head);
});

console.log("listening on port 5050")
server.listen(5050);

The exception happens in the proxy.ws(req, socket, head) call:

Error: Must provide a proper URL as target
    at ProxyServer.<anonymous> (...../node_modules/http-proxy/lib/http-proxy/index.js:68:35)
    at Server.<anonymous> (...../poc.js:26:9)  // the location in my sample code of the proxy.ws(req, socket, head) above
    at emitThree (events.js:116:13)
    at Server.emit (events.js:194:7)
    at onParserExecuteCommon (_http_server.js:409:14)
    at HTTPParser.onParserExecute (_http_server.js:377:5)

The code in http-proxy/index.js:68:35 throws this exception if there is no .target or .forward member of the options.

How do I set the target on a per request basis and also get websockets to work?


Solution

  • I have an answer. After looking at this question by Conrad and the comments, and then experimenting: single-proxyserver-to-multiple-targets

    proxy.ws can take an additional argument of options, just like proxy.web.

    Here is the working code.

    'use strict';
    var http = require('http'),
        httpProxy = require('http-proxy');
    
    //
    // Create a proxy server with custom application logic
    //
    var proxy = httpProxy.createProxyServer({});
    
    //
    // Create your custom server and just call `proxy.web()` to proxy
    // a web request to the target passed in the options
    // also you can use `proxy.ws()` to proxy a websockets request
    //
    var server = http.createServer(function(req, res) {
      // You can define here your custom logic to handle the request
      // and then proxy the request.
      proxy.web(req, res, { target: 'http://127.0.0.1:5060' });
    });
    
    //
    // Listen to the `upgrade` event and proxy the
    // WebSocket requests as well.
    //
    server.on('upgrade', function (req, socket, head) {
      proxy.ws(req, socket, head, { target: 'ws://127.0.0.1:5060' });
    });
    
    console.log("listening on port 5050")
    server.listen(5050);