1'use strict'; 2 3function createMultipartBuffers(boundary, sizes) { 4 const bufs = []; 5 for (let i = 0; i < sizes.length; ++i) { 6 const mb = sizes[i] * 1024 * 1024; 7 bufs.push(Buffer.from([ 8 `--${boundary}`, 9 `content-disposition: form-data; name="file${i + 1}"; ` 10 + `filename="random${i + 1}.bin"`, 11 'content-type: application/octet-stream', 12 '', 13 '0'.repeat(mb), 14 '', 15 ].join('\r\n'))); 16 } 17 bufs.push(Buffer.from([ 18 `--${boundary}--`, 19 '', 20 ].join('\r\n'))); 21 return bufs; 22} 23 24const boundary = '-----------------------------168072824752491622650073'; 25const buffers = createMultipartBuffers(boundary, (new Array(100)).fill(1)); 26const calls = { 27 partBegin: 0, 28 headerField: 0, 29 headerValue: 0, 30 headerEnd: 0, 31 headersEnd: 0, 32 partData: 0, 33 partEnd: 0, 34 end: 0, 35}; 36 37const moduleName = process.argv[2]; 38switch (moduleName) { 39 case 'busboy': { 40 const busboy = require('busboy'); 41 42 const parser = busboy({ 43 limits: { 44 fieldSizeLimit: Infinity, 45 }, 46 headers: { 47 'content-type': `multipart/form-data; boundary=${boundary}`, 48 }, 49 }); 50 parser.on('file', (name, stream, info) => { 51 ++calls.partBegin; 52 stream.on('data', (chunk) => { 53 ++calls.partData; 54 }).on('end', () => { 55 ++calls.partEnd; 56 }); 57 }).on('close', () => { 58 ++calls.end; 59 console.timeEnd(moduleName); 60 }); 61 62 console.time(moduleName); 63 for (const buf of buffers) 64 parser.write(buf); 65 break; 66 } 67 68 case 'formidable': { 69 const { MultipartParser } = require('formidable'); 70 71 const parser = new MultipartParser(); 72 parser.initWithBoundary(boundary); 73 parser.on('data', ({ name }) => { 74 ++calls[name]; 75 if (name === 'end') 76 console.timeEnd(moduleName); 77 }); 78 79 console.time(moduleName); 80 for (const buf of buffers) 81 parser.write(buf); 82 83 break; 84 } 85 86 case 'multiparty': { 87 const { Readable } = require('stream'); 88 89 const { Form } = require('multiparty'); 90 91 const form = new Form({ 92 maxFieldsSize: Infinity, 93 maxFields: Infinity, 94 maxFilesSize: Infinity, 95 autoFields: false, 96 autoFiles: false, 97 }); 98 99 const req = new Readable({ read: () => {} }); 100 req.headers = { 101 'content-type': `multipart/form-data; boundary=${boundary}`, 102 }; 103 104 function hijack(name, fn) { 105 const oldFn = form[name]; 106 form[name] = function() { 107 fn(); 108 return oldFn.apply(this, arguments); 109 }; 110 } 111 112 hijack('onParseHeaderField', () => { 113 ++calls.headerField; 114 }); 115 hijack('onParseHeaderValue', () => { 116 ++calls.headerValue; 117 }); 118 hijack('onParsePartBegin', () => { 119 ++calls.partBegin; 120 }); 121 hijack('onParsePartData', () => { 122 ++calls.partData; 123 }); 124 hijack('onParsePartEnd', () => { 125 ++calls.partEnd; 126 }); 127 128 form.on('close', () => { 129 ++calls.end; 130 console.timeEnd(moduleName); 131 }).on('part', (p) => p.resume()); 132 133 console.time(moduleName); 134 form.parse(req); 135 for (const buf of buffers) 136 req.push(buf); 137 req.push(null); 138 139 break; 140 } 141 142 default: 143 if (moduleName === undefined) 144 console.error('Missing parser module name'); 145 else 146 console.error(`Invalid parser module name: ${moduleName}`); 147 process.exit(1); 148} 149