HTTPS Proxy Server in node.js

后端 未结 4 1888
名媛妹妹
名媛妹妹 2020-12-07 12:38

I am developing a node.js proxy server application and I want it to support HTTP and HTTPS(SSL) protocols (as server).

I\'m cu

相关标签:
4条回答
  • 2020-12-07 13:03

    Solutions barely exist for this, and the documentation is poor at best for supporting both on one server. The trick here is to understand that client proxy configurations may send https requests to an http proxy server. This is true for Firefox if you specify an HTTP proxy and then check "same for all protocols".

    You can handle https connections sent to an HTTP server by listening for the "connect" event. Note that you won't have access to the response object on the connect event, only the socket and bodyhead. Data sent over this socket will remain encrypted to you as the proxy server.

    In this solution, you don't have to make your own certificates, and you won't have certificate conflicts as a result. The traffic is simply proxied, not intercepted and rewritten with different certificates.

    //  Install npm dependencies first
    //  npm init
    //  npm install --save url@0.10.3
    //  npm install --save http-proxy@1.11.1
    
    var httpProxy = require("http-proxy");
    var http = require("http");
    var url = require("url");
    var net = require('net');
    
    var server = http.createServer(function (req, res) {
      var urlObj = url.parse(req.url);
      var target = urlObj.protocol + "//" + urlObj.host;
    
      console.log("Proxy HTTP request for:", target);
    
      var proxy = httpProxy.createProxyServer({});
      proxy.on("error", function (err, req, res) {
        console.log("proxy error", err);
        res.end();
      });
    
      proxy.web(req, res, {target: target});
    }).listen(8080);  //this is the port your clients will connect to
    
    var regex_hostport = /^([^:]+)(:([0-9]+))?$/;
    
    var getHostPortFromString = function (hostString, defaultPort) {
      var host = hostString;
      var port = defaultPort;
    
      var result = regex_hostport.exec(hostString);
      if (result != null) {
        host = result[1];
        if (result[2] != null) {
          port = result[3];
        }
      }
    
      return ( [host, port] );
    };
    
    server.addListener('connect', function (req, socket, bodyhead) {
      var hostPort = getHostPortFromString(req.url, 443);
      var hostDomain = hostPort[0];
      var port = parseInt(hostPort[1]);
      console.log("Proxying HTTPS request for:", hostDomain, port);
    
      var proxySocket = new net.Socket();
      proxySocket.connect(port, hostDomain, function () {
          proxySocket.write(bodyhead);
          socket.write("HTTP/" + req.httpVersion + " 200 Connection established\r\n\r\n");
        }
      );
    
      proxySocket.on('data', function (chunk) {
        socket.write(chunk);
      });
    
      proxySocket.on('end', function () {
        socket.end();
      });
    
      proxySocket.on('error', function () {
        socket.write("HTTP/" + req.httpVersion + " 500 Connection error\r\n\r\n");
        socket.end();
      });
    
      socket.on('data', function (chunk) {
        proxySocket.write(chunk);
      });
    
      socket.on('end', function () {
        proxySocket.end();
      });
    
      socket.on('error', function () {
        proxySocket.end();
      });
    
    });
    
    0 讨论(0)
  • 2020-12-07 13:07

    I have created a http/https proxy with the aid of the http-proxy module: https://gist.github.com/ncthis/6863947

    Code as of now:

    var fs = require('fs'),
      http = require('http'),
      https = require('https'),
      httpProxy = require('http-proxy');
    
    var isHttps = true; // do you want a https proxy?
    
    var options = {
      https: {
        key: fs.readFileSync('key.pem'),
        cert: fs.readFileSync('key-cert.pem')
      }
    };
    
    // this is the target server
    var proxy = new httpProxy.HttpProxy({
      target: {
        host: '127.0.0.1',
        port: 8080
      }
    });
    
    if (isHttps)
      https.createServer(options.https, function(req, res) {
        console.log('Proxying https request at %s', new Date());
        proxy.proxyRequest(req, res);
      }).listen(443, function(err) {
        if (err)
          console.log('Error serving https proxy request: %s', req);
    
        console.log('Created https proxy. Forwarding requests from %s to %s:%s', '443', proxy.target.host, proxy.target.port);
      });
    else
      http.createServer(options.https, function(req, res) {
        console.log('Proxying http request at %s', new Date());
        console.log(req);
        proxy.proxyRequest(req, res);
      }).listen(80, function(err) {
        if (err)
          console.log('Error serving http proxy request: %s', req);
    
        console.log('Created http proxy. Forwarding requests from %s to %s:%s', '80', proxy.target.host, proxy.target.port);
      });
    
    0 讨论(0)
  • 2020-12-07 13:07

    The node-http-proxy docs contain examples of this. Look for "Proxying to HTTPS from HTTPS" at https://github.com/nodejitsu/node-http-proxy The configuration process is slightly different in every browser. Some have the option to use your proxy settings for all protocols; some you need to configure the SSL proxy separately.

    0 讨论(0)
  • Here is my NO-dependencies solution (pure NodeJS system libraries):

    const http = require('http')
    const port = process.env.PORT || 9191
    const net = require('net')
    const url = require('url')
    
    const requestHandler = (req, res) => { // discard all request to proxy server except HTTP/1.1 CONNECT method
      res.writeHead(405, {'Content-Type': 'text/plain'})
      res.end('Method not allowed')
    }
    
    const server = http.createServer(requestHandler)
    
    const listener = server.listen(port, (err) => {
      if (err) {
        return console.error(err)
      }
      const info = listener.address()
      console.log(`Server is listening on address ${info.address} port ${info.port}`)
    })
    
    server.on('connect', (req, clientSocket, head) => { // listen only for HTTP/1.1 CONNECT method
      console.log(clientSocket.remoteAddress, clientSocket.remotePort, req.method, req.url)
      if (!req.headers['proxy-authorization']) { // here you can add check for any username/password, I just check that this header must exist!
        clientSocket.write([
          'HTTP/1.1 407 Proxy Authentication Required',
          'Proxy-Authenticate: Basic realm="proxy"',
          'Proxy-Connection: close',
        ].join('\r\n'))
        clientSocket.end('\r\n\r\n')  // empty body
        return
      }
      const {port, hostname} = url.parse(`//${req.url}`, false, true) // extract destination host and port from CONNECT request
      if (hostname && port) {
        const serverErrorHandler = (err) => {
          console.error(err.message)
          if (clientSocket) {
            clientSocket.end(`HTTP/1.1 500 ${err.message}\r\n`)
          }
        }
        const serverEndHandler = () => {
          if (clientSocket) {
            clientSocket.end(`HTTP/1.1 500 External Server End\r\n`)
          }
        }
        const serverSocket = net.connect(port, hostname) // connect to destination host and port
        const clientErrorHandler = (err) => {
          console.error(err.message)
          if (serverSocket) {
            serverSocket.end()
          }
        }
        const clientEndHandler = () => {
          if (serverSocket) {
            serverSocket.end()
          }
        }
        clientSocket.on('error', clientErrorHandler)
        clientSocket.on('end', clientEndHandler)
        serverSocket.on('error', serverErrorHandler)
        serverSocket.on('end', serverEndHandler)
        serverSocket.on('connect', () => {
          clientSocket.write([
            'HTTP/1.1 200 Connection Established',
            'Proxy-agent: Node-VPN',
          ].join('\r\n'))
          clientSocket.write('\r\n\r\n') // empty body
          // "blindly" (for performance) pipe client socket and destination socket between each other
          serverSocket.pipe(clientSocket, {end: false})
          clientSocket.pipe(serverSocket, {end: false})
        })
      } else {
        clientSocket.end('HTTP/1.1 400 Bad Request\r\n')
        clientSocket.destroy()
      }
    })
    

    I tested this code with Firefox Proxy Settings (it even asks for username and password!). I entered IP address of machine where this code is runned and 9191 port as you can see in the code. I also set "Use this proxy server for all protocols". I run this code locally and on VPS - in both cases works!

    You can test your NodeJS proxy with curl:

    curl -x http://username:password@127.0.0.1:9191 https://www.google.com/
    
    0 讨论(0)
提交回复
热议问题