Nodejs HTTP and HTTPS over same port

前端 未结 4 1304
逝去的感伤
逝去的感伤 2020-11-27 03:58

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

I have a NodeJS server that runs over HTTPS and port 3001. Now I\'d lik

相关标签:
4条回答
  • 2020-11-27 03:59

    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/httpolyglot module. Seems to do what it says quite reliably

        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);
    
    0 讨论(0)
  • 2020-11-27 04:03

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

    By convention when you request http://127.0.0.1 your browser will try to connect to port 80. If you try to open https://127.0.0.1 your 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 https where we already have a listener for https for port 443. Here's the 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);
    

    Test with https:

    $ curl https://127.0.0.1 -k
    secure!
    

    With 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).

    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

    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:

    $ 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):

    $ 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
    
    0 讨论(0)
  • 2020-11-27 04:09

    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.

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

    0 讨论(0)
  • 2020-11-27 04:09

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

    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
    }
    
    0 讨论(0)
提交回复
热议问题