Wie erstelle ich einen einfachen http-Proxy in node.js?

83

Ich versuche, einen Proxyserver zu erstellen, um HTTP GETAnforderungen von einem Client an eine Website eines Drittanbieters (z. B. Google) weiterzuleiten . Mein Proxy muss nur eingehende Anforderungen in den entsprechenden Pfad auf der Zielwebsite spiegeln. Wenn also die angeforderte URL meines Clients lautet:

127.0.0.1/images/srpr/logo11w.png

Die folgende Ressource sollte bereitgestellt werden:

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

Folgendes habe ich mir ausgedacht:

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

Es funktioniert gut mit HTML-Seiten, aber für andere Dateitypen wird nur eine leere Seite oder eine Fehlermeldung von der Zielsite zurückgegeben (die an verschiedenen Sites unterschiedlich ist).

Nasser Torabzade
quelle
1
Auch wenn die Antwort Verwendungen http, ein Auftrag von verwandten Module von niedrig bis hoch Abstraktion sind: node, http, connect, expressaus genommen stackoverflow.com/questions/6040012/...
neaumusic

Antworten:

102

Ich halte es nicht für eine gute Idee, die vom Server eines Drittanbieters empfangenen Antworten zu verarbeiten. Dies erhöht nur den Speicherbedarf Ihres Proxyservers. Außerdem ist dies der Grund, warum Ihr Code nicht funktioniert.

Versuchen Sie stattdessen, die Antwort an den Client weiterzuleiten. Betrachten Sie folgendes 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
  });
}
vmx
quelle
1
Danke, aber die Sache ist, dass ich die Antwort des Drittanbieter-Servers verarbeiten und / oder manipulieren und sie dann an meinen Client weitergeben muss. Irgendeine Idee, wie man das umsetzt?
Nasser Torabzade
4
In diesem Fall müssen Sie die Inhaltstyp-Header beibehalten. HTML-Daten funktionieren wie erwähnt, da der Inhaltstyp standardmäßig text/htmlfür Bilder / PDFs oder andere Inhalte sicherstellt, dass Sie die richtigen Header weitergeben. Ich kann Ihnen weitere Hilfe anbieten, wenn Sie uns mitteilen, welche Änderungen Sie an den Antworten vornehmen.
VMX
5
Sollten Sie kein Proxy-Modul verwenden: github.com/nodejitsu/node-http-proxy ?
Maciej Jankowski
1
Weiß jemand, wie man die Anforderungsheader behält?
Phil
1
nett, aber nicht ganz richtig ... wenn der Remote-Server eine Umleitung hat, wird dieser Code nicht funktionieren
Zibri
27

Hier ist eine Implementierung mit node-http-proxyvon 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
quelle
4
Ich denke, dass Node-http-Proxy in erster Linie für Reverse-Proxy gedacht ist ... Von externen Clients zu internen Servern, die auf lokalen IPs und nicht standardmäßigen Ports ausgeführt werden, über den Reverse-Node-Proxy, der Verbindungen an Standard-Ports auf einer öffentlichen IP-Adresse akzeptiert.
Sonniger
@ Samir Sicher, das ist eines der Dinge, die Sie damit machen können. Es ist ziemlich flexibel.
Bosgood
12

Hier ist ein Proxyserver, der eine Anforderung verwendet , die Weiterleitungen verarbeitet. Verwenden Sie es, indem Sie auf Ihre Proxy-URL http://domain.com:3000/?url=[your_url] klicken.

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");
    }
}
Henry
quelle
3
Hallo Henry, wie füge ich Header für die Anfrage hinzu?
KCN
Die Linie res.end(e);wird einTypeError [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 einfach und lesbar: So erstellen Sie einen lokalen Proxyserver für einen lokalen HTTP-Server mit nur Node.js (getestet in Version 8.1.0 ). Ich fand es besonders nützlich für Integrationstests. Hier ist mein Anteil:

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

Ihr Code funktioniert nicht für Binärdateien, da diese im Datenereignishandler nicht in Zeichenfolgen umgewandelt werden können. Wenn Sie Binärdateien bearbeiten müssen, müssen Sie einen Puffer verwenden . Leider habe ich kein Beispiel für die Verwendung eines Puffers, da ich in meinem Fall HTML-Dateien bearbeiten musste. Ich überprüfe nur den Inhaltstyp und aktualisiere sie dann nach Bedarf für Text- / HTML-Dateien:

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
quelle
3

Hier ist eine optimierte Version von Mikes Antwort oben, die den Inhaltstyp der Website korrekt abruft, POST- und GET-Anforderungen unterstützt und den User-Agent Ihres Browsers verwendet, damit Websites Ihren Proxy als Browser identifizieren können. Sie können die URL einfach durch Ändern festlegen url =und es werden automatisch HTTP- und HTTPS-Inhalte festgelegt, ohne dies manuell zu tun.

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

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Jasoncornwall
quelle
1

Ich habe einen Proxy in NodeJS geschrieben, der sich um HTTPS mit optionaler Dekodierung der Nachricht kümmert. Dieser Proxy kann auch einen Proxy-Authentifizierungs-Header hinzufügen, um einen Unternehmens-Proxy zu durchlaufen. Sie müssen als Argument die URL angeben, um die Datei proxy.pac zu finden, um die Verwendung des Unternehmensproxys zu konfigurieren.

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

Florian Prud'homme
quelle