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

IT技术 javascript node.js proxy
2021-02-11 20:27:36

我正在尝试创建一个代理服务器来将HTTP GET来自客户端的请求传递到第三方网站(比如谷歌)。我的代理只需要将传入请求镜像到目标站点上的相应路径,因此如果我的客户端请求的 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 页面,但对于其他类型的文件,它只返回一个空白页面或来自目标站点的一些错误消息(在不同站点中有所不同)。

6个回答

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

而是尝试将响应传递给客户端。考虑以下片段:

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
  });
}
谢谢,但问题是我需要处理和/或操作 3rd 方服务器的响应,然后将其传递给我的客户端。知道如何实现吗?
2021-03-18 20:27:36
不错但不太正确...如果远程服务器有重定向,则此代码将不起作用
2021-03-23 20:27:36
在这种情况下,您将需要维护内容类型标头。HTML 数据像您提到的那样工作,因为内容类型默认为text/html,对于图像/pdf 或任何其他内容,请确保传递正确的标题。如果您分享您对回复应用的修改,我将能够提供更多帮助。
2021-03-27 20:27:36
您不应该使用代理module:github.com/nodejitsu/node-http-proxy吗?
2021-04-09 20:27:36
有谁知道如何保留请求标头?
2021-04-09 20:27:36

这是使用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);
我认为 node-http-proxy 主要用于反向代理......,从外部客户端到在本地 IP 和非标准端口上运行的内部服务器,通过反向节点代理接受公共 IP 地址上的标准端口上的连接。
2021-03-23 20:27:36
@Samir 当然,这是你可以用它做的事情之一。它非常灵活。
2021-04-12 20:27:36

这是使用处理重定向的请求的代理服务器通过点击您的代理 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");
    }
}
嗨亨利,如何为请求添加标头?
2021-04-03 20:27:36
该行,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
2021-04-04 20:27:36

这是上面 Mike 回答的更优化版本,它正确获取网站内容类型,支持 POST 和 GET 请求,并使用您的浏览器用户代理,以便网站可以将您的代理识别为浏览器。您可以通过更改简单地设置 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')

在此处输入图片说明

在此处输入图片说明

太棒了,我为我的网络过滤器绕过网站制作了一个 Node.js 网络代理。incog.dev/web(合金选项)。:)
2021-03-31 20:27:36
使用代理库解决各种错误。上述解决方案也适用于处理需要传递与地址不同的主机名的代理场景。无需使用 SNICallback。var options = { hostname: address, port: parsedPort, path: clientRequest.url, method: clientRequest.method, headers: { 'User-Agent': clientRequest.headers['user-agent'], host : parsedHost } };
2021-04-03 20:27:36

超级简单易读,以下是如何使用 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 代理。
2021-04-11 20:27:36