summaryrefslogtreecommitdiffstats
path: root/debian/tests/test_modules/dicer/lib
diff options
context:
space:
mode:
Diffstat (limited to 'debian/tests/test_modules/dicer/lib')
-rw-r--r--debian/tests/test_modules/dicer/lib/Dicer.js239
-rw-r--r--debian/tests/test_modules/dicer/lib/HeaderParser.js110
-rw-r--r--debian/tests/test_modules/dicer/lib/PartStream.js11
3 files changed, 360 insertions, 0 deletions
diff --git a/debian/tests/test_modules/dicer/lib/Dicer.js b/debian/tests/test_modules/dicer/lib/Dicer.js
new file mode 100644
index 0000000..9d580cb
--- /dev/null
+++ b/debian/tests/test_modules/dicer/lib/Dicer.js
@@ -0,0 +1,239 @@
+var WritableStream = require('stream').Writable,
+ inherits = require('util').inherits;
+
+var StreamSearch = require('streamsearch');
+
+var PartStream = require('./PartStream'),
+ HeaderParser = require('./HeaderParser');
+
+var DASH = 45,
+ B_ONEDASH = Buffer.from('-'),
+ B_CRLF = Buffer.from('\r\n'),
+ EMPTY_FN = function() {};
+
+function Dicer(cfg) {
+ if (!(this instanceof Dicer))
+ return new Dicer(cfg);
+ WritableStream.call(this, cfg);
+
+ if (!cfg || (!cfg.headerFirst && typeof cfg.boundary !== 'string'))
+ throw new TypeError('Boundary required');
+
+ if (typeof cfg.boundary === 'string')
+ this.setBoundary(cfg.boundary);
+ else
+ this._bparser = undefined;
+
+ this._headerFirst = cfg.headerFirst;
+
+ var self = this;
+
+ this._dashes = 0;
+ this._parts = 0;
+ this._finished = false;
+ this._realFinish = false;
+ this._isPreamble = true;
+ this._justMatched = false;
+ this._firstWrite = true;
+ this._inHeader = true;
+ this._part = undefined;
+ this._cb = undefined;
+ this._ignoreData = false;
+ this._partOpts = (typeof cfg.partHwm === 'number'
+ ? { highWaterMark: cfg.partHwm }
+ : {});
+ this._pause = false;
+
+ this._hparser = new HeaderParser(cfg);
+ this._hparser.on('header', function(header) {
+ self._inHeader = false;
+ self._part.emit('header', header);
+ });
+
+}
+inherits(Dicer, WritableStream);
+
+Dicer.prototype.emit = function(ev) {
+ if (ev === 'finish' && !this._realFinish) {
+ if (!this._finished) {
+ var self = this;
+ process.nextTick(function() {
+ self.emit('error', new Error('Unexpected end of multipart data'));
+ if (self._part && !self._ignoreData) {
+ var type = (self._isPreamble ? 'Preamble' : 'Part');
+ self._part.emit('error', new Error(type + ' terminated early due to unexpected end of multipart data'));
+ self._part.push(null);
+ process.nextTick(function() {
+ self._realFinish = true;
+ self.emit('finish');
+ self._realFinish = false;
+ });
+ return;
+ }
+ self._realFinish = true;
+ self.emit('finish');
+ self._realFinish = false;
+ });
+ }
+ } else
+ WritableStream.prototype.emit.apply(this, arguments);
+};
+
+Dicer.prototype._write = function(data, encoding, cb) {
+ // ignore unexpected data (e.g. extra trailer data after finished)
+ if (!this._hparser && !this._bparser)
+ return cb();
+
+ if (this._headerFirst && this._isPreamble) {
+ if (!this._part) {
+ this._part = new PartStream(this._partOpts);
+ if (this._events.preamble)
+ this.emit('preamble', this._part);
+ else
+ this._ignore();
+ }
+ var r = this._hparser.push(data);
+ if (!this._inHeader && r !== undefined && r < data.length)
+ data = data.slice(r);
+ else
+ return cb();
+ }
+
+ // allows for "easier" testing
+ if (this._firstWrite) {
+ this._bparser.push(B_CRLF);
+ this._firstWrite = false;
+ }
+
+ this._bparser.push(data);
+
+ if (this._pause)
+ this._cb = cb;
+ else
+ cb();
+};
+
+Dicer.prototype.reset = function() {
+ this._part = undefined;
+ this._bparser = undefined;
+ this._hparser = undefined;
+};
+
+Dicer.prototype.setBoundary = function(boundary) {
+ var self = this;
+ this._bparser = new StreamSearch('\r\n--' + boundary);
+ this._bparser.on('info', function(isMatch, data, start, end) {
+ self._oninfo(isMatch, data, start, end);
+ });
+};
+
+Dicer.prototype._ignore = function() {
+ if (this._part && !this._ignoreData) {
+ this._ignoreData = true;
+ this._part.on('error', EMPTY_FN);
+ // we must perform some kind of read on the stream even though we are
+ // ignoring the data, otherwise node's Readable stream will not emit 'end'
+ // after pushing null to the stream
+ this._part.resume();
+ }
+};
+
+Dicer.prototype._oninfo = function(isMatch, data, start, end) {
+ var buf, self = this, i = 0, r, ev, shouldWriteMore = true;
+
+ if (!this._part && this._justMatched && data) {
+ while (this._dashes < 2 && (start + i) < end) {
+ if (data[start + i] === DASH) {
+ ++i;
+ ++this._dashes;
+ } else {
+ if (this._dashes)
+ buf = B_ONEDASH;
+ this._dashes = 0;
+ break;
+ }
+ }
+ if (this._dashes === 2) {
+ if ((start + i) < end && this._events.trailer)
+ this.emit('trailer', data.slice(start + i, end));
+ this.reset();
+ this._finished = true;
+ // no more parts will be added
+ if (self._parts === 0) {
+ self._realFinish = true;
+ self.emit('finish');
+ self._realFinish = false;
+ }
+ }
+ if (this._dashes)
+ return;
+ }
+ if (this._justMatched)
+ this._justMatched = false;
+ if (!this._part) {
+ this._part = new PartStream(this._partOpts);
+ this._part._read = function(n) {
+ self._unpause();
+ };
+ ev = this._isPreamble ? 'preamble' : 'part';
+ if (this._events[ev])
+ this.emit(ev, this._part);
+ else
+ this._ignore();
+ if (!this._isPreamble)
+ this._inHeader = true;
+ }
+ if (data && start < end && !this._ignoreData) {
+ if (this._isPreamble || !this._inHeader) {
+ if (buf)
+ shouldWriteMore = this._part.push(buf);
+ shouldWriteMore = this._part.push(data.slice(start, end));
+ if (!shouldWriteMore)
+ this._pause = true;
+ } else if (!this._isPreamble && this._inHeader) {
+ if (buf)
+ this._hparser.push(buf);
+ r = this._hparser.push(data.slice(start, end));
+ if (!this._inHeader && r !== undefined && r < end)
+ this._oninfo(false, data, start + r, end);
+ }
+ }
+ if (isMatch) {
+ this._hparser.reset();
+ if (this._isPreamble)
+ this._isPreamble = false;
+ else {
+ ++this._parts;
+ this._part.on('end', function() {
+ if (--self._parts === 0) {
+ if (self._finished) {
+ self._realFinish = true;
+ self.emit('finish');
+ self._realFinish = false;
+ } else {
+ self._unpause();
+ }
+ }
+ });
+ }
+ this._part.push(null);
+ this._part = undefined;
+ this._ignoreData = false;
+ this._justMatched = true;
+ this._dashes = 0;
+ }
+};
+
+Dicer.prototype._unpause = function() {
+ if (!this._pause)
+ return;
+
+ this._pause = false;
+ if (this._cb) {
+ var cb = this._cb;
+ this._cb = undefined;
+ cb();
+ }
+};
+
+module.exports = Dicer;
diff --git a/debian/tests/test_modules/dicer/lib/HeaderParser.js b/debian/tests/test_modules/dicer/lib/HeaderParser.js
new file mode 100644
index 0000000..8ccb6e5
--- /dev/null
+++ b/debian/tests/test_modules/dicer/lib/HeaderParser.js
@@ -0,0 +1,110 @@
+var EventEmitter = require('events').EventEmitter,
+ inherits = require('util').inherits;
+
+var StreamSearch = require('streamsearch');
+
+var B_DCRLF = Buffer.from('\r\n\r\n'),
+ RE_CRLF = /\r\n/g,
+ RE_HDR = /^([^:]+):[ \t]?([\x00-\xFF]+)?$/,
+ MAX_HEADER_PAIRS = 2000, // from node's http.js
+ MAX_HEADER_SIZE = 80 * 1024; // from node's http_parser
+
+function HeaderParser(cfg) {
+ EventEmitter.call(this);
+
+ var self = this;
+ this.nread = 0;
+ this.maxed = false;
+ this.npairs = 0;
+ this.maxHeaderPairs = (cfg && typeof cfg.maxHeaderPairs === 'number'
+ ? cfg.maxHeaderPairs
+ : MAX_HEADER_PAIRS);
+ this.buffer = '';
+ this.header = {};
+ this.finished = false;
+ this.ss = new StreamSearch(B_DCRLF);
+ this.ss.on('info', function(isMatch, data, start, end) {
+ if (data && !self.maxed) {
+ if (self.nread + (end - start) > MAX_HEADER_SIZE) {
+ end = (MAX_HEADER_SIZE - self.nread);
+ self.nread = MAX_HEADER_SIZE;
+ } else
+ self.nread += (end - start);
+
+ if (self.nread === MAX_HEADER_SIZE)
+ self.maxed = true;
+
+ self.buffer += data.toString('binary', start, end);
+ }
+ if (isMatch)
+ self._finish();
+ });
+}
+inherits(HeaderParser, EventEmitter);
+
+HeaderParser.prototype.push = function(data) {
+ var r = this.ss.push(data);
+ if (this.finished)
+ return r;
+};
+
+HeaderParser.prototype.reset = function() {
+ this.finished = false;
+ this.buffer = '';
+ this.header = {};
+ this.ss.reset();
+};
+
+HeaderParser.prototype._finish = function() {
+ if (this.buffer)
+ this._parseHeader();
+ this.ss.matches = this.ss.maxMatches;
+ var header = this.header;
+ this.header = {};
+ this.buffer = '';
+ this.finished = true;
+ this.nread = this.npairs = 0;
+ this.maxed = false;
+ this.emit('header', header);
+};
+
+HeaderParser.prototype._parseHeader = function() {
+ if (this.npairs === this.maxHeaderPairs)
+ return;
+
+ var lines = this.buffer.split(RE_CRLF), len = lines.length, m, h,
+ modded = false;
+
+ for (var i = 0; i < len; ++i) {
+ if (lines[i].length === 0)
+ continue;
+ if (lines[i][0] === '\t' || lines[i][0] === ' ') {
+ // folded header content
+ // RFC2822 says to just remove the CRLF and not the whitespace following
+ // it, so we follow the RFC and include the leading whitespace ...
+ this.header[h][this.header[h].length - 1] += lines[i];
+ } else {
+ m = RE_HDR.exec(lines[i]);
+ if (m) {
+ h = m[1].toLowerCase();
+ if (m[2]) {
+ if (this.header[h] === undefined)
+ this.header[h] = [m[2]];
+ else
+ this.header[h].push(m[2]);
+ } else
+ this.header[h] = [''];
+ if (++this.npairs === this.maxHeaderPairs)
+ break;
+ } else {
+ this.buffer = lines[i];
+ modded = true;
+ break;
+ }
+ }
+ }
+ if (!modded)
+ this.buffer = '';
+};
+
+module.exports = HeaderParser;
diff --git a/debian/tests/test_modules/dicer/lib/PartStream.js b/debian/tests/test_modules/dicer/lib/PartStream.js
new file mode 100644
index 0000000..b646ac0
--- /dev/null
+++ b/debian/tests/test_modules/dicer/lib/PartStream.js
@@ -0,0 +1,11 @@
+var inherits = require('util').inherits,
+ ReadableStream = require('stream').Readable;
+
+function PartStream(opts) {
+ ReadableStream.call(this, opts);
+}
+inherits(PartStream, ReadableStream);
+
+PartStream.prototype._read = function(n) {};
+
+module.exports = PartStream;