Подтвердить что ты не робот

Nodejs HTTP и HTTPS через один и тот же порт

Я искал поисковые запросы и смотрел здесь в stackoverflow, но я не могу найти ответ, который мне нравится; -)

У меня есть сервер NodeJS, который работает через HTTPS и порт 3001. Теперь я хотел бы получить все входящие HTTP-запросы на порт 3001 и перенаправить их на один и тот же URL-адрес, но через HTTPS.

Это должно быть возможно. Не правда ли?

Спасибо!

4b9b3361

Ответ 1

Вам не нужно слушать один и тот же порт, если вы следуете за соглашением

По соглашению при запросе http://127.0.0.1 ваш браузер попытается подключиться к порту 80. Если вы попытаетесь открыть https://127.0.0.1, ваш браузер попытается подключиться к порту 443. Таким образом, чтобы обеспечить весь трафик, просто слушать к порту 80 на http с переадресацией на https, где у нас уже есть слушатель для https для порта 443. Здесь код:

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);

Тест с https:

$ curl https://127.0.0.1 -k
secure!

С 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

Если вы должны прослушивать один и тот же порт

Существует не простой способ прослушивания HTTP/https на одном и том же порту. Лучше всего создать прокси-сервер в простом сетевом сокете, который подключается к (http или https) в зависимости от характера входящего соединения (http против https).

Вот полный код (на основе https://gist.github.com/bnoordhuis/4740141), который делает именно это. Он прослушивает localhost: 3000 и передает его на http (который, в свою очередь, перенаправляет его на https), или если входящее соединение находится в https, оно просто передает его 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');
}

В качестве теста, если вы подключите его с помощью https, вы получите обработчик https:

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

если вы подключаете его с http, вы получаете обработчик перенаправления (который просто переносит вас на 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

Ответ 2

Я знаю его старый вопрос, но просто помещаю его в качестве ссылки для кого-то другого. Самый простой способ, который я нашел, - использовать https://github.com/mscdex/httpolyglot. Кажется, делать то, что он говорит довольно надежно

    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);

Ответ 3

Если обслуживание HTTP и HTTPS по одному порту является абсолютным требованием, вы можете напрямую проксировать запрос к соответствующей реализации HTTP, а не подключать сокет к другому порту.

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);
            }
            
            // Resume the socket data stream
            socket.resume();
        });
    });

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

Ответ 4

Если это чистый Node.JS HTTP-модуль, вы можете попробовать следующее:

if (!request.connection.encrypted) { // Check if the request is not HTTPS
    response.writeHead(301, { // May be 302
        Location: 'https://' + YourHostName + ':3001' + request.url
        /* Here you can add some more headers */
    });

    response.end(); // End the response
} else {
    // Behavior for HTTPS requests
}