Nodejs HTTP 和 HTTPS 通过同一端口

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22453782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 17:02:24  来源:igfitidea点击:

Nodejs HTTP and HTTPS over same port

node.jshttpexpresshttps

提问by user2164996

I've been googling and looking here at stackoverflow, but I can't find an answer I like ;-)

我一直在谷歌上搜索并查看stackoverflow,但我找不到我喜欢的答案;-)

I have a NodeJS server that runs over HTTPS and port 3001. Now I'd like to fetch all incoming HTTP requests on port 3001 and redirect them to the same URL but over HTTPS.

我有一个通过 HTTPS 和端口 3001 运行的 NodeJS 服务器。现在我想在端口 3001 上获取所有传入的 HTTP 请求,并将它们重定向到相同的 URL,但通过 HTTPS。

This must be possible. Isn't it?

这一定是可能的。不是吗?

Thanks!

谢谢!

回答by basarat

You don't need to listen on same port if you follow convention

如果您遵循惯例,则不需要在同一端口上侦听

By convention when you request http://127.0.0.1your browser will try to connect to port 80. If you try to open https://127.0.0.1your browser will try to connect to port 443. So to secure all traffic it is simply conventional to listen to port 80 on http with a redirect to httpswhere we already have a listener for https for port 443. Here's the code:

按照惯例,当您请求http://127.0.0.1浏览器时,将尝试连接到端口 80。如果您尝试打开https://127.0.0.1浏览器,将尝试连接到端口 443。因此,为了保护所有流量,常规做法是在 http 上侦听端口 80 并重定向到https中我们已经有一个用于 443 端口的 https 侦听器。代码如下:

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

Test with https:

用 https 测试:

$ curl https://127.0.0.1 -k
secure!

With http:

使用 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

If you must listen on same port

如果你必须在同一个端口上监听

There isn't simple way to have http / https listen on the same port. You best bet is to create proxy server on a simple net socket that pipes to (http or https) based on the nature of the incoming connection (http vs. https).

没有简单的方法可以让 http/https 在同一个端口上监听。您最好的选择是在一个简单的网络套接字上创建代理服务器,该网络套接字根据传入连接的性质(http 与 https)连接到(http 或 https)。

Here is the complete code (based on https://gist.github.com/bnoordhuis/4740141) that does exactly that. It listens on localhost:3000 and pipes it to http (which in turn redirects it to https) or if the incomming connection is in https it just passes it to https handler

这是完全可以做到这一点的完整代码(基于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');
}

As a test, If you connect it with https you get the https handler:

作为测试,如果您将其与 https 连接,您将获得 https 处理程序:

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

if you connect it with http you get the redirect handler (which simply takes you to the https handler):

如果您将它与 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

回答by Jake Holzinger

If serving HTTP and HTTPS over a single port is an absolute requirement you can proxy the request to the relevant HTTP implementation directly, rather than piping the socket to another port.

如果在单个端口上提供 HTTP 和 HTTPS 是绝对要求,您可以直接将请求代理到相关的 HTTP 实现,而不是通过管道将套接字传输到另一个端口。

httpx.js

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

例子.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'));

回答by Prakash Rajagaopal

I know its an old question but just putting it as a reference for someone else. The easiest way that I found was to use the https://github.com/mscdex/httpolyglotmodule. Seems to do what it says quite reliably

我知道这是一个老问题,但只是将其作为其他人的参考。我发现的最简单的方法是使用 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);

回答by micnic

If it's pure Node.JS HTTP module then you can try this:

如果它是纯 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
}