¿Cómo crear un proxy http simple en node.js?

83

Estoy tratando de crear un servidor proxy para pasar HTTP GETsolicitudes de un cliente a un sitio web de terceros (digamos Google). Mi proxy solo necesita reflejar las solicitudes entrantes en su ruta correspondiente en el sitio de destino, por lo que si la URL solicitada por mi cliente es:

127.0.0.1/images/srpr/logo11w.png

Se debe servir el siguiente recurso:

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

Esto es lo que se me ocurrió:

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

Funciona bien con páginas html, pero para otros tipos de archivos, solo devuelve una página en blanco o algún mensaje de error del sitio de destino (que varía en diferentes sitios).

Nasser Torabzade
fuente
1
A pesar de que los usos de respuesta http, una orden de módulos relacionados de menor a mayor abstracción son: node, http, connect, expresstomada de stackoverflow.com/questions/6040012/...
neaumusic

Respuestas:

102

No creo que sea una buena idea procesar la respuesta recibida del servidor de terceros. Esto solo aumentará la huella de memoria de su servidor proxy. Además, es la razón por la que su código no funciona.

En su lugar, intente transmitir la respuesta al cliente. Considere el siguiente fragmento:

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
  });
}
vmx
fuente
1
gracias, pero la cosa es que necesito procesar y / o manipular la respuesta del servidor de terceros, y luego pasarla a mi cliente. ¿Alguna idea de cómo implementar eso?
Nasser Torabzade
4
Deberá mantener los encabezados de tipo de contenido en ese caso. Los datos HTML funcionan como mencionaste porque el tipo de contenido predeterminado es text/html, para imágenes / PDF o cualquier otro contenido, asegúrate de pasar los encabezados correctos. Podré ofrecer más ayuda si comparte las modificaciones que aplica a las respuestas.
vmx
5
¿No debería utilizar el módulo proxy: github.com/nodejitsu/node-http-proxy ?
Maciej Jankowski
1
¿Alguien sabe cómo mantener los encabezados de las solicitudes?
Phil
1
agradable pero no del todo correcto ... si el servidor remoto tiene una redirección, este código no funcionará
Zibri
27

Aquí hay una implementación usando node-http-proxyde 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);
bosgood
fuente
4
Creo que node-http-proxy es principalmente para el proxy inverso ..., desde clientes externos a servidores internos que se ejecutan en IP locales y puertos no estándar a través del proxy de nodo inverso que acepta conexiones en puertos estándar en una dirección IP pública.
Soleado
@Samir Claro, esa es una de las cosas que puedes hacer con él. Es bastante flexible.
bosgood
12

Aquí hay un servidor proxy que usa una solicitud que maneja las redirecciones. Úselo presionando la URL de su proxy 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");
    }
}
Enrique
fuente
3
Hola, Henry, ¿cómo agregar encabezados para la solicitud?
KCN
La línea, res.end(e);causará unTypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer. Received an instance of Error
Niel de Wet el
6

Súper simple y legible, así es como se crea un servidor proxy local en un servidor HTTP local con solo Node.js (probado en v8.1.0 ). Lo encontré particularmente útil para las pruebas de integración, así que aquí está mi parte:

/**
 * 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

Francesco Casula
fuente
4

Su código no funciona para archivos binarios porque no se pueden convertir en cadenas en el controlador de eventos de datos. Si necesita manipular archivos binarios, deberá usar un búfer . Lo siento, no tengo un ejemplo de uso de un búfer porque en mi caso necesitaba manipular archivos HTML. Solo verifico el tipo de contenido y luego los archivos de texto / html los actualizo según sea necesario:

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();
});    
Mike Dilorenzo
fuente
3

Aquí hay una versión más optimizada de la respuesta de Mike anterior que obtiene el tipo de contenido de los sitios web correctamente, admite solicitudes POST y GET y usa el agente de usuario de su navegador para que los sitios web puedan identificar su proxy como un navegador. Simplemente puede configurar la URL cambiando url =y automáticamente configurará HTTP y HTTPS sin hacerlo manualmente.

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

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Jasoncornwall
fuente
1

Juste escribí un proxy en nodejs que se encarga de HTTPS con decodificación opcional del mensaje. Este proxy también puede agregar un encabezado de autenticación de proxy para pasar por un proxy corporativo. Debe proporcionar como argumento la URL para encontrar el archivo proxy.pac para configurar el uso del proxy corporativo.

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

Florian Prud'homme
fuente