Node.js'de basit bir http proxy nasıl oluşturulur?


87

HTTP GETBir istemciden gelen istekleri üçüncü taraf bir web sitesine (örneğin, google) iletmek için bir proxy sunucusu oluşturmaya çalışıyorum . Proxy'min gelen istekleri hedef sitedeki ilgili yoluna yansıtması gerekiyor, yani müşterimin istediği url:

127.0.0.1/images/srpr/logo11w.png

Aşağıdaki kaynak sunulmalıdır:

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

İşte bulduğum şey:

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

HTML sayfalarında iyi çalışır, ancak diğer dosya türleri için, yalnızca boş bir sayfa veya hedef siteden (farklı sitelerde değişiklik gösteren) bazı hata mesajları döndürür.


1
Cevap kullanımları olsa http, yüksek soyutlamaya düşük seviyesinden ilgili modüllerin bir emir gibidir: node, http, connect, expressalınan stackoverflow.com/questions/6040012/...
neaumusic

Yanıtlar:


104

Üçüncü taraf sunucudan alınan yanıtı işlemenin iyi bir fikir olduğunu düşünmüyorum. Bu, yalnızca proxy sunucunuzun bellek ayak izini artıracaktır. Ayrıca, kodunuzun çalışmamasının nedeni budur.

Bunun yerine yanıtı müşteriye iletmeyi deneyin. Snippet'i takip etmeyi düşünün:

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

1
teşekkürler, ancak önemli olan şu ki, 3. taraf sunucunun yanıtını işlemem ve / veya değiştirmem ve ardından bunu müşterime iletmem gerekiyor. bunu nasıl uygulayacağına dair bir fikrin var mı?
Nasser Torabzade

4
Bu durumda içerik türü başlıklarını korumanız gerekecektir. HTML verileri bahsettiğiniz gibi çalışır çünkü içerik türü varsayılan olarak text/htmlgörüntüler / pdf'ler veya diğer içerikler için doğru başlıkları iletmenizi sağlar. Yanıtlara uyguladığınız değişiklikleri paylaşırsanız daha fazla yardım sunabileceğim.
vmx

5
proxy modülünü kullanmanız gerekmez mi : github.com/nodejitsu/node-http-proxy ?
Maciej Jankowski

1
İstek başlıklarının nasıl saklanacağını bilen var mı?
Phil

1
güzel ama pek doğru değil ... uzak sunucunun yeniden yönlendirmesi varsa, bu kod çalışmayacaktır
Zibri

30

İşte node-http-proxynodejitsu'dan kullanılan bir uygulama .

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

4
Düğüm-http-proxy'nin öncelikle ters proxy için olduğunu düşünüyorum ... Dış istemcilerden yerel IP'lerde çalışan dahili sunuculara ve genel bir IP adresindeki standart bağlantı noktalarında bağlantıları kabul eden ters düğüm proxy'si aracılığıyla standart olmayan bağlantı noktalarına.
Sunny

@Samir Tabii, onunla yapabileceğiniz şeylerden biri de bu. Oldukça esnektir.
bosgood

12

Yönlendirmeleri işleyen, isteği kullanan bir proxy sunucusu burada . Proxy URL'nizi http://domain.com:3000/?url=[urlunuz] 'a basarak kullanın

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

3
Merhaba Henry, istek için başlık nasıl eklenir?
KCN

Çizgi, res.end(e);bir sebep olurTypeError [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

Son derece basit ve okunabilir, işte sadece Node.js ile yerel bir HTTP sunucusuna yerel bir proxy sunucusunu nasıl oluşturacağınız ( v8.1.0'da test edilmiştir ). Entegrasyon testi için özellikle yararlı buldum, bu yüzden benim payım:

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


4

Kodunuz, veri olay işleyicisindeki dizelere dönüştürülemediğinden ikili dosyalar için çalışmaz. İkili dosyaları değiştirmeniz gerekiyorsa, bir arabellek kullanmanız gerekecektir . Maalesef arabellek kullanma örneğim yok çünkü benim durumumda HTML dosyalarını değiştirmem gerekiyordu. Sadece içerik türünü kontrol ediyorum ve ardından metin / html dosyaları için bunları gerektiği gibi güncelliyorum:

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

3

İşte Mike'ın yukarıdaki yanıtının, Content-Type web sitelerini doğru bir şekilde alan, POST ve GET isteklerini destekleyen ve web sitelerinin proxy'nizi bir tarayıcı olarak tanımlayabilmesi için tarayıcılarınızın User-Agent kullanan daha optimize bir versiyonu. URL'yi değiştirerek kolayca ayarlayabilirsiniz ve url =manuel olarak yapmadan HTTP ve HTTPS öğelerini otomatik olarak ayarlayacaktır.

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

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin


1

Sadece nodejs'de mesajın isteğe bağlı deşifresiyle HTTPS ile ilgilenen bir proxy yazdım. Bu proxy, kurumsal bir proxy'den geçmek için proxy kimlik doğrulama başlığı da ekleyebilir. Kurumsal proxy kullanımını yapılandırmak için proxy.pac dosyasını bulmak için url'yi bağımsız değişken olarak vermeniz gerekir.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.