Jak stworzyć proste proxy HTTP w node.js?

86

Próbuję utworzyć serwer proxy, aby przekazywać HTTP GETżądania od klienta do witryny innej firmy (np. Google). Mój serwer proxy musi tylko odzwierciedlać przychodzące żądania do odpowiedniej ścieżki w witrynie docelowej, więc jeśli żądany adres URL mojego klienta to:

127.0.0.1/images/srpr/logo11w.png

Należy udostępnić następujący zasób:

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

Oto co wymyśliłem:

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

Działa dobrze ze stronami html, ale w przypadku innych typów plików zwraca po prostu pustą stronę lub komunikat o błędzie z witryny docelowej (który różni się w różnych witrynach).

Nasser Torabzade
źródło
1
Nawet jeśli zastosowania odpowiedź http, zamówienie powiązanych modułów od niskiego do wysokiego abstrakcji są: node, http, connect, expresszaczerpnięte z stackoverflow.com/questions/6040012/...
neaumusic

Odpowiedzi:

103

Uważam, że przetwarzanie odpowiedzi otrzymanej z serwera innej firmy nie jest dobrym pomysłem. Zwiększy to tylko zużycie pamięci serwera proxy. Co więcej, jest to powód, dla którego twój kod nie działa.

Zamiast tego spróbuj przekazać odpowiedź do klienta. Rozważ następujący fragment:

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
źródło
1
dzięki, ale chodzi o to, że muszę przetwarzać i / lub manipulować odpowiedzią serwera innej firmy, a następnie przekazać ją mojemu klientowi. masz pomysł, jak to zrealizować?
Nasser Torabzade
4
W takim przypadku będziesz musiał zachować nagłówki typu zawartości. Dane HTML działają tak, jak wspomniałeś, ponieważ domyślnie typ zawartości to text/html, w przypadku obrazów / plików PDF lub jakiejkolwiek innej treści, upewnij się, że przekazujesz prawidłowe nagłówki. Będę mógł zaoferować więcej pomocy, jeśli podzielisz się modyfikacjami zastosowanymi w odpowiedziach.
vmx
5
czy nie powinieneś używać modułu proxy: github.com/nodejitsu/node-http-proxy ?
Maciej Jankowski
1
Czy ktoś wie, jak zachować nagłówki żądań?
Phil
1
ładne, ale niezupełnie ... jeśli zdalny serwer ma przekierowanie, ten kod nie zadziała
Zibri
30

Oto implementacja wykorzystująca node-http-proxy od 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
źródło
4
Myślę, że węzeł-http-proxy służy przede wszystkim do odwrotnego proxy ..., Od klientów zewnętrznych do serwerów wewnętrznych działających na lokalnych adresach IP i niestandardowych portach przez odwrotny serwer proxy, który akceptuje połączenia na standardowych portach na publicznym adresie IP.
Słoneczny
@Samir Jasne, to jedna z rzeczy, które możesz z tym zrobić. Jest dość elastyczny.
bosgood
12

Oto serwer proxy używający żądania, które obsługuje przekierowania. Użyj go, naciskając adres URL serwera proxy http://domain.com:3000/?url=[twoj_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");
    }
}
Henz
źródło
3
Cześć henry, jak dodać nagłówki do żądania?
KCN
Linia res.end(e);spowodujeTypeError [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
6

Super prosty i czytelny, oto jak utworzyć lokalny serwer proxy na lokalnym serwerze HTTP za pomocą samego Node.js (testowane w wersji 8.1.0 ). Uważam, że jest to szczególnie przydatne do testowania integracji, więc oto mój udział:

/**
 * 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
źródło
4

Twój kod nie działa w przypadku plików binarnych, ponieważ nie można ich rzutować na ciągi w module obsługi zdarzeń danych. Jeśli chcesz manipulować plikami binarnymi, musisz użyć bufora . Przepraszam, nie mam przykładu użycia bufora, ponieważ w moim przypadku potrzebowałem manipulować plikami HTML. Po prostu sprawdzam typ zawartości, a następnie pliki tekstowe / html aktualizuję w razie potrzeby:

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
źródło
3

Oto bardziej zoptymalizowana wersja powyższej odpowiedzi Mike'a, która poprawnie pobiera typ zawartości witryn, obsługuje żądania POST i GET oraz używa agenta użytkownika przeglądarki, aby witryny mogły identyfikować serwer proxy jako przeglądarkę. Możesz po prostu ustawić adres URL, zmieniając, url =a on automatycznie ustawi HTTP i HTTPS bez robienia tego ręcznie.

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

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

jasoncornwall
źródło
1

Właśnie napisałem proxy w nodejs, które zajmuje się HTTPS z opcjonalnym dekodowaniem wiadomości. Ten serwer proxy może również dodać nagłówek uwierzytelniania proxy, aby przejść przez korporacyjny serwer proxy. Musisz podać jako argument adres URL, aby znaleźć plik proxy.pac i skonfigurować użycie korporacyjnego serwera proxy.

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

Florian Prud'homme
źródło