Question

I want to execute a simple node-http-proxy example on phusion passenger with nginx. You can find this example code on https://github.com/nodejitsu/node-http-proxy.

var http = require('http'),
httpProxy = require('http-proxy');
//
// Create your proxy server
//
httpProxy.createServer(9000, 'localhost').listen(8000);

//
// Create your target server
//
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);

If I execute this code, I get the following error.

App 28096 stderr: /usr/share/passenger/helper-scripts/node-loader.js:157
App 28096 stderr:       throw new Error("http.Server.listen() was called more than once, which " +
App 28096 stderr:             ^
App 28096 stderr: Error: http.Server.listen() was called more than once, which is not allowed because Phusion Passenger is in auto-install mode. This means that the first http.Server object for which listen() is called, is automatically installed as the Phusion Passenger request handler. If you want to create and listen on multiple http.Server object then you should disable auto-install mode.

Than I find a post on google groups https://groups.google.com/forum/#!topic/phusion-passenger/sZ4SjU8ypwc and add

PhusionPassenger.configure({ autoInstall: false });

before I call the first time "createServer(...).listen(port)".

The next step was to replace the value of the port parameter of the listen-method with the value 'passenger'. But I have no success. Has anybody managed to get this to run?

--- My solution ---

Thank you Hongli for the advice "Next, you need to modify one (and only one) invocation ...". This solved my problem.

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

if (typeof(PhusionPassenger) != 'undefined') {
  PhusionPassenger.configure({ autoInstall: false });
}

httpProxy.createServer(9000, 'localhost').listen('passenger');

var target_server = 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);
Was it helpful?

Solution

Update July 28 2015: this answer has now been incorporated into the official Passenger documentation. Please refer to https://www.phusionpassenger.com/library/indepth/nodejs/reverse_port_binding.html for the latest answer.


Phusion Passenger author here. First, let me explain what why this error occurs.

As explained in the Node.js tutorial, Phusion Passenger inverses port binding by hooking into http.Server.listen(). If there are multiple listen() then Passenger doesn't know which http.Server object to use for receiving requests, so it throws this error.

To solve this problem, you need to tell Passenger "I want to specify explicitly which http.Server to use for receiving requests". This is done in two steps. First, you must call the following code as early as possible in your program:

if (typeof(PhusionPassenger) != 'undefined') {
    PhusionPassenger.configure({ autoInstall: false });
}

Next, you need to modify one (and only one) invocation of http.Server.listen(), and make it pass 'passenger' s argument:

server.listen('passenger');

If Passenger notices that you have multiple listen('passenger') calls, it will complain. You can only have one.

Since Phusion Passenger 4.0.52 you can also pass '/passenger' as argument. This is intended for Hapi.js support (see below).

Express.js example

Here is a full example utilizing the Express framework. You must pass 'passenger' to Express's listen() call.

if (typeof(PhusionPassenger) != 'undefined') {
    PhusionPassenger.configure({ autoInstall: false });
}

var express = require('express');
var app = express();
app.get('/', function(req, res){
    var body = 'Hello World';
    res.setHeader('Content-Type', 'text/plain');
    res.setHeader('Content-Length', body.length);
    res.end(body);
});

if (typeof(PhusionPassenger) != 'undefined') {
    app.listen('passenger');
} else {
    app.listen(3000);
}

Hapi.js example

When using Hapi.js, you must pass '/passenger' (not 'passenger') to Hapi's server constructor. This will cause Hapi try to listen on a Unix domain socket, which allows Phusion Passenger's overrides to kick in. If you do not pass that initial slash, then Hapi will try to treat the argument as a TCP/IP domain name, and Hapi will fail with an error.

if (typeof(PhusionPassenger) != 'undefined') {
    PhusionPassenger.configure({ autoInstall: false });
}

var Hapi = require('hapi');
var server;

if (typeof(PhusionPassenger) != 'undefined') {
    // Requires Phusion Passenger >= 4.0.52!
    server = new Hapi.Server('/passenger');
} else {
    server = new Hapi.Server('localhost', 3000);
}

server.route({
    method: 'GET',
    path: '/hello',
    handler: function (request, reply) {
        reply('hello world');
    }
});

server.start();

OTHER TIPS

Hapi.js v8.x.x

For hapi versions > v6.x.x use this form:

// http://stackoverflow.com/questions/20645231/phusion-passenger-error-http-server-listen-was-called-more-than-once/20645549
if ('undefined' !== typeof(PhusionPassenger)) {
  PhusionPassenger.configure({autoInstall: false});
}

var Hapi   = require('hapi'),
    server = new Hapi.Server(settings.server);

if ('undefined' !== typeof(PhusionPassenger)) {// Requires Phusion Passenger >= 4.0.52!
  server.connection({port: '/passenger'});
} else {
  server.connection({port: 3000, host: 'localhost'});
}

server.route({
    method: 'GET',
    path: '/hello',
    handler: function (request, reply) {
        reply('hello world');
    }
});

// Start the server
server.start(function serverStart() {
  server.log(['server'], 'Server@' + settings.hostname + ' (' + server.info.host + ')' + ' started at port ' + server.info.port);
});

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top