| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 | "use strict";var utils     = require("./lib/utils");var debug     = require("debug")("resp-mod");function RespModifier (opts) {    // options    opts               = opts || {};    opts.blacklist     = utils.toArray(opts.blacklist)     || [];    opts.whitelist     = utils.toArray(opts.whitelist)     || [];    opts.hostBlacklist = utils.toArray(opts.hostBlacklist) || [];    opts.rules         = opts.rules                        || [];    opts.ignore        = opts.ignore || opts.excludeList   || utils.defaultIgnoreTypes;    // helper functions    opts.regex = (function () {        var matches = opts.rules.map(function (item) {            return item.match.source;        }).join("|");        return new RegExp(matches);    })();    var respMod        = this;    respMod.opts       = opts;    respMod.middleware = respModifierMiddleware;    respMod.update = function (key, value) {        if (respMod.opts[key]) {            respMod.opts[key] = value;        }        return respMod;    };    function respModifierMiddleware(req, res, next) {        if (res._respModifier) {            debug("Reject req", req.url);            return next();        }        debug("Accept req", req.url);        res._respModifier = true;        var writeHead   = res.writeHead;        var runPatches  = true;        var write       = res.write;        var end         = res.end;        var singlerules = utils.isWhiteListedForSingle(req.url, respMod.opts.rules);        var withoutSingle = respMod.opts.rules.filter(function (rule) {            if (rule.paths && rule.paths.length) {                return false;            }            return true;        });        /**         * Exit early for blacklisted domains         */        if (respMod.opts.hostBlacklist.indexOf(req.headers.host) > -1) {            return next();        }        if (singlerules.length) {            modifyResponse(singlerules, true);        } else {            if (utils.isWhitelisted(req.url, respMod.opts.whitelist)) {                modifyResponse(withoutSingle, true);            } else {                if (!utils.hasAcceptHeaders(req) || utils.inBlackList(req.url, respMod.opts)) {                    debug("Black listed or no text/html headers", req.url);                    return next();                } else {                    modifyResponse(withoutSingle);                }            }        }        next();        /**         * Actually do the overwrite         * @param {Array} rules         * @param {Boolean} [force] - if true, will always attempt to perform         * an overwrite - regardless of whether it appears to be HTML or not         */        function modifyResponse(rules, force) {            req.headers["accept-encoding"] = "identity";            function restore() {                res.writeHead = writeHead;                res.write = write;                res.end = end;            }            res.push = function (chunk) {                res.data = (res.data || "") + chunk;            };            res.write = function (string, encoding) {                if (!runPatches) {                    return write.call(res, string, encoding);                }                if (string !== undefined) {                    var body = string instanceof Buffer ? string.toString(encoding) : string;                    // If this chunk appears to be valid, push onto the res.data stack                    if (force || (utils.isHtml(body) || utils.isHtml(res.data))) {                        res.push(body);                    } else {                        restore();                        return write.call(res, string, encoding);                    }                }                return true;            };            res.writeHead = function () {                if (!runPatches) {                    return writeHead.apply(res, arguments);                }                var headers = arguments[arguments.length - 1];                if (typeof headers === "object") {                    for (var name in headers) {                        if (/content-length/i.test(name)) {                            delete headers[name];                        }                    }                }                if (res.getHeader("content-length")) {                    res.removeHeader("content-length");                }                writeHead.apply(res, arguments);            };            res.end = function (string, encoding) {                res.data = res.data || "";                if (typeof string === "string") {                    res.data += string;                }                if (string instanceof Buffer) {                    res.data += string.toString();                }                if (!runPatches) {                    return end.call(res, string, encoding);                }                // Check if our body is HTML, and if it does not already have the snippet.                if (force || utils.isHtml(res.data) && !utils.snip(res.data)) {                    // Include, if necessary, replacing the entire res.data with the included snippet.                    res.data = utils.applyRules(rules, res.data, req, res);                    runPatches = false;                }                if (res.data !== undefined && !res._header) {                    res.setHeader("content-length", Buffer.byteLength(res.data, encoding));                }                end.call(res, res.data, encoding);            };        }    }    return respMod;}module.exports = function (opts) {    var resp = new RespModifier(opts);    return resp.middleware;};module.exports.create = function (opts) {    var resp = new RespModifier(opts);    return resp;};module.exports.utils = utils;
 |