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

Javascriptnode.jsProxy

Javascript Problem Overview


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).

Javascript Solutions


Solution 1 - Javascript

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
  });
}

Solution 2 - Javascript

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);

Solution 3 - Javascript

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");
    }
}

Solution 4 - Javascript

Here's a more optimized version of Mike's answer above that gets the websites Content-Type properly, supports POST and GET request, and uses your browsers User-Agent so websites can identify your proxy as a browser. You can just simply set the URL by changing url = and it will automatically set HTTP and HTTPS stuff without manually doing it.

var express = require('express')
var app = express()
var https = require('https');
var http = require('http');
const { response } = require('express');


app.use('/', function(clientRequest, clientResponse) {
    var url;
    url = 'https://www.google.com'
    var parsedHost = url.split('/').splice(2).splice(0, 1).join('/')
    var parsedPort;
    var parsedSSL;
    if (url.startsWith('https://')) {
        parsedPort = 443
        parsedSSL = https
    } else if (url.startsWith('http://')) {
        parsedPort = 80
        parsedSSL = http
    }
    var options = { 
      hostname: parsedHost,
      port: parsedPort,
      path: clientRequest.url,
      method: clientRequest.method,
      headers: {
        'User-Agent': clientRequest.headers['user-agent']
      }
    };  
  
    var serverRequest = parsedSSL.request(options, function(serverResponse) { 
      var body = '';   
      if (String(serverResponse.headers['content-type']).indexOf('text/html') !== -1) {
        serverResponse.on('data', function(chunk) {
          body += chunk;
        }); 
  
        serverResponse.on('end', function() {
          // Make changes to HTML files when they're done being read.
          body = body.replace(`example`, `Cat!` );
  
          clientResponse.writeHead(serverResponse.statusCode, serverResponse.headers);
          clientResponse.end(body);
        }); 
      }   
      else {
        serverResponse.pipe(clientResponse, {
          end: true
        }); 
        clientResponse.contentType(serverResponse.headers['content-type'])
      }   
    }); 
  
    serverRequest.end();
  });    


  app.listen(3000)
  console.log('Running on 0.0.0.0:3000')

enter image description here

enter image description here

Solution 5 - Javascript

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

Solution 6 - Javascript

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();
});    

Solution 7 - Javascript

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

Solution 8 - Javascript

here is one that I made:

var http = require("http")
var Unblocker = require("unblocker")
var unblocker = Unblocker({})
http.createServer(function(req,res){
  unblocker(req,res,function(err){
    var headers = {"content-type": "text/html"}
    if(err){
      res.writeHead(500, headers)
      return res.end(err.stack || err)
    }
    if(req.url == "/"){
      res.writeHead(200, headers)
      return res.end(
        `
        <title>Seventh Grade by Gary Soto</title>
        <embed src="https://www.cforks.org/Downloads/7.pdf" width="1500" height="1500"/>
        `
      )
    }else{
      res.writeHead(404, headers)
      return res.end("ERROR 404: File Not Found.");
    }
  })
})
.listen(8080)

demo: view the demo:

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNasser TorabzadeView Question on Stackoverflow
Solution 1 - JavascriptvmxView Answer on Stackoverflow
Solution 2 - JavascriptbosgoodView Answer on Stackoverflow
Solution 3 - JavascriptHenryView Answer on Stackoverflow
Solution 4 - JavascriptjasoncornwallView Answer on Stackoverflow
Solution 5 - JavascriptFrancesco CasulaView Answer on Stackoverflow
Solution 6 - JavascriptMike DilorenzoView Answer on Stackoverflow
Solution 7 - JavascriptFlorian Prud'hommeView Answer on Stackoverflow
Solution 8 - JavascriptBobView Answer on Stackoverflow