Comparing version 0.0.4 to 0.0.5
158
lib/Dicer.js
@@ -12,75 +12,32 @@ var EventEmitter = require('events').EventEmitter, | ||
function Dicer(conf) { | ||
function Dicer(opts) { | ||
if (!(this instanceof Dicer)) | ||
return new Dicer(conf); | ||
return new Dicer(opts); | ||
EventEmitter.call(this); | ||
if (typeof conf.boundary !== 'string') | ||
if (!opts.headerFirst && typeof opts.boundary !== 'string') | ||
throw new TypeError('Boundary required'); | ||
var self = this, isPreamble = true, inHeader = true, dashes = 0; | ||
if (typeof opts.boundary === 'string') | ||
this.setBoundary(opts.boundary); | ||
else | ||
this._bparser = undefined; | ||
this._headerFirst = opts.headerFirst; | ||
var self = this; | ||
this._dashes = 0; | ||
this._isPreamble = true; | ||
this._justMatched = false; | ||
this._firstWrite = true; | ||
this._bparser = new StreamSearch('\r\n--' + conf.boundary); | ||
this._bparser.on('info', function redo(isMatch, data, start, end) { | ||
var buf; | ||
if (!self._part && self._justMatched && data) { | ||
var i = 0; | ||
while (dashes < 2 && (start + i) < end) { | ||
if (data[start + i] === 45) { | ||
++i; | ||
++dashes; | ||
} else { | ||
if (dashes) | ||
buf = B_ONEDASH; | ||
dashes = 0; | ||
break; | ||
} | ||
} | ||
if (dashes === 2) { | ||
if ((start + i) < end && self._events['trailer']) | ||
self.emit('trailer', data.slice(start + i, end)); | ||
self.emit('end'); | ||
self.destroy(); | ||
} | ||
if (dashes) | ||
return; | ||
} | ||
if (self._justMatched) | ||
self._justMatched = false; | ||
if (!self._part) { | ||
self._part = new PartStream(); | ||
self.emit(isPreamble ? 'preamble' : 'part', self._part); | ||
if (!isPreamble) | ||
inHeader = true; | ||
} | ||
if (data && start < end) { | ||
if (isPreamble || !inHeader) { | ||
if (buf) | ||
self._part.emit('data', buf); | ||
self._part.emit('data', data.slice(start, end)); | ||
} else if (!isPreamble && inHeader) { | ||
if (buf) | ||
self._hparser.push(buf); | ||
var r = self._hparser.push(data.slice(start, end)); | ||
if (!inHeader && r !== undefined && r < end) | ||
redo(false, data, start + r, end); | ||
} | ||
} | ||
if (isMatch) { | ||
if (isPreamble) | ||
isPreamble = false; | ||
self._hparser.reset(); | ||
self._part.emit('end'); | ||
self._part = undefined; | ||
self._justMatched = true; | ||
dashes = 0; | ||
} | ||
}); | ||
this._inHeader = true; | ||
this._part = undefined; | ||
this._hparser = new HeaderParser(); | ||
this._hparser.on('header', function(header) { | ||
inHeader = false; | ||
self._inHeader = false; | ||
self._part.emit('header', header); | ||
}); | ||
this.writable = true; | ||
@@ -96,2 +53,14 @@ } | ||
if (this._headerFirst && this._isPreamble) { | ||
if (!this._part) { | ||
this._part = new PartStream(); | ||
this.emit('preamble', this._part); | ||
} | ||
var r = this._hparser.push(data); | ||
if (!this._inHeader && r !== undefined && r < data.length) | ||
data = data.slice(r); | ||
else | ||
return true; | ||
} | ||
// allows for "easier" testing | ||
@@ -119,7 +88,70 @@ if (this._firstWrite) { | ||
this._hparser = undefined; | ||
this._justMatched = false; | ||
this._firstWrite = true; | ||
this.emit('close'); | ||
}; | ||
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._oninfo = function(isMatch, data, start, end) { | ||
var buf; | ||
if (!this._part && this._justMatched && data) { | ||
var i = 0; | ||
while (this._dashes < 2 && (start + i) < end) { | ||
if (data[start + i] === 45) { | ||
++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.emit('end'); | ||
this.destroy(); | ||
} | ||
if (this._dashes) | ||
return; | ||
} | ||
if (this._justMatched) | ||
this._justMatched = false; | ||
if (!this._part) { | ||
this._part = new PartStream(); | ||
this.emit(this._isPreamble ? 'preamble' : 'part', this._part); | ||
if (!this._isPreamble) | ||
this._inHeader = true; | ||
} | ||
if (data && start < end) { | ||
if (this._isPreamble || !this._inHeader) { | ||
if (buf) | ||
this._part.emit('data', buf); | ||
this._part.emit('data', data.slice(start, end)); | ||
} else if (!this._isPreamble && this._inHeader) { | ||
if (buf) | ||
this._hparser.push(buf); | ||
var r = this._hparser.push(data.slice(start, end)); | ||
if (!this._inHeader && r !== undefined && r < end) | ||
this._oninfo(false, data, start + r, end); | ||
} | ||
} | ||
if (isMatch) { | ||
if (this._isPreamble) | ||
this._isPreamble = false; | ||
this._hparser.reset(); | ||
this._part.emit('end'); | ||
this._part = undefined; | ||
this._justMatched = true; | ||
this._dashes = 0; | ||
} | ||
}; | ||
module.exports = Dicer; |
{ "name": "dicer", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"author": "Brian White <mscdex@mscdex.net>", | ||
@@ -4,0 +4,0 @@ "description": "A very fast streaming multipart parser for node.js", |
@@ -35,3 +35,2 @@ | ||
var m; | ||
console.dir(req.headers['content-type']); | ||
if (req.method === 'POST' | ||
@@ -83,5 +82,5 @@ && req.headers['content-type'] | ||
* **preamble**(< _ReadableStream_ >stream) - Emitted for preamble data if you should happen to need it (this should almost always be ignored though). | ||
* **preamble**(< _PartStream_ >stream) - Emitted for preamble if you should happen to need it (can usually be ignored). | ||
* **trailer**(< _Buffer_ >data) - Emitted when trailing data was found after the terminating boundary (as with the preamble, this should be ignored too). | ||
* **trailer**(< _Buffer_ >data) - Emitted when trailing data was found after the terminating boundary (as with the preamble, this can usually be ignored too). | ||
@@ -96,6 +95,8 @@ | ||
* **reset**() - _(void)_ - Resets internal state. | ||
* **headerFirst** - _boolean_ - If true, preamble header parsing will be performed first. | ||
* **setBoundary**(< _string_ >boundary) - _(void)_ - Sets the boundary to use for parsing and performs some initialization needed for parsing. You should only need to use this if you set `headerFirst` to true in the constructor and are parsing the boundary from the preamble header. | ||
_PartStream_ is a _ReadableStream_ | ||
@@ -106,2 +107,2 @@ | ||
* **header**(< _object_ >header) - An object containing the header for this particular part. Each property value is an _array_ of one or more header values. | ||
* **header**(< _object_ >header) - An object containing the header for this particular part. Each property value is an _array_ of one or more string values. |
@@ -33,3 +33,3 @@ var assert = require('assert'), | ||
parser.on('header', function(header) { | ||
assert(!fired, errPrefix + 'Header event fired more than once') | ||
assert(!fired, errPrefix + 'Header event fired more than once'); | ||
fired = true; | ||
@@ -36,0 +36,0 @@ assert.deepEqual(header, v.expected, errPrefix + 'Parsed result mismatch'); |
var Dicer = require('..'); | ||
var assert = require('assert'), | ||
fs = require('fs'); | ||
fs = require('fs'), | ||
inspect = require('util').inspect; | ||
@@ -9,3 +10,3 @@ var FIXTURES_ROOT = __dirname + '/fixtures/'; | ||
{ source: 'nested', | ||
boundary: 'AaB03x', | ||
opts: { boundary: 'AaB03x' }, | ||
chsize: 32, | ||
@@ -16,3 +17,3 @@ nparts: 2, | ||
{ source: 'many', | ||
boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR', | ||
opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' }, | ||
chsize: 16, | ||
@@ -22,10 +23,40 @@ nparts: 7, | ||
}, | ||
{ source: 'nested-full', | ||
opts: { boundary: 'AaB03x', headerFirst: true }, | ||
chsize: 32, | ||
nparts: 2, | ||
what: 'One nested multipart with preceding header' | ||
}, | ||
].forEach(function(v) { | ||
var fd = fs.openSync(FIXTURES_ROOT + v.source + '/original', 'r'), n = 0, | ||
var fd, n = 0, buffer = new Buffer(v.chsize), | ||
errPrefix = '[' + v.what + ']: ', | ||
buffer = new Buffer(v.chsize), | ||
state = { done: false, parts: [] }, | ||
state = { done: false, parts: [], preamble: undefined }, | ||
part = { body: undefined, bodylen: 0, header: undefined }; | ||
var dicer = new Dicer({ boundary: v.boundary }); | ||
fd = fs.openSync(FIXTURES_ROOT + v.source + '/original', 'r') | ||
var dicer = new Dicer(v.opts); | ||
dicer.on('preamble', function(p) { | ||
var preamble = { body: undefined, bodylen: 0, header: undefined }; | ||
p.on('header', function(h) { | ||
preamble.header = h; | ||
}); | ||
p.on('data', function(data) { | ||
// make a copy because we are using readSync which re-uses a buffer ... | ||
var copy = new Buffer(data.length); | ||
data.copy(copy); | ||
data = copy; | ||
if (!preamble.body) | ||
preamble.body = [ data ]; | ||
else | ||
preamble.body.push(data); | ||
preamble.bodylen += data.length; | ||
}); | ||
p.on('end', function() { | ||
if (part.body) | ||
part.body = Buffer.concat(part.body, part.bodylen); | ||
if (part.body || preamble.header) | ||
state.preamble = preamble; | ||
}); | ||
}); | ||
dicer.on('part', function(p) { | ||
@@ -68,5 +99,35 @@ p.on('header', function(h) { | ||
assert(state.done, errPrefix + 'Parser did not finish'); | ||
var preamble = undefined; | ||
if (fs.existsSync(FIXTURES_ROOT + v.source + '/preamble')) { | ||
var prebody = fs.readFileSync(FIXTURES_ROOT + v.source + '/preamble'); | ||
if (prebody.length) { | ||
preamble = { | ||
body: prebody, | ||
bodylen: prebody.length, | ||
header: undefined | ||
}; | ||
} | ||
} | ||
if (fs.existsSync(FIXTURES_ROOT + v.source + '/preamble.header')) { | ||
var prehead = JSON.parse(fs.readFileSync(FIXTURES_ROOT + v.source | ||
+ '/preamble.header', 'binary')); | ||
if (!preamble) { | ||
preamble = { | ||
body: undefined, | ||
bodylen: 0, | ||
header: prehead | ||
}; | ||
} | ||
} | ||
assert.deepEqual(state.preamble, preamble, errPrefix | ||
+ 'Preamble mismatch:\nActual:' + inspect(state.preamble) | ||
+ '\nExpected: ' + inspect(preamble)); | ||
assert.equal(state.parts.length, v.nparts, | ||
errPrefix + 'Part count mismatch. Actual: ' + state.parts.length | ||
+ '. Expected: ' + v.nparts); | ||
errPrefix + 'Part count mismatch:\nActual: ' + state.parts.length | ||
+ '\nExpected: ' + v.nparts); | ||
for (var i = 0, header, body; i < v.nparts; ++i) { | ||
@@ -79,10 +140,13 @@ body = fs.readFileSync(FIXTURES_ROOT + v.source + '/part' + (i+1)); | ||
header = undefined; | ||
try { | ||
if (fs.existsSync(FIXTURES_ROOT + v.source + '/part' + (i+1) + '.header')) { | ||
header = fs.readFileSync(FIXTURES_ROOT + v.source | ||
+ '/part' + (i+1) + '.header', 'binary'); | ||
header = JSON.parse(header); | ||
} catch (err) {} | ||
} | ||
assert.deepEqual(state.parts[i].header, header, | ||
errPrefix + 'Part #' + (i+1) + ' parsed header mismatch'); | ||
errPrefix + 'Part #' + (i+1) | ||
+ ' parsed header mismatch:\nActual: ' | ||
+ inspect(state.parts[i].header) | ||
+ '\nExpected: ' + inspect(header)); | ||
} | ||
}); |
@@ -1,2 +0,4 @@ | ||
require('./test-headerparser'); | ||
require('./test-multipart'); | ||
require('fs').readdirSync(__dirname).forEach(function(f) { | ||
if (f.substr(0, 5) === 'test-') | ||
require('./' + f); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
40365
42
1152
105
0
4