node.js에서 간단한 http 프록시를 만드는 방법은 무엇입니까?


83

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

답변:


102

타사 서버에서받은 응답을 처리하는 것은 좋은 생각이 아니라고 생각합니다. 이렇게하면 프록시 서버의 메모리 공간 만 늘어납니다. 또한 코드가 작동하지 않는 이유입니다.

대신 클라이언트에 응답을 전달하십시오. 다음 스 니펫을 고려하십시오.

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
감사합니다.하지만 문제는 타사 서버의 응답을 처리 및 / 또는 조작 한 다음 클라이언트에 전달해야한다는 것입니다. 그것을 구현하는 방법을 아십니까?
Nasser Torabzade 2013

4
이 경우 콘텐츠 유형 헤더를 유지해야합니다. HTML 데이터는 앞서 언급 한대로 작동합니다. content-type은 기본적으로 text/html이미지 / pdf 또는 기타 콘텐츠에 대해이므로 올바른 헤더를 전달해야합니다. 답변에 적용한 수정 사항을 공유하시면 더 많은 도움을 드릴 수 있습니다.
VMX

5
프록시 모듈을 사용하지 않아야합니다 : github.com/nodejitsu/node-http-proxy ?
Maciej Jankowski

1
누구든지 요청 헤더를 유지하는 방법을 알고 있습니까?
Phil

1
좋은 아니지만 꽤 괜찮 ... 원격 서버는 리디렉션이있는 경우,이 코드하지 않습니다 일
Zibri

27

다음 node-http-proxy은 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);

4
node-http-proxy는 주로 역방향 프록시 용이라고 생각합니다. 외부 클라이언트에서 공용 IP 주소의 표준 포트에서 연결을 허용하는 역방향 노드 프록시를 통해 로컬 IP에서 실행되는 내부 서버와 비표준 포트로.
Sunny

@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

매우 간단하고 읽기 쉽습니다 . Node.js만으로 로컬 HTTP 서버에 로컬 프록시 서버를 만드는 방법은 다음과 같습니다 ( 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

이진 파일은 데이터 이벤트 처리기에서 문자열로 캐스트 할 수 없기 때문에 코드가 작동하지 않습니다. 바이너리 파일을 조작해야한다면 buffer 를 사용해야합니다 . 죄송합니다. 제 경우에는 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

다음은 웹 사이트 Content-Type을 올바르게 가져오고 POST 및 GET 요청을 지원하며 웹 사이트에서 프록시를 브라우저로 식별 할 수 있도록 브라우저 User-Agent를 사용하는 위의 Mike의 답변에 대한보다 최적화 된 버전입니다. 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')

여기에 이미지 설명 입력

여기에 이미지 설명 입력


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.