diff options
Diffstat (limited to '')
6 files changed, 817 insertions, 0 deletions
diff --git a/fastify-busboy/bench/dicer/dicer-bench-multipart-parser.js b/fastify-busboy/bench/dicer/dicer-bench-multipart-parser.js new file mode 100644 index 0000000..d24f599 --- /dev/null +++ b/fastify-busboy/bench/dicer/dicer-bench-multipart-parser.js @@ -0,0 +1,60 @@ +'use strict' + +const Dicer = require('../../deps/dicer/lib/Dicer') + +function createMultipartBuffer(boundary, size) { + const head = + '--' + boundary + '\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--' + boundary + '--\r\n' + , buffer = Buffer.allocUnsafe(size); + + buffer.write(head, 0, 'ascii'); + buffer.write(tail, buffer.length - tail.length, 'ascii'); + return buffer; +} + +for (var i = 0, il = 10; i < il; i++) { // eslint-disable-line no-var + const boundary = '-----------------------------168072824752491622650073', + d = new Dicer({ boundary: boundary }), + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { + partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + + d.on('part', function (p) { + callbacks.partBegin++; + p.on('header', function (header) { + /*for (var h in header) + console.log('Part header: k: ' + inspect(h) + ', v: ' + inspect(header[h]));*/ + }); + p.on('data', function (data) { + callbacks.partData++; + //console.log('Part data: ' + inspect(data.toString())); + }); + p.on('end', function () { + //console.log('End of part\n'); + callbacks.partEnd++; + }); + }); + d.on('end', function () { + //console.log('End of parts'); + callbacks.end++; + }); + + const start = +new Date(); + d.write(buffer); + const duration = +new Date - start; + const mbPerSec = (mb / (duration / 1000)).toFixed(2); + + console.log(mbPerSec + ' mb/sec'); +} diff --git a/fastify-busboy/bench/dicer/formidable-bench-multipart-parser.js b/fastify-busboy/bench/dicer/formidable-bench-multipart-parser.js new file mode 100644 index 0000000..0470771 --- /dev/null +++ b/fastify-busboy/bench/dicer/formidable-bench-multipart-parser.js @@ -0,0 +1,71 @@ +'use strict' + +require('../node_modules/formidable/test/common'); +var multipartParser = require('../node_modules/formidable/lib/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + parser = new MultipartParser(), + boundary = '-----------------------------168072824752491622650073', + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + +parser.initWithBoundary(boundary); +parser.onHeaderField = function() { + callbacks.headerField++; +}; + +parser.onHeaderValue = function() { + callbacks.headerValue++; +}; + +parser.onPartBegin = function() { + callbacks.partBegin++; +}; + +parser.onPartData = function() { + callbacks.partData++; +}; + +parser.onPartEnd = function() { + callbacks.partEnd++; +}; + +parser.onEnd = function() { + callbacks.end++; +}; + +var start = +new Date(), + nparsed = parser.write(buffer), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = Buffer.allocUnsafe(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); diff --git a/fastify-busboy/bench/dicer/multipartser-bench-multipart-parser.js b/fastify-busboy/bench/dicer/multipartser-bench-multipart-parser.js new file mode 100644 index 0000000..40ca00b --- /dev/null +++ b/fastify-busboy/bench/dicer/multipartser-bench-multipart-parser.js @@ -0,0 +1,57 @@ +'use strict' + +var multipartser = require('multipartser'), + boundary = '-----------------------------168072824752491622650073', + parser = multipartser(), + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + +parser.boundary( boundary ); + +parser.on( 'part', function ( part ) { +}); + +parser.on( 'end', function () { + //console.log( 'completed parsing' ); +}); + +parser.on( 'error', function ( error ) { + console.error( error ); +}); + +var start = +new Date(), + nparsed = parser.data(buffer), + nend = parser.end(), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = Buffer.allocUnsafe(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); diff --git a/fastify-busboy/bench/dicer/multiparty-bench-multipart-parser.js b/fastify-busboy/bench/dicer/multiparty-bench-multipart-parser.js new file mode 100644 index 0000000..ab79ec0 --- /dev/null +++ b/fastify-busboy/bench/dicer/multiparty-bench-multipart-parser.js @@ -0,0 +1,78 @@ +'use strict' + +var assert = require('node:assert'), + Form = require('multiparty').Form, + boundary = '-----------------------------168072824752491622650073', + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + +var form = new Form({ boundary: boundary }); + +hijack('onParseHeaderField', function() { + callbacks.headerField++; +}); + +hijack('onParseHeaderValue', function() { + callbacks.headerValue++; +}); + +hijack('onParsePartBegin', function() { + callbacks.partBegin++; +}); + +hijack('onParsePartData', function() { + callbacks.partData++; +}); + +hijack('onParsePartEnd', function() { + callbacks.partEnd++; +}); + +form.on('finish', function() { + callbacks.end++; +}); + +var start = new Date(); +form.write(buffer, function(err) { + var duration = new Date() - start; + assert.ifError(err); + var mbPerSec = (mb / (duration / 1000)).toFixed(2); + console.log(mbPerSec+' mb/sec'); +}); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = Buffer.allocUnsafe(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); + +function hijack(name, fn) { + var oldFn = form[name]; + form[name] = function() { + fn(); + return oldFn.apply(this, arguments); + }; +} diff --git a/fastify-busboy/bench/dicer/parted-bench-multipart-parser.js b/fastify-busboy/bench/dicer/parted-bench-multipart-parser.js new file mode 100644 index 0000000..e0a4670 --- /dev/null +++ b/fastify-busboy/bench/dicer/parted-bench-multipart-parser.js @@ -0,0 +1,65 @@ +'use strict' + +// A special, edited version of the multipart parser from parted is needed here +// because otherwise it attempts to do some things above and beyond just parsing +// -- like saving to disk and whatnot + +var assert = require('node:assert'); +var Parser = require('./parted-multipart'), + boundary = '-----------------------------168072824752491622650073', + parser = new Parser('boundary=' + boundary), + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + +parser.on('header', function() { + //callbacks.headerField++; +}); + +parser.on('data', function() { + //callbacks.partBegin++; +}); + +parser.on('part', function() { + +}); + +parser.on('end', function() { + //callbacks.end++; +}); + +var start = +new Date(), + nparsed = parser.write(buffer), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +//assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = Buffer.allocUnsafe(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + /*for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + }*/ +}); diff --git a/fastify-busboy/bench/dicer/parted-multipart.js b/fastify-busboy/bench/dicer/parted-multipart.js new file mode 100644 index 0000000..f214ff4 --- /dev/null +++ b/fastify-busboy/bench/dicer/parted-multipart.js @@ -0,0 +1,486 @@ +'use strict' + +/** + * Parted (https://github.com/chjj/parted) + * A streaming multipart state parser. + * Copyright (c) 2011, Christopher Jeffrey. (MIT Licensed) + */ + +var fs = require('node:fs') + , path = require('node:path') + , EventEmitter = require('node:events').EventEmitter + , StringDecoder = require('node:string_decoder').StringDecoder + , set = require('qs').set + , each = Array.prototype.forEach; + +/** + * Character Constants + */ + +var DASH = '-'.charCodeAt(0) + , CR = '\r'.charCodeAt(0) + , LF = '\n'.charCodeAt(0) + , COLON = ':'.charCodeAt(0) + , SPACE = ' '.charCodeAt(0); + +/** + * Parser + */ + +var Parser = function(type, options) { + if (!(this instanceof Parser)) { + return new Parser(type, options); + } + + EventEmitter.call(this); + + this.writable = true; + this.readable = true; + + this.options = options || {}; + + var key = grab(type, 'boundary'); + if (!key) { + return this._error('No boundary key found.'); + } + + this.key = Buffer.allocUnsafe('\r\n--' + key); + + this._key = {}; + each.call(this.key, function(ch) { + this._key[ch] = true; + }, this); + + this.state = 'start'; + this.pending = 0; + this.written = 0; + this.writtenDisk = 0; + this.buff = Buffer.allocUnsafe(200); + + this.preamble = true; + this.epilogue = false; + + this._reset(); +}; + +Parser.prototype.__proto__ = EventEmitter.prototype; + +/** + * Parsing + */ + +Parser.prototype.write = function(data) { + if (!this.writable + || this.epilogue) return; + + try { + this._parse(data); + } catch (e) { + this._error(e); + } + + return true; +}; + +Parser.prototype.end = function(data) { + if (!this.writable) return; + + if (data) this.write(data); + + if (!this.epilogue) { + return this._error('Message underflow.'); + } + + return true; +}; + +Parser.prototype._parse = function(data) { + var i = 0 + , len = data.length + , buff = this.buff + , key = this.key + , ch + , val + , j; + + for (; i < len; i++) { + if (this.pos >= 200) { + return this._error('Potential buffer overflow.'); + } + + ch = data[i]; + + switch (this.state) { + case 'start': + switch (ch) { + case DASH: + this.pos = 3; + this.state = 'key'; + break; + default: + break; + } + break; + case 'key': + if (this.pos === key.length) { + this.state = 'key_end'; + i--; + } else if (ch !== key[this.pos]) { + if (this.preamble) { + this.state = 'start'; + i--; + } else { + this.state = 'body'; + val = this.pos - i; + if (val > 0) { + this._write(key.slice(0, val)); + } + i--; + } + } else { + this.pos++; + } + break; + case 'key_end': + switch (ch) { + case CR: + this.state = 'key_line_end'; + break; + case DASH: + this.state = 'key_dash_end'; + break; + default: + return this._error('Expected CR or DASH.'); + } + break; + case 'key_line_end': + switch (ch) { + case LF: + if (this.preamble) { + this.preamble = false; + } else { + this._finish(); + } + this.state = 'header_name'; + this.pos = 0; + break; + default: + return this._error('Expected CR.'); + } + break; + case 'key_dash_end': + switch (ch) { + case DASH: + this.epilogue = true; + this._finish(); + return; + default: + return this._error('Expected DASH.'); + } + case 'header_name': + switch (ch) { + case COLON: + this.header = buff.toString('ascii', 0, this.pos); + this.pos = 0; + this.state = 'header_val'; + break; + default: + buff[this.pos++] = ch | 32; + break; + } + break; + case 'header_val': + switch (ch) { + case CR: + this.state = 'header_val_end'; + break; + case SPACE: + if (this.pos === 0) { + break; + } + // FALL-THROUGH + default: + buff[this.pos++] = ch; + break; + } + break; + case 'header_val_end': + switch (ch) { + case LF: + val = buff.toString('ascii', 0, this.pos); + this._header(this.header, val); + this.pos = 0; + this.state = 'header_end'; + break; + default: + return this._error('Expected LF.'); + } + break; + case 'header_end': + switch (ch) { + case CR: + this.state = 'head_end'; + break; + default: + this.state = 'header_name'; + i--; + break; + } + break; + case 'head_end': + switch (ch) { + case LF: + this.state = 'body'; + i++; + if (i >= len) return; + data = data.slice(i); + i = -1; + len = data.length; + break; + default: + return this._error('Expected LF.'); + } + break; + case 'body': + switch (ch) { + case CR: + if (i > 0) { + this._write(data.slice(0, i)); + } + this.pos = 1; + this.state = 'key'; + data = data.slice(i); + i = 0; + len = data.length; + break; + default: + // boyer-moore-like algorithm + // at felixge's suggestion + while ((j = i + key.length - 1) < len) { + if (this._key[data[j]]) break; + i = j; + } + break; + } + break; + } + } + + if (this.state === 'body') { + this._write(data); + } +}; + +Parser.prototype._header = function(name, val) { + /*if (name === 'content-disposition') { + this.field = grab(val, 'name'); + this.file = grab(val, 'filename'); + + if (this.file) { + this.data = stream(this.file, this.options.path); + } else { + this.decode = new StringDecoder('utf8'); + this.data = ''; + } + }*/ + + return this.emit('header', name, val); +}; + +Parser.prototype._write = function(data) { + /*if (this.data == null) { + return this._error('No disposition.'); + } + + if (this.file) { + this.data.write(data); + this.writtenDisk += data.length; + } else { + this.data += this.decode.write(data); + this.written += data.length; + }*/ + + this.emit('data', data); +}; + +Parser.prototype._reset = function() { + this.pos = 0; + this.decode = null; + this.field = null; + this.data = null; + this.file = null; + this.header = null; +}; + +Parser.prototype._error = function(err) { + this.destroy(); + this.emit('error', typeof err === 'string' + ? new Error(err) + : err); +}; + +Parser.prototype.destroy = function(err) { + this.writable = false; + this.readable = false; + this._reset(); +}; + +Parser.prototype._finish = function() { + var self = this + , field = this.field + , data = this.data + , file = this.file + , part; + + this.pending++; + + this._reset(); + + if (data && data.path) { + part = data.path; + data.end(next); + } else { + part = data; + next(); + } + + function next() { + if (!self.readable) return; + + self.pending--; + + self.emit('part', field, part); + + if (data && data.path) { + self.emit('file', field, part, file); + } + + if (self.epilogue && !self.pending) { + self.emit('end'); + self.destroy(); + } + } +}; + +/** + * Uploads + */ + +Parser.root = process.platform === 'win32' + ? 'C:/Temp' + : '/tmp'; + +/** + * Middleware + */ + +Parser.middleware = function(options) { + options = options || {}; + return function(req, res, next) { + if (options.ensureBody) { + req.body = {}; + } + + if (req.method === 'GET' + || req.method === 'HEAD' + || req._multipart) return next(); + + req._multipart = true; + + var type = req.headers['content-type']; + + if (type) type = type.split(';', 1)[0].trim().toLowerCase(); + + if (type === 'multipart/form-data') { + Parser.handle(req, res, next, options); + } else { + next(); + } + }; +}; + +/** + * Handler + */ + +Parser.handle = function(req, res, next, options) { + var parser = new Parser(req.headers['content-type'], options) + , diskLimit = options.diskLimit + , limit = options.limit + , parts = {} + , files = {}; + + parser.on('error', function(err) { + req.destroy(); + next(err); + }); + + parser.on('part', function(field, part) { + set(parts, field, part); + }); + + parser.on('file', function(field, path, name) { + set(files, field, { + path: path, + name: name, + toString: function() { + return path; + } + }); + }); + + parser.on('data', function() { + if (this.writtenDisk > diskLimit || this.written > limit) { + this.emit('error', new Error('Overflow.')); + this.destroy(); + } + }); + + parser.on('end', next); + + req.body = parts; + req.files = files; + req.pipe(parser); +}; + +/** + * Helpers + */ + +var isWindows = process.platform === 'win32'; + +var stream = function(name, dir) { + var ext = path.extname(name) || '' + , name = path.basename(name, ext) || '' + , dir = dir || Parser.root + , tag; + + tag = Math.random().toString(36).substring(2); + + name = name.substring(0, 200) + '.' + tag; + name = path.join(dir, name) + ext.substring(0, 6); + name = name.replace(/\0/g, ''); + + if (isWindows) { + name = name.replace(/[:*<>|"?]/g, ''); + } + + return fs.createWriteStream(name); +}; + +var grab = function(str, name) { + if (!str) return; + + var rx = new RegExp('\\b' + name + '\\s*=\\s*("[^"]+"|\'[^\']+\'|[^;,]+)', 'i') + , cap = rx.exec(str); + + if (cap) { + return cap[1].trim().replace(/^['"]|['"]$/g, ''); + } +}; + +/** + * Expose + */ + +module.exports = Parser; |