Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nodejs HTTP and HTTPS over same port

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 like to fetch all incoming HTTP requests on port 3001 and redirect them to the same URL but over HTTPS.

This must be possible. Isn't it?

Thanks!

like image 273
user2164996 Avatar asked Mar 17 '14 12:03

user2164996


People also ask

Can HTTP and HTTPS be on same port?

It's possible to serve both HTTP and HTTPS on the same port. A TLS handshake record starts with byte 22, so you can use that to determine which protocol the client is trying to speak.

Can a Web server use both HTTP and HTTPS?

1 Answer. Show activity on this post. http runs on port 80, and https runs on TCP port 443. They can both be open at the same time, they can even serve different websites.

Which of the following method creates HTTP server in Nodejs?

We can create a HTTP server with the help of http. createServer() method.

Which node JS module is used to create an HTTP server?

The Built-in HTTP Module Node. js has a built-in module called HTTP, which allows Node. js to transfer data over the Hyper Text Transfer Protocol (HTTP).


2 Answers

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 
like image 60
basarat Avatar answered Sep 21 '22 23:09

basarat


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'));
like image 45
Jake Holzinger Avatar answered Sep 21 '22 23:09

Jake Holzinger