UNPKG

whistle

Version:

HTTP, HTTP2, HTTPS, Websocket debugging proxy

140 lines (125 loc) 3.53 kB
var Transform = require('pipestream').Transform; var util = require('util'); var iconv = require('iconv-lite'); var fileMgr = require('./file-mgr'); var DOCTYPE = Buffer.from('<!DOCTYPE html>\r\n'); function WhistleTransform(options) { Transform.call(this); options = options || {}; var value = parseInt((options.speed * 1000) / 8); if (value > 0) { this._speed = value; } if ((value = parseInt(options.delay)) > 0) { this._delay = value; } this._noDoctype = options.noDoctype; var charset = options.charset && String(options.charset); if (!iconv.encodingExists(charset)) { charset = 'utf8'; } this._isHtml = options.isHtml; this._body = getBuffer(options, 'body', charset); this._top = getBuffer(options, 'top', charset); this._bottom = getBuffer(options, 'bottom', charset); if (this._body || this._top || this._bottom) { if (options.strictHtml) { this._strictHtml = true; } else if (options.safeHtml) { this._safeHtml = true; } } } function getBuffer(options, name, charset) { var buf = options[name]; if (buf == null || Array.isArray(buf) || Buffer.isBuffer(buf)) { return buf; } return iconv.encode(buf + '', charset); } util.inherits(WhistleTransform, Transform); function filterHtml(list, isSafe) { if (!Array.isArray(list)) { return list; } list = list.filter(function(buf) { if (!buf || buf._strictHtml) { return false; } if (isSafe || !buf._safeHtml) { return true; } return false; }); return fileMgr.joinData(list); } function joinData(list) { return Array.isArray(list) ? fileMgr.joinData(list) : list; } WhistleTransform.prototype.allowInject = function (chunk) { if (!this._isHtml) { return true; } var first = chunk && chunk.toString().trim()[0]; var isStrict = !first || first === '<'; if (isStrict || this._strictHtml) { if (isStrict) { this._top = joinData(this._top); this._body = joinData(this._body); this._bottom = joinData(this._bottom); } return isStrict; } var isSafe = first !== '{' && first !== '['; if (this._safeHtml && !isSafe) { return false; } this._top = filterHtml(this._top, isSafe); this._body = filterHtml(this._body, isSafe); this._bottom = filterHtml(this._bottom, isSafe); return true; }; WhistleTransform.prototype._transform = function (chunk, encoding, callback) { var self = this; var cb = function () { if (self._allowInject && self._ended && self._bottom) { chunk = chunk ? Buffer.concat([chunk, self._bottom]) : self._bottom; self._bottom = null; } if (chunk && self._speed) { setTimeout(function () { callback(null, chunk); }, Math.round((chunk.length * 1000) / self._speed)); } else { callback(null, chunk); } }; if (!self._ended) { self._ended = !chunk; } if (!self._inited) { self._allowInject = self.allowInject(chunk); self._inited = true; if (self._allowInject) { if (self._body) { self._ended = true; chunk = self._body; self._body = null; } var top = self._top; if (top) { if (self._isHtml && !self._noDoctype) { top = Buffer.concat([DOCTYPE, top]); } chunk = chunk ? Buffer.concat([top, chunk]) : top; self._top = null; } } return self._delay ? setTimeout(cb, self._delay) : cb(); } if (self._ended) { chunk = null; } cb(); }; module.exports = WhistleTransform;