【问题标题】:Node JS HTTP Proxy hanging upNode JS HTTP 代理挂了
【发布时间】:2020-03-17 13:07:18
【问题描述】:

我有一个 http 代理来代理任何网站并在将 HTML 提供回客户端之前注入一些自定义 JS 文件。每当我尝试访问代理网站时,它都会挂起或浏览器似乎无法确定加载。但是当我检查 HTML 源代码时,我成功地注入了我的自定义 JavaScript 文件。代码如下:

const cheerio = require('cheerio');
const http = require('http');
const httpProxy = require('http-proxy');
const { ungzip } = require('node-gzip');

_initProxy(host: string) {
    let proxy = httpProxy.createProxyServer({});
    let option = {
        target: host,
        selfHandleResponse: true
    };

    proxy.on('proxyRes', function (proxyRes, req, res) {
        let body = [];
        proxyRes.on('data', function (chunk) {
            body.push(chunk);
        });
        proxyRes.on('end', async function () {
            let buffer = Buffer.concat(body);
            if (proxyRes.headers['content-encoding'] === 'gzip') {
                try {
                    let $ = null;
                    const decompressed = await ungzip(buffer);
                    const scriptTag = '<script src="my-customjs.js"></script>';
                    $ = await cheerio.load(decompressed.toString());
                    await $('body').append(scriptTag);
                    res.end($.html());
                } catch (e) {
                    console.log(e);
                }
            }
        });
    });

    let server = http.createServer(function (req, res) {
        proxy.web(req, res, option, function (e) {
            console.log(e);
        });
    });

    console.log("listening on port 5051");
    server.listen(5051);
}

如果我做错了什么,有人可以告诉我,看起来 node-http-proxy 正在死去很多并且不能太依赖它,因为代理有时可以工作并在下次运行时死掉,具体取决于我运行了多少次服务器。

【问题讨论】:

  • 奇怪你有问题,我觉得很好。虽然你不处理响应未压缩时的情况。
  • 我愿意,只是想简化这篇文章的代码。
  • 不是你的问题的真正答案,但你知道香草 nginx 可以通过几行配置来做到这一点,并且可能比节点代理更有效,因为你不需要解析html? gist.github.com/Tofandel/1ce9635335c6969b5bc0289140b925cd
  • 我可能只是为您的问题添加了一个灯泡。您的脚本是否也被代理了?检查您是否可以访问它。如果不是,可能只是服务器也在尝试代理 js 文件并且不知道如何解析它们,它们将有一个空的正文并挂断浏览器
  • @NizarB。我的意思是 Cheerio 正在解析你所有的 html,这对于代理来说是相当内存和 cpu 密集型的,当你添加一个不需要这样做的脚本时,你可以找到字符串 "

标签: javascript node.js reverse-proxy http-proxy node-http-proxy


【解决方案1】:

您的代码看起来不错,所以我很好奇并尝试了它。

虽然您确实记录了一些错误,但您并没有处理几种情况:

  • 服务器返回一个没有响应的正文(cheerio 会在这种情况下生成一个空的 HTML 正文)
  • 服务器返回未压缩的响应(您的代码将默默地丢弃响应)

我对你的代码做了一些修改。

更改初始选项

let proxy = httpProxy.createProxyServer({
    secure: false,
    changeOrigin: true
});
  • 不验证 TLS 证书 secure: false
  • 发送正确的Host标头changeOrigin: true

删除if 语句并用三元替换它

const isCompressed = proxyRes.headers['content-encoding'] === 'gzip';
const decompressed = isCompressed ? await ungzip(buffer) : buffer;

您还可以删除 cheerio 上的 2 个 await,Cheerio 不是异步的,不会返回 awaitable。

最终代码

这是最终的代码,它可以工作。您提到“看起来 node-http-proxy 正在死去很多 [...] 取决于我运行服务器的次数。”我没有遇到过这样的稳定​​性问题,所以如果发生这种情况,您的问题可能出在其他地方(坏内存?)

const cheerio = require('cheerio');
const http = require('http');
const httpProxy = require('http-proxy');
const { ungzip } = require('node-gzip');

const host = 'https://github.com';

let proxy = httpProxy.createProxyServer({
    secure: false,
    changeOrigin: true
});
let option = {
    target: host,
    selfHandleResponse: true
};

proxy.on('proxyRes', function (proxyRes, req, res) {

    console.log(`Proxy response with status code: ${proxyRes.statusCode} to url ${req.url}`);
    if (proxyRes.statusCode == 301) {
        throw new Error('You should probably do something here, I think there may be an httpProxy option to handle redirects');
    }
    let body = [];
    proxyRes.on('data', function (chunk) {
        body.push(chunk);
    });
    proxyRes.on('end', async function () {
        let buffer = Buffer.concat(body);
        try {
            let $ = null;
            const isCompressed = proxyRes.headers['content-encoding'] === 'gzip';
            const decompressed = isCompressed ? await ungzip(buffer) : buffer;
            const scriptTag = '<script src="my-customjs.js"></script>';
            $ = cheerio.load(decompressed.toString());
            $('body').append(scriptTag);
            res.end($.html());
        } catch (e) {
            console.log(e);
        }
    });
});

let server = http.createServer(function (req, res) {
    proxy.web(req, res, option, function (e) {
        console.log(e);
    });
});

console.log("listening on port 5051");
server.listen(5051);

【讨论】:

  • 感谢您的回答。我让它在我的代码的原始版本上也能正常工作。感谢您在代码版本中所做的改进。但是,我确实在我的线程中提到,在代理网站中注入自定义 JS 文件时,我也可以正常工作。我试图解决的主要问题是,它可能会导致不确定的加载。我可能会开始监控更多,但我真的怀疑这是 RAM 问题。将尝试发布 StackBlitz 来复制我的问题。
  • 另一个注意事项,这与 HTTP 协议配合得很好,仅将“安全”标志设置为 false 将无法正确加载 HTTPS 网站。
  • @NizarB。不清楚它是否工作,因为你说了几个结果......“挂断”,“无限加载”,“成功注入”,我想知道后者是否可能在一个空体内,这是我的看过几次。我认为还有更多代码,感谢您发布的代码已大大简化。
  • @NizarB。关于 HTTPS,这对我来说非常适合https://github.com,上面发布了确切的代码(在节点 12.13.0,http-proxy 1.18.0 上)。问题可能出在您的代码中的其他地方吗?你能确认我发布的代码按照我描述的方式工作吗?如果是这样,那么问题可能出在代码的其他地方。如果没有,那么一些时髦的事情正在发生..
  • 肯定发生了一些奇怪的事情,我正在努力寻找它。您是否尝试过不同的 HTTPS 网站?对我来说,它看起来坏了,仍然永远加载。我会及时通知你。
【解决方案2】:

我最终使用 CherryPy 编写了一个小型 Python 服务器,并使用 mitmproxy 代理了 Web 应用程序。现在一切都很顺利。也许我在使用 node-http-proxy 时做错了,但我也对在生产环境中使用它持怀疑态度。

【讨论】:

  • 这不能被认为是对您提出的问题的了解。
  • @MWaheed 对,感谢您的 AWASER。
猜你喜欢
  • 2015-07-21
  • 1970-01-01
  • 2012-11-15
  • 1970-01-01
  • 2019-11-05
  • 1970-01-01
  • 2016-02-07
  • 2020-02-06
  • 1970-01-01
相关资源
最近更新 更多