• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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