web-dev-qa-db-fra.com

Nodejs HTTP et HTTPS sur le même port

J'ai fait des recherches sur Google et je regarde ici stackoverflow, mais je ne trouve pas de réponse que j'aime ;-)

J'ai un serveur NodeJS qui s'exécute sur HTTPS et le port 3001. Maintenant, je voudrais récupérer toutes les requêtes HTTP entrantes sur le port 3001 et les rediriger vers la même URL mais sur HTTPS.

Cela doit être possible. N'est-ce pas?

Merci!

37
user2164996

Vous n'avez pas besoin d'écouter sur le même port si vous suivez la convention

Par convention lorsque vous demandez http://127.0.0.1 votre navigateur essaiera de se connecter au port 80. Si vous essayez d'ouvrir https://127.0.0.1 votre navigateur essaiera de se connecter au port 443. Donc, pour sécuriser tout le trafic, il est tout simplement classique d'écouter le port 80 sur http avec une redirection vers https où nous avons déjà un écouteur pour https pour le port 443. Voici le code:

var https = require('https');

var fs = require('fs');
var options = {
    key: fs.readFileSync('./key.pem'),
    cert: fs.readFileSync('./cert.pem')
};

https.createServer(options, function (req, res) {
    res.end('secure!');
}).listen(443);

// Redirect from http port 80 to https
var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(301, { "Location": "https://" + req.headers['Host'] + req.url });
    res.end();
}).listen(80);

Testez avec https:

$ curl https://127.0.0.1 -k
secure!

Avec http:

$ curl http://127.0.0.1 -i
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1/
Date: Sun, 01 Jun 2014 06:15:16 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Si vous devez écouter sur le même port

Il n'y a pas de moyen simple d'écouter http/https sur le même port. Le mieux est de créer un serveur proxy sur une simple socket réseau vers laquelle (http ou https) est basé sur la nature de la connexion entrante (http vs https).

Voici le code complet (basé sur https://Gist.github.com/bnoordhuis/4740141 ) qui fait exactement cela. Il écoute sur localhost: 3000 et le redirige vers http (qui à son tour le redirige vers https) ou si la connexion entrante est dans https, il la transmet simplement au gestionnaire https

var fs = require('fs');
var net = require('net');
var http = require('http');
var https = require('https');

var baseAddress = 3000;
var redirectAddress = 3001;
var httpsAddress = 3002;
var httpsOptions = {
    key: fs.readFileSync('./key.pem'),
    cert: fs.readFileSync('./cert.pem')
};

net.createServer(tcpConnection).listen(baseAddress);
http.createServer(httpConnection).listen(redirectAddress);
https.createServer(httpsOptions, httpsConnection).listen(httpsAddress);

function tcpConnection(conn) {
    conn.once('data', function (buf) {
        // A TLS handshake record starts with byte 22.
        var address = (buf[0] === 22) ? httpsAddress : redirectAddress;
        var proxy = net.createConnection(address, function () {
            proxy.write(buf);
            conn.pipe(proxy).pipe(conn);
        });
    });
}

function httpConnection(req, res) {
    var Host = req.headers['Host'];
    res.writeHead(301, { "Location": "https://" + Host + req.url });
    res.end();
}

function httpsConnection(req, res) {
    res.writeHead(200, { 'Content-Length': '5' });
    res.end('HTTPS');
}

À titre de test, si vous le connectez avec https, vous obtenez le gestionnaire https:

$ curl https://127.0.0.1:3000 -k
HTTPS

si vous le connectez avec http, vous obtenez le gestionnaire de redirection (qui vous amène simplement au gestionnaire https):

$ curl http://127.0.0.1:3000 -i
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1:3000/
Date: Sat, 31 May 2014 16:36:56 GMT
Connection: keep-alive
Transfer-Encoding: chunked
70
basarat

Je sais que c'est une vieille question mais je la mets juste comme référence pour quelqu'un d'autre. Le moyen le plus simple que j'ai trouvé était d'utiliser le module https://github.com/mscdex/httpolyglot . Semble faire ce qu'il dit de manière assez fiable

    var httpolyglot = require('httpolyglot');
    var server = httpolyglot.createServer(options,function(req,res) {
      if (!req.socket.encrypted) {
      // Redirect to https
        res.writeHead(301, { "Location": "https://" + req.headers['Host'] + req.url });
        res.end();
      } else {
        // The express app or any other compatible app 
        app.apply(app,arguments);
      }
  });
 // Some port
 server.listen(11000);
16
Prakash Rajagaopal

Si le service HTTP et HTTPS sur un seul port est une exigence absolue, vous pouvez directement proxy la demande à l'implémentation HTTP pertinente, plutôt que de diriger le socket vers un autre port.

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'));
14
Jake Holzinger