'use strict'; function createMultipartBuffers(boundary, sizes) { const bufs = []; for (let i = 0; i < sizes.length; ++i) { const mb = sizes[i] * 1024 * 1024; bufs.push(Buffer.from([ `--${boundary}`, `content-disposition: form-data; name="file${i + 1}"; ` + `filename="random${i + 1}.bin"`, 'content-type: application/octet-stream', '', '0'.repeat(mb), '', ].join('\r\n'))); } bufs.push(Buffer.from([ `--${boundary}--`, '', ].join('\r\n'))); return bufs; } const boundary = '-----------------------------168072824752491622650073'; const buffers = createMultipartBuffers(boundary, [ 10, 10, 10, 20, 50, ]); const calls = { partBegin: 0, headerField: 0, headerValue: 0, headerEnd: 0, headersEnd: 0, partData: 0, partEnd: 0, end: 0, }; const moduleName = process.argv[2]; switch (moduleName) { case 'busboy': { const busboy = require('busboy'); const parser = busboy({ limits: { fieldSizeLimit: Infinity, }, headers: { 'content-type': `multipart/form-data; boundary=${boundary}`, }, }); parser.on('file', (name, stream, info) => { ++calls.partBegin; stream.on('data', (chunk) => { ++calls.partData; }).on('end', () => { ++calls.partEnd; }); }).on('close', () => { ++calls.end; console.timeEnd(moduleName); }); console.time(moduleName); for (const buf of buffers) parser.write(buf); break; } case 'formidable': { const { MultipartParser } = require('formidable'); const parser = new MultipartParser(); parser.initWithBoundary(boundary); parser.on('data', ({ name }) => { ++calls[name]; if (name === 'end') console.timeEnd(moduleName); }); console.time(moduleName); for (const buf of buffers) parser.write(buf); break; } case 'multiparty': { const { Readable } = require('stream'); const { Form } = require('multiparty'); const form = new Form({ maxFieldsSize: Infinity, maxFields: Infinity, maxFilesSize: Infinity, autoFields: false, autoFiles: false, }); const req = new Readable({ read: () => {} }); req.headers = { 'content-type': `multipart/form-data; boundary=${boundary}`, }; function hijack(name, fn) { const oldFn = form[name]; form[name] = function() { fn(); return oldFn.apply(this, arguments); }; } hijack('onParseHeaderField', () => { ++calls.headerField; }); hijack('onParseHeaderValue', () => { ++calls.headerValue; }); hijack('onParsePartBegin', () => { ++calls.partBegin; }); hijack('onParsePartData', () => { ++calls.partData; }); hijack('onParsePartEnd', () => { ++calls.partEnd; }); form.on('close', () => { ++calls.end; console.timeEnd(moduleName); }).on('part', (p) => p.resume()); console.time(moduleName); form.parse(req); for (const buf of buffers) req.push(buf); req.push(null); break; } default: if (moduleName === undefined) console.error('Missing parser module name'); else console.error(`Invalid parser module name: ${moduleName}`); process.exit(1); }