5

我想编写能够修改响应正文的 Node.js http 代理。到目前为止,我已经这样做了:

http = require('http'),
httpProxy = require('http-proxy');

var proxy = httpProxy.createProxyServer();
http.createServer( function (req, res){
    //here I want to change the body I guess
    proxy.web(req, res, {
        target: req.url
    });
}).listen(8013);

我曾尝试使用res.write(),但它给了我一个错误“发送后无法设置标题”。好吧,我不想更改标题,我想更改正文。
如何改变身体?任何建议将不胜感激。

4

2 回答 2

5

您会收到该错误,因为写入 HTTP 响应必然会更改标头;Content-Length必须是正确的。

我解决这个问题的方法是缓冲整个响应体,使用cheerio解析和摆弄,然后将结果发送给客户端。

我通过猴子补丁res.writeHead,res.writeres.end在将请求交给 http-proxy 模块之前完成了这项工作。

function requestHandler(req, res) {
    var writeHead = res.writeHead, write = res.write, end = res.end;

    res.writeHead = function(status, reason, headers) {
        if (res.headersSent) return req.socket.destroy(); // something went wrong; abort
        if (typeof reason == 'object') headers = reason;
        headers = headers || {};
        res.headers = headers;
        if (headers['content-type'] && headers['content-type'].substr(0,9) == 'text/html') { // we should only fiddle with HTML responses
            delete headers['transfer-encoding']; // since we buffer the entire response, we'll send a proper Content-Length later with no Transfer-Encoding.

            var buf = new Buffer();
            res.write = function(data, encoding) {
                if (Buffer.isBuffer(data)) buf = Buffer.concat([buf, data]); // append raw buffer
                else buf = Buffer.concat([buf, new Buffer(data, encoding)]); // append string with optional character encoding (default utf8)

                if (buf.length > 10 * 1024 * 1024) error('Document too large'); // sanity check: if the response is huge, bail.
                // ...we don't want to let someone bring down the server by filling up all our RAM.
            }

            res.end = function(data, encoding) {
                if (data) res.write(data, encoding);

                var $ = cheerio.load(buf.toString());

                // This is where we can modify the response.  For example,
                $('body').append('<p>Hi mom!</p>');

                buf = new Buffer($.html()); // we have to convert back to a buffer so that we can get the *byte count* (rather than character count) of the body

                res.headers['content-type'] = 'text/html; charset=utf-8'; // JS always deals in UTF-8.
                res.headers['content-length'] = buf.length;

                // Finally, send the modified response out using the real `writeHead`/`end`:
                writeHead.call(res, status, res.headers);
                end.call(res, buf);
            }


        } else {
            writeHead.call(res, status, headers); // if it's not HTML, let the response go through normally
        }
    }

    proxy.web(req, res, {
        target: req.url
    });

    function error(msg) { // utility function to report errors
        if (res.headersSent) end.call(res, msg);
        else {
            msg = new Buffer(msg);
            writeHead.call(res, 502, { 'Content-Type': 'text/plain', 'Content-Length': msg.length });
            end.call(res, msg);
        }
    }
}
于 2014-03-18T18:25:48.190 回答
1

从 http-proxy 模块的 git 存储库中查看此示例

https://github.com/nodejitsu/node-http-proxy/blob/master/examples/middleware/modifyResponse-middleware.js

于 2014-03-18T18:30:52.073 回答