Як створити простий http-проксі в node.js?


87

Я намагаюся створити проксі-сервер для передачі HTTP GETзапитів від клієнта на сторонній веб-сайт (скажімо, google). Моєму проксі просто потрібно віддзеркалити вхідні запити до відповідного шляху на цільовому сайті, тому, якщо запитувана моїм клієнтом URL-адреса:

127.0.0.1/images/srpr/logo11w.png

Потрібно подати такий ресурс:

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

Ось що я придумав:

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-сторінками, але для інших типів файлів він просто повертає порожню сторінку або якесь повідомлення про помилку з цільового сайту (яке різниться на різних сайтах).


1
Незважаючи на те, що відповідь використовує http, порядок пов'язаних модулів від низької до високої абстракції: node, http, connect, expressвзяті з stackoverflow.com/questions/6040012 / ...
neaumusic

Відповіді:


104

Я не думаю, що це хороша ідея обробляти відповіді, отримані від стороннього сервера. Це лише збільшить розмір пам'яті вашого проксі-сервера. Крім того, це причина, через яку ваш код не працює.

Натомість спробуйте передати відповідь клієнту. Розглянемо такий фрагмент:

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
спасибі, але справа в тому, що мені потрібно обробити та / або маніпулювати відповіддю стороннього сервера, а потім передати його моєму клієнту. будь-яка ідея, як це реалізувати?
Нассер Торабзаде

4
У цьому випадку вам потрібно буде підтримувати заголовки типу вмісту. Дані HTML працюють, як ви вже згадали, оскільки за замовчуванням для типу вмісту text/html, для зображень / pdfs або будь-якого іншого вмісту, переконайтеся, що ви передаєте правильні заголовки. Я зможу запропонувати додаткову допомогу, якщо ви поділитесь, які зміни ви застосовуєте до відповідей.
vmx

5
чи не слід використовувати модуль проксі: github.com/nodejitsu/node-http-proxy ?
Мацей Янковський

1
Хтось знає, як зберегти заголовки запитів?
Філ

1
приємно, але не зовсім правильно ... якщо віддалений сервер має переспрямування, цей код не буде працювати
Zibri

30

Ось реалізація з використанням node-http-proxynodejitsu.

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
Я думаю, що node-http-proxy в першу чергу призначений для зворотного проксі-сервера ..., від зовнішніх клієнтів до внутрішніх серверів, що працюють на локальних IP-адресах та нестандартних портах, через проксі-сервер зворотного вузла, який приймає з'єднання на стандартних портах із загальнодоступною IP-адресою.
Сонячно

@Samir Звичайно, це одна з речей, яку ти можеш з цим робити. Це досить гнучко.
bosgood

12

Ось проксі-сервер, що використовує запит, який обробляє переспрямування. Використовуйте його, натиснувши 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");
    }
}

3
Привіт, Генрі, як додати заголовки для запиту?
KCN

Лінія, res.end(e);спричинить -TypeError [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

Надзвичайно простий і читабельний, ось як ви створюєте локальний проксі-сервер для локального HTTP-сервера лише за допомогою Node.js (протестовано на v8.1.0 ). Я вважаю це особливо корисним для інтеграційного тестування, тому ось моя частка:

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

Ваш код не працює для двійкових файлів, оскільки вони не можуть бути передані в рядки в обробнику подій даних. Якщо вам потрібно маніпулювати бінарними файлами, вам потрібно буде використовувати буфер . На жаль, у мене немає прикладу використання буфера, оскільки в моєму випадку мені потрібно було маніпулювати файлами HTML. Я просто перевіряю тип вмісту, а потім для текстових / html-файлів оновлюю їх за потреби:

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

Ось більш оптимізована версія відповіді Майка вище, яка правильно визначає тип вмісту веб-сайтів, підтримує запит POST та GET та використовує User-Agent вашого браузера, щоб веб-сайти могли ідентифікувати ваш проксі як браузер. Ви можете просто встановити URL-адресу, змінивши, url =і вона автоматично встановить HTTP та HTTPS, не роблячи цього вручну.

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

введіть тут опис зображення

введіть тут опис зображення


1

Я просто написав проксі-сервер на nodejs, який піклується про HTTPS з необов'язковим декодуванням повідомлення. Цей проксі також може додати заголовок проксі-автентифікації, щоб пройти через корпоративний проксі. Вам потрібно вказати в якості аргументу URL-адресу для пошуку файлу proxy.pac, щоб налаштувати використання корпоративного проксі.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.