Zane Claes
Zane Claes

Reputation: 14954

Binding HTTP and HTTPS traffic on the SAME port in node.js?

I have a scenario where my node.js app is behind a Load Balancing HAProxy, which is forwarding both HTTP and HTTPS traffic to port 8000 on my node servers.

Unfortunately this puts me in a tricky spot: I need to use the SAME port for my http and https servers. I would also be satisfied merely redirecting http to https, if that is easier in some way.

But, right now, I am in a position where I am forced to choose HTTP --OR-- HTTPS

For the sake of including code...

https.createServer(api.credentials, api.server).listen(8000);
http.createServer(api.server).listen(8000); // ERR ADDR IN USE

Upvotes: 2

Views: 4810

Answers (2)

Jake Holzinger
Jake Holzinger

Reputation: 6063

You can't bind multiple servers to the same port, but you can open a TCP server and proxy the traffic to the relevant HTTP implementation.

httpx.js

'use strict';
let net = require('net');
let http = require('http');
let https = require('https');

exports.createServer = (opts, handler) => {

    let server = net.createServer(socket => {
        socket.once('data', buffer => {
            // Pause the socket
            socket.pause();

            // Determine if this is an HTTP(s) request
            let byte = buffer[0];

            let protocol;
            if (byte === 22) {
                protocol = 'https';
            } else if (32 < byte && byte < 127) {
                protocol = 'http';
            }

            let proxy = server[protocol];
            if (proxy) {
                // Push the buffer back onto the front of the data stream
                socket.unshift(buffer);

                // Emit the socket to the HTTP(s) server
                proxy.emit('connection', socket);
            }
            
            // As of NodeJS 10.x the socket must be 
            // resumed asynchronously or the socket
            // connection hangs, potentially crashing
            // the process. Prior to NodeJS 10.x
            // the socket may be resumed synchronously.
            process.nextTick(() => socket.resume()); 
        });
    });

    server.http = http.createServer(handler);
    server.https = https.createServer(opts, handler);
    return server;
};

example.js

'use strict';
let express = require('express');
let fs = require('fs');
let io =  require('socket.io');

let httpx = require('./httpx');

let opts = {
    key: fs.readFileSync('./server.key'),
    cert: fs.readFileSync('./server.cert')
};

let app = express();
app.use(express.static('public'));

let server = httpx.createServer(opts, app);
let ws = io(server.http);
let wss = io(server.https);
server.listen(8080, () => console.log('Server started'));

Upvotes: 4

dylanweber
dylanweber

Reputation: 608

You cannot make two tasks listen on the same port. HTTP listens on the default port 80 and HTTPS listens on the default port 443. You're going to want to manage your HAProxy program to forward accordingly.

I found this nifty page that recommended you to set up your configuration like so:

global
  stats socket ./haproxy.stats level admin

frontend ft_http
    bind :80
    mode http
    default_backend bk_http

frontend ft_https
    bind :443
    mode tcp
    default_backend bk_https

backend bk_http
    mode http
    balance roundrobin
    stick on src table bk_https
    default-server inter 1s
    server s1 192.168.1.1:80 check id 1
    server s2 192.168.1.2:80 check id 2

backend bk_https
    mode tcp
    balance roundrobin
    stick-table type ip size 200k expire 30m
    stick on src
    default-server inter 1s
    server s1 192.168.1.1:443 check id 1
    server s2 192.168.1.2:443 check id 2

Note how the configuration above sets the Node.js ports at 80 and 443. Since Node.js doesn't have admin privileges, you might want to edit your ports to work like so:

 backend bk_http
    mode http
    balance roundrobin
    stick on src table bk_https
    default-server inter 1s
    server s1 192.168.1.1:8000 check id 1
    server s2 192.168.1.2:8000 check id 2

backend bk_https
    mode tcp
    balance roundrobin
    stick-table type ip size 200k expire 30m
    stick on src
    default-server inter 1s
    server s1 192.168.1.1:8001 check id 1
    server s2 192.168.1.2:8001 check id 2

You are also going to want to change your Node.js code to:

https.createServer(api.credentials, api.server).listen(8001);
http.createServer(api.server).listen(8000);

I'm not familiar with HAProxy, so I may be off.

Upvotes: 1

Related Questions