Nodejs HTTP 和 HTTPS 在同一个端口上

Posted

技术标签:

【中文标题】Nodejs HTTP 和 HTTPS 在同一个端口上【英文标题】:Nodejs HTTP and HTTPS over same port 【发布时间】:2014-04-22 14:58:16 【问题描述】:

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

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

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

谢谢!

【问题讨论】:

Automatic HTTPS connection/redirect with node.js/express的可能重复 @ScottGress 那个问题/答案真的很老了。我相信这里的发帖人想要一个更新的回复 【参考方案1】:

如果你遵循约定,你不需要在同一个端口上监听

按照惯例,当您请求 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);

用 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

【讨论】:

HTTPS 重定向连接被拒绝。有什么想法吗? 这对于在非标准端口上进行 HTTPS 重定向非常有用,谢谢! 这将无法处理大请求 - 尝试使用几个 100 kb,只有第一个“块”通过,其余的未正确传输。【参考方案2】:

如果通过单个端口提供 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);
            
            
            // 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'));

【讨论】:

到目前为止,最好的答案。真的很有帮助。比必须为一个应用程序提供多个端口要好得多。 这确实有效。这比在上述答案之一中使用 .once('data') 更好。这也将处理更大的请求。【参考方案3】:

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

【讨论】:

【参考方案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

【讨论】:

以上是关于Nodejs HTTP 和 HTTPS 在同一个端口上的主要内容,如果未能解决你的问题,请参考以下文章

nodejs HTTP Headers超长的问题

Node 入门

nodejs通过代理(proxy)发送http请求(request)

NodeJs 中的 http、https 和 http2

nodejs实现在线聊天室

Node.js 使用jQuery取得Nodejs http服务端返回的JSON文字示例