Узлы HTTP и HTTPS через один и тот же порт
Я гуглил и смотрю здесь на stackru, но я не могу найти ответ, который мне нравится;-)
У меня есть сервер NodeJS, который работает через HTTPS и порт 3001. Теперь я хотел бы извлечь все входящие HTTP-запросы через порт 3001 и перенаправить их на тот же URL-адрес, но через HTTPS.
Это должно быть возможно. Не так ли?
Спасибо!
4 ответа
Вам не нужно слушать на том же порту, если вы следуете соглашению
По договоренности, когда вы запрашиваете 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
Если обслуживание 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'));
Я знаю, что это старый вопрос, но просто ставлю его как ссылку для кого-то другого. Самым простым способом, который я нашел, было использование модуля 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);
Если это чистый HTTP-модуль Node.JS, вы можете попробовать это:
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
}