监听HTTP和HTTPS的单个express应用程序

68

我能否创建一个同时监听HTTP和HTTPS的Express服务器,使用相同的路由和中间件?

目前我使用Express在HTTP上进行此操作,通过stunnel将HTTPS隧道传输到Express,但我更喜欢纯Node解决方案。

我可以使用以下代码来实现,但是使用了标记为私有的handle方法:

var express = require( 'express' )
    , https = require("https")
    , fs = require( 'fs' );

var app = express.createServer();
// init routes and middlewares
app.listen( 80 );

var privateKey = fs.readFileSync( 'privatekey.pem' ).toString();
var certificate = fs.readFileSync( 'certificate.pem' ).toString();
var options = {key: privateKey, cert: certificate};
https.createServer( options, function(req,res)
{
    app.handle( req, res );
} ).listen( 443 );

在这里简洁地回答了:https://dev59.com/EWw05IYBdhLWcg3whCRn#23894573 - arcseldon
6个回答

60

为了使您的应用程序能够同时监听端口80443上的httphttps ,请执行以下操作:

创建一个Express应用程序:

var express = require('express');
var app = express();
express()返回的应用程序是一个JavaScript函数。它可以作为回调传递给Node的HTTP服务器来处理请求。这使得使用相同的代码库很容易提供您的应用程序的HTTP和HTTPS版本。
您可以按以下方式执行此操作:
var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
var app = express();

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

http.createServer(app).listen(80);
https.createServer(options, app).listen(443);

有关详细信息,请参阅文档


38

关于这个问题的一个可能的更新,你可能想看一下express 3中的变化这里。变更文档中提到:

express() 的返回值是一个JavaScript函数,封装了使Express应用程序运行的所有功能。这意味着您可以通过将其传递给node的http.createServer()https.createServer()轻松设置应用程序的HTTP和HTTPS版本:

在Express 3中,express.createServer()现在是express()

以下代码是适用于express 3的完整示例:

var fs = require('fs')
    , https = require('https')
    , http = require('http')
    , express = require('express')
    , keys_dir = 'keys/'
    , server_options = {
        key  : fs.readFileSync(keys_dir + 'privatekey.pem'),
        ca   : fs.readFileSync(keys_dir + 'certauthority.pem'),
        cert : fs.readFileSync(keys_dir + 'certificate.pem')
      }
    , app = express();
app.configure(function(){
  app.use(express.cookieParser());
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.session( { secret: '' } ));
  app.use(app.router);
});
app.configure('development',function(){
  app.use(express.static(__dirname + '/public'));
  app.use(express.errorHandler({dumpExceptions: true, showStack:true}));
  app.set('view options', { pretty: true });
});
app.get('/', function(req, res){
  res.send('Hello World!');
});
https.createServer(server_options,app).listen(7000);
http.createServer(app).listen(8000);

24
最好使用提问者使用的相同语言来回答问题。我已经擅自将 Coffeescript 翻译成了原生 JS。 - gcochard

28
您可以通过类似以下的方式分享实现代码:
var register = function (app) {
    // config middleware
    app.configure({

    });

    // config routes
    app.get(...);
};

var http = express.createServer();
register(http);
http.listen(80);

var https = express.createServer({ key: /* https properties */ });
register(https);
https.listen(443);

11
此解决方案已经被弃用。是否有更新的解决方案? - Catalin MUNTEANU
1
@CMN - 请参考下面cmd的解决方案。它适用于当前版本的Node(v4.4.2)和Express(4.13.4)。 - paulsm4

2
您可以在同一端口使用express和https。
这对我有效。
最初的回答。
const express=require('express');
const app=express();
const cors=require('cors');
const path=require("path");
const routes=require('./routes/api');
const routerComplain=require('./routes/api');
const routerstores=require('./routes/api');
const routerstock=require('./routes/api');
const routerreport=require('./routes/api');
const routeritem=require('./routes/api');
const bodyParser=require('body-parser');
const routerRegister=require('./routes/api');
const mongoose=require('mongoose');
var server = require('http').Server(app);
var io = require('socket.io')(server);
require("dotenv").config();

mongoose.connect('mongodb://@@@@@@@@@@@@@@@@@',{ useNewUrlParser: true },(err)=>{
    if(!err){
        console.log('db connected')
    }else{
        console.log('error in db')
    }
});

mongoose.Promise = global.Promise;
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(cors({credentials: true, origin:'http://localhost:3000'}));
app.use(express.static(path.join(__dirname, "client", "build")))

app.use('/reg',routes);
app.use('/complain',routerComplain);
app.use('/register',routerRegister);
app.use('/stores',routerstores);
app.use('/reports',routerreport);
app.use('/stock',routerstock);
app.use('/items',routeritem);

app.get("*", (req, res) => {
    res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});

io.on('connection', function (socket) {
    socket.emit('news', { hello: 'world' });
    socket.on('my other event', function (data) {
      console.log(data);
    });
  })

const port = process.env.port||4000;

server.listen(port,function(){
    console.log('now listening for request');
});

2
如果您想使用传统的两个端口之一,上述解决方案可能适用,但是使用httpolyglot,您可以非常轻松地在同一端口上使用相同的中间件进行http和https。以下是一些我使用过的骨架代码:https://github.com/mscdex/httpolyglot
var express = require('express');
var fs = require('fs');
var httpolyglot = require('httpolyglot');
var app = express();

const options = {
    key: fs.readFileSync("/etc/ssl/certs/key"),
    cert: fs.readFileSync("/etc/ssl/certs/cer.cer")
};

httpolyglot.createServer(options, app).listen(port);

如果您想要进行http到https的转发,您可以在createServer()调用之前添加此中间件函数:

如果你想实现http到https的重定向,你可以在createServer()调用之前添加以下中间件函数:

app.use(function(req, res, next) {
    if (!req.secure ) {
            res.redirect (301, 'https://' + req.hostname + ':port' + req.originalUrl);
    }
    next();
});

This can be set up on a custom port


整洁...把它们放在同一个端口上有什么好处吗? - Nick Manning

0

1
你看了另一篇帖子吗?帖子里有一个例子。你需要创建两个服务器实例,让其中一个监听80端口,另一个监听443端口,分别支持Http和Https协议。 - Declan Cook
2
是的,我已经阅读了其他帖子:有两个具有不同路由的服务器实例。我想要同样的路由(或仅一个实例监听 HTTP 和 HTTPS)。 - Jazz
你可以单独声明路由函数,然后使用它们来声明两个服务器的路由。除此之外,我不知道还有简单的方法来实现你想做的事情。你为什么需要同时通过http和https来提供相同的内容呢? - Declan Cook
由于此服务器内容将包含在HTTP和HTTPS站点中,我可以为两个服务器声明路由和中间件,但我希望能做得更好。 - Jazz
我最初尝试同时监听http和https,但后来放弃了这个想法,使用nginx来处理请求(同时支持http和https),并在我的node应用程序中仅使用http。在生产、开发和测试中都更加轻松。 - Amitava
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接