How to create a simple http proxy in node.js?

2019-01-12 16:57发布

问题:

I'm trying to create a proxy server to pass HTTP GET requests from a client to a third party website (say google). My proxy just needs to mirror incoming requests to their corresponding path on the target site, so if my client's requested url is:

127.0.0.1/images/srpr/logo11w.png

the following resource should be served:

http://www.google.com/images/srpr/logo11w.png

here is what I came up with:

http.createServer(onRequest).listen(80);

function onRequest (client_req, client_res) {
    client_req.addListener("end", function() {
        var options = {
            hostname: 'www.google.com',
            port: 80,
            path: client_req.url,
            method: client_req.method
            headers: client_req.headers
        };
        var req=http.request(options, function(res) {
            var body;
            res.on('data', function (chunk) {
                body += chunk;
            });
            res.on('end', function () {
                 client_res.writeHead(res.statusCode, res.headers);
                 client_res.end(body);
            });
        });
        req.end();
    });
}

it works well with html pages, but for other types of files, it just returns a blank page or some error message from target site (which varies in different sites).

回答1:

I don't think it's a good idea to process response received from the 3rd party server. This will only increase your proxy server's memory footprint. Further, it's the reason why your code is not working.

Instead try passing the response through to the client. Consider following snippet:

var http = require('http');

http.createServer(onRequest).listen(3000);

function onRequest(client_req, client_res) {
  console.log('serve: ' + client_req.url);

  var options = {
    hostname: 'www.google.com',
    port: 80,
    path: client_req.url,
    method: client_req.method
    headers: client_req.headers
  };

  var proxy = http.request(options, function (res) {
    client_res.writeHead(res.statusCode, res.headers)
    res.pipe(client_res, {
      end: true
    });
  });

  client_req.pipe(proxy, {
    end: true
  });
}


回答2:

Here's an implementation using node-http-proxy from nodejitsu.

var http = require('http');
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});

http.createServer(function(req, res) {
    proxy.web(req, res, { target: 'http://www.google.com' });
}).listen(3000);


回答3:

Here's a proxy server using request that handles redirects. Use it by hitting your proxy URL http://domain.com:3000/?url=[your_url]

var http = require('http');
var url = require('url');
var request = require('request');

http.createServer(onRequest).listen(3000);

function onRequest(req, res) {

    var queryData = url.parse(req.url, true).query;
    if (queryData.url) {
        request({
            url: queryData.url
        }).on('error', function(e) {
            res.end(e);
        }).pipe(res);
    }
    else {
        res.end("no url found");
    }
}


回答4:

Your code doesn't work for binary files because they can't be cast to strings in the data event handler. If you need to manipulate binary files you'll need to use a buffer. Sorry, I do not have an example of using a buffer because in my case I needed to manipulate HTML files. I just check the content type and then for text/html files update them as needed:

app.get('/*', function(clientRequest, clientResponse) {
  var options = { 
    hostname: 'google.com',
    port: 80, 
    path: clientRequest.url,
    method: 'GET'
  };  

  var googleRequest = http.request(options, function(googleResponse) { 
    var body = ''; 

    if (String(googleResponse.headers['content-type']).indexOf('text/html') !== -1) {
      googleResponse.on('data', function(chunk) {
        body += chunk;
      }); 

      googleResponse.on('end', function() {
        // Make changes to HTML files when they're done being read.
        body = body.replace(/google.com/gi, host + ':' + port);
        body = body.replace(
          /<\/body>/, 
          '<script src="http://localhost:3000/new-script.js" type="text/javascript"></script></body>'
        );

        clientResponse.writeHead(googleResponse.statusCode, googleResponse.headers);
        clientResponse.end(body);
      }); 
    }   
    else {
      googleResponse.pipe(clientResponse, {
        end: true
      }); 
    }   
  }); 

  googleRequest.end();
});    


回答5:

Super simple and readable, here's how you create a local proxy server to a local HTTP server with just Node.js (tested on v8.1.0). I've found it particular useful for integration testing so here's my share:

/**
 * Once this is running open your browser and hit http://localhost
 * You'll see that the request hits the proxy and you get the HTML back
 */

'use strict';

const net = require('net');
const http = require('http');

const PROXY_PORT = 80;
const HTTP_SERVER_PORT = 8080;

let proxy = net.createServer(socket => {
    socket.on('data', message => {
        console.log('---PROXY- got message', message.toString());

        let serviceSocket = new net.Socket();

        serviceSocket.connect(HTTP_SERVER_PORT, 'localhost', () => {
            console.log('---PROXY- Sending message to server');
            serviceSocket.write(message);
        });

        serviceSocket.on('data', data => {
            console.log('---PROXY- Receiving message from server', data.toString();
            socket.write(data);
        });
    });
});

let httpServer = http.createServer((req, res) => {
    switch (req.url) {
        case '/':
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.end('<html><body><p>Ciao!</p></body></html>');
            break;
        default:
            res.writeHead(404, {'Content-Type': 'text/plain'});
            res.end('404 Not Found');
    }
});

proxy.listen(PROXY_PORT);
httpServer.listen(HTTP_SERVER_PORT);

https://gist.github.com/fracasula/d15ae925835c636a5672311ef584b999



回答6:

I juste wrote a proxy in nodejs that take care of HTTPS with optional decoding of the message. This proxy also can add proxy-authentification header in order to go through a corporate proxy. You need to give as argument the url to find the proxy.pac file in order to configurate the usage of corporate proxy.

https://github.com/luckyrantanplan/proxy-to-proxy-https