如何在 node.js 中创建一个简单的 http 代理?

Posted

技术标签:

【中文标题】如何在 node.js 中创建一个简单的 http 代理?【英文标题】:How to create a simple http proxy in node.js? 【发布时间】:2013-12-19 12:55:57 【问题描述】:

我正在尝试创建一个代理服务器以将来自客户端的 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页面,但对于其他类型的文件,它只是返回一个空白页面或来自目标站点的一些错误消息(不同站点不同)。

【问题讨论】:

虽然答案使用http,但相关模块从低抽象到高抽象的顺序是:nodehttpconnectexpress取自***.com/questions/6040012/… 【参考方案1】:

我认为处理从 3rd 方服务器收到的响应不是一个好主意。这只会增加代理服务器的内存占用。此外,这也是您的代码无法正常工作的原因。

而是尝试将响应传递给客户端。考虑以下 sn-p:

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

【讨论】:

谢谢,但问题是我需要处理和/或操纵第 3 方服务器的响应,然后将其传递给我的客户端。知道如何实现吗? 在这种情况下,您需要维护内容类型标头。 HTML 数据的工作方式如您所述,因为内容类型默认为 text/html,对于图像/pdf 或任何其他内容,请确保您传递正确的标题。如果您分享您对回复应用的修改,我将能够提供更多帮助。 您不应该使用代理模块:github.com/nodejitsu/node-http-proxy 吗? 有谁知道如何保留请求标头? 不错但不太正确...如果远程服务器有重定向,此代码将不起作用【参考方案2】:

这是使用来自 nodejitsu 的 node-http-proxy 的实现。

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

【讨论】:

我认为 node-http-proxy 主要用于反向代理...,通过接受标准连接的反向节点代理,从外部客户端到在本地 IP 和非标准端口上运行的内部服务器公共 IP 地址上的端口。 @Samir 当然,这是你可以用它做的事情之一。它非常灵活。【参考方案3】:

这是一个使用request 处理重定向的代理服务器。通过点击您的代理 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");
    

【讨论】:

嗨,henry,如何为请求添加标头? 行,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【参考方案4】:

这是 Mike 上面答案的更优化版本,它可以正确获取网站 Content-Type,支持 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')

【讨论】:

在使用代理库时遇到各种错误。上述解决方案也适用于处理需要传递与地址不同的主机名的代理场景。无需使用 SNICallback。 var options = 主机名:地址,端口:parsedPort,路径:clientRequest.url,方法:clientRequest.method,标头:'User-Agent':clientRequest.headers['user-agent'],主机:parsedHost ; 太棒了,我为我的网络过滤器绕过网站做了一个 Node.js 网络代理。 incog.dev/web(合金选项)。 :)【参考方案5】:

超级简单易读,以下是仅使用 Node.js(在 v8.1.0 上测试)创建本地代理服务器到本地 HTTP 服务器的方法。我发现它对集成测试特别有用,所以这是我的分享:

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

【讨论】:

所有这些都是本地主机上的端口转发。它实际上不是一个 http 代理。【参考方案6】:

您的代码不适用于二进制文件,因为它们无法在数据事件处理程序中转换为字符串。如果你需要操作二进制文件,你需要使用buffer。抱歉,我没有使用缓冲区的示例,因为在我的情况下,我需要操作 HTML 文件。我只是检查内容类型,然后根据需要更新 text/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();
);    

【讨论】:

【参考方案7】:

我刚刚在 nodejs 中编写了一个代理,它通过可选的消息解码来处理 HTTPS。 该代理还可以添加代理身份验证标头以通过公司代理。您需要提供 url 作为参数以查找 proxy.pac 文件以配置公司代理的使用。

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

【讨论】:

【参考方案8】:

这是我做的一个:

var http = require("http")
var Unblocker = require("unblocker")
var unblocker = Unblocker()
http.createServer(function(req,res)
  unblocker(req,res,function(err)
    var headers = "content-type": "text/html"
    if(err)
      res.writeHead(500, headers)
      return res.end(err.stack || err)
    
    if(req.url == "/")
      res.writeHead(200, headers)
      return res.end(
        `
        <title>Seventh Grade by Gary Soto</title>
        <embed src="https://www.cforks.org/Downloads/7.pdf"  />
        `
      )
    else
      res.writeHead(404, headers)
      return res.end("ERROR 404: File Not Found.");
    
  )
)
.listen(8080)

演示:view the demo:

【讨论】:

以上是关于如何在 node.js 中创建一个简单的 http 代理?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 mongodb (node.js) 在集合中创建一个包含所有值的数组

如何在 zip 文件归档器中创建文件夹 - node.js

如何在 node.js 中的 paypal 中创建可变的定期付款

如何在 Sequelize 中创建一个表以使用 Node JS 存储在 Postgresql 中

利用node的http模块创建一个简单的http服务器

在 Azure 中创建 Node.js Web 应用