UNPKG

11.2 kBJavaScriptView Raw
1'use strict';
2
3const assert = require('assert');
4const { transcode } = require('buffer');
5const { inspect } = require('util');
6
7const busboy = require('..');
8
9const active = new Map();
10
11const tests = [
12 { source: ['foo'],
13 expected: [
14 ['foo',
15 '',
16 { nameTruncated: false,
17 valueTruncated: false,
18 encoding: 'utf-8',
19 mimeType: 'text/plain' },
20 ],
21 ],
22 what: 'Unassigned value'
23 },
24 { source: ['foo=bar'],
25 expected: [
26 ['foo',
27 'bar',
28 { nameTruncated: false,
29 valueTruncated: false,
30 encoding: 'utf-8',
31 mimeType: 'text/plain' },
32 ],
33 ],
34 what: 'Assigned value'
35 },
36 { source: ['foo&bar=baz'],
37 expected: [
38 ['foo',
39 '',
40 { nameTruncated: false,
41 valueTruncated: false,
42 encoding: 'utf-8',
43 mimeType: 'text/plain' },
44 ],
45 ['bar',
46 'baz',
47 { nameTruncated: false,
48 valueTruncated: false,
49 encoding: 'utf-8',
50 mimeType: 'text/plain' },
51 ],
52 ],
53 what: 'Unassigned and assigned value'
54 },
55 { source: ['foo=bar&baz'],
56 expected: [
57 ['foo',
58 'bar',
59 { nameTruncated: false,
60 valueTruncated: false,
61 encoding: 'utf-8',
62 mimeType: 'text/plain' },
63 ],
64 ['baz',
65 '',
66 { nameTruncated: false,
67 valueTruncated: false,
68 encoding: 'utf-8',
69 mimeType: 'text/plain' },
70 ],
71 ],
72 what: 'Assigned and unassigned value'
73 },
74 { source: ['foo=bar&baz=bla'],
75 expected: [
76 ['foo',
77 'bar',
78 { nameTruncated: false,
79 valueTruncated: false,
80 encoding: 'utf-8',
81 mimeType: 'text/plain' },
82 ],
83 ['baz',
84 'bla',
85 { nameTruncated: false,
86 valueTruncated: false,
87 encoding: 'utf-8',
88 mimeType: 'text/plain' },
89 ],
90 ],
91 what: 'Two assigned values'
92 },
93 { source: ['foo&bar'],
94 expected: [
95 ['foo',
96 '',
97 { nameTruncated: false,
98 valueTruncated: false,
99 encoding: 'utf-8',
100 mimeType: 'text/plain' },
101 ],
102 ['bar',
103 '',
104 { nameTruncated: false,
105 valueTruncated: false,
106 encoding: 'utf-8',
107 mimeType: 'text/plain' },
108 ],
109 ],
110 what: 'Two unassigned values'
111 },
112 { source: ['foo&bar&'],
113 expected: [
114 ['foo',
115 '',
116 { nameTruncated: false,
117 valueTruncated: false,
118 encoding: 'utf-8',
119 mimeType: 'text/plain' },
120 ],
121 ['bar',
122 '',
123 { nameTruncated: false,
124 valueTruncated: false,
125 encoding: 'utf-8',
126 mimeType: 'text/plain' },
127 ],
128 ],
129 what: 'Two unassigned values and ampersand'
130 },
131 { source: ['foo+1=bar+baz%2Bquux'],
132 expected: [
133 ['foo 1',
134 'bar baz+quux',
135 { nameTruncated: false,
136 valueTruncated: false,
137 encoding: 'utf-8',
138 mimeType: 'text/plain' },
139 ],
140 ],
141 what: 'Assigned key and value with (plus) space'
142 },
143 { source: ['foo=bar%20baz%21'],
144 expected: [
145 ['foo',
146 'bar baz!',
147 { nameTruncated: false,
148 valueTruncated: false,
149 encoding: 'utf-8',
150 mimeType: 'text/plain' },
151 ],
152 ],
153 what: 'Assigned value with encoded bytes'
154 },
155 { source: ['foo%20bar=baz%20bla%21'],
156 expected: [
157 ['foo bar',
158 'baz bla!',
159 { nameTruncated: false,
160 valueTruncated: false,
161 encoding: 'utf-8',
162 mimeType: 'text/plain' },
163 ],
164 ],
165 what: 'Assigned value with encoded bytes #2'
166 },
167 { source: ['foo=bar%20baz%21&num=1000'],
168 expected: [
169 ['foo',
170 'bar baz!',
171 { nameTruncated: false,
172 valueTruncated: false,
173 encoding: 'utf-8',
174 mimeType: 'text/plain' },
175 ],
176 ['num',
177 '1000',
178 { nameTruncated: false,
179 valueTruncated: false,
180 encoding: 'utf-8',
181 mimeType: 'text/plain' },
182 ],
183 ],
184 what: 'Two assigned values, one with encoded bytes'
185 },
186 { source: [
187 Array.from(transcode(Buffer.from('foo'), 'utf8', 'utf16le')).map(
188 (n) => `%${n.toString(16).padStart(2, '0')}`
189 ).join(''),
190 '=',
191 Array.from(transcode(Buffer.from('😀!'), 'utf8', 'utf16le')).map(
192 (n) => `%${n.toString(16).padStart(2, '0')}`
193 ).join(''),
194 ],
195 expected: [
196 ['foo',
197 '😀!',
198 { nameTruncated: false,
199 valueTruncated: false,
200 encoding: 'UTF-16LE',
201 mimeType: 'text/plain' },
202 ],
203 ],
204 charset: 'UTF-16LE',
205 what: 'Encoded value with multi-byte charset'
206 },
207 { source: [
208 'foo=<',
209 Array.from(transcode(Buffer.from('©:^þ'), 'utf8', 'latin1')).map(
210 (n) => `%${n.toString(16).padStart(2, '0')}`
211 ).join(''),
212 ],
213 expected: [
214 ['foo',
215 '<©:^þ',
216 { nameTruncated: false,
217 valueTruncated: false,
218 encoding: 'ISO-8859-1',
219 mimeType: 'text/plain' },
220 ],
221 ],
222 charset: 'ISO-8859-1',
223 what: 'Encoded value with single-byte, ASCII-compatible, non-UTF8 charset'
224 },
225 { source: ['foo=bar&baz=bla'],
226 expected: [],
227 what: 'Limits: zero fields',
228 limits: { fields: 0 }
229 },
230 { source: ['foo=bar&baz=bla'],
231 expected: [
232 ['foo',
233 'bar',
234 { nameTruncated: false,
235 valueTruncated: false,
236 encoding: 'utf-8',
237 mimeType: 'text/plain' },
238 ],
239 ],
240 what: 'Limits: one field',
241 limits: { fields: 1 }
242 },
243 { source: ['foo=bar&baz=bla'],
244 expected: [
245 ['foo',
246 'bar',
247 { nameTruncated: false,
248 valueTruncated: false,
249 encoding: 'utf-8',
250 mimeType: 'text/plain' },
251 ],
252 ['baz',
253 'bla',
254 { nameTruncated: false,
255 valueTruncated: false,
256 encoding: 'utf-8',
257 mimeType: 'text/plain' },
258 ],
259 ],
260 what: 'Limits: field part lengths match limits',
261 limits: { fieldNameSize: 3, fieldSize: 3 }
262 },
263 { source: ['foo=bar&baz=bla'],
264 expected: [
265 ['fo',
266 'bar',
267 { nameTruncated: true,
268 valueTruncated: false,
269 encoding: 'utf-8',
270 mimeType: 'text/plain' },
271 ],
272 ['ba',
273 'bla',
274 { nameTruncated: true,
275 valueTruncated: false,
276 encoding: 'utf-8',
277 mimeType: 'text/plain' },
278 ],
279 ],
280 what: 'Limits: truncated field name',
281 limits: { fieldNameSize: 2 }
282 },
283 { source: ['foo=bar&baz=bla'],
284 expected: [
285 ['foo',
286 'ba',
287 { nameTruncated: false,
288 valueTruncated: true,
289 encoding: 'utf-8',
290 mimeType: 'text/plain' },
291 ],
292 ['baz',
293 'bl',
294 { nameTruncated: false,
295 valueTruncated: true,
296 encoding: 'utf-8',
297 mimeType: 'text/plain' },
298 ],
299 ],
300 what: 'Limits: truncated field value',
301 limits: { fieldSize: 2 }
302 },
303 { source: ['foo=bar&baz=bla'],
304 expected: [
305 ['fo',
306 'ba',
307 { nameTruncated: true,
308 valueTruncated: true,
309 encoding: 'utf-8',
310 mimeType: 'text/plain' },
311 ],
312 ['ba',
313 'bl',
314 { nameTruncated: true,
315 valueTruncated: true,
316 encoding: 'utf-8',
317 mimeType: 'text/plain' },
318 ],
319 ],
320 what: 'Limits: truncated field name and value',
321 limits: { fieldNameSize: 2, fieldSize: 2 }
322 },
323 { source: ['foo=bar&baz=bla'],
324 expected: [
325 ['fo',
326 '',
327 { nameTruncated: true,
328 valueTruncated: true,
329 encoding: 'utf-8',
330 mimeType: 'text/plain' },
331 ],
332 ['ba',
333 '',
334 { nameTruncated: true,
335 valueTruncated: true,
336 encoding: 'utf-8',
337 mimeType: 'text/plain' },
338 ],
339 ],
340 what: 'Limits: truncated field name and zero value limit',
341 limits: { fieldNameSize: 2, fieldSize: 0 }
342 },
343 { source: ['foo=bar&baz=bla'],
344 expected: [
345 ['',
346 '',
347 { nameTruncated: true,
348 valueTruncated: true,
349 encoding: 'utf-8',
350 mimeType: 'text/plain' },
351 ],
352 ['',
353 '',
354 { nameTruncated: true,
355 valueTruncated: true,
356 encoding: 'utf-8',
357 mimeType: 'text/plain' },
358 ],
359 ],
360 what: 'Limits: truncated zero field name and zero value limit',
361 limits: { fieldNameSize: 0, fieldSize: 0 }
362 },
363 { source: ['&'],
364 expected: [],
365 what: 'Ampersand'
366 },
367 { source: ['&&&&&'],
368 expected: [],
369 what: 'Many ampersands'
370 },
371 { source: ['='],
372 expected: [
373 ['',
374 '',
375 { nameTruncated: false,
376 valueTruncated: false,
377 encoding: 'utf-8',
378 mimeType: 'text/plain' },
379 ],
380 ],
381 what: 'Assigned value, empty name and value'
382 },
383 { source: [''],
384 expected: [],
385 what: 'Nothing'
386 },
387];
388
389for (const test of tests) {
390 active.set(test, 1);
391
392 const { what } = test;
393 const charset = test.charset || 'utf-8';
394 const bb = busboy({
395 limits: test.limits,
396 headers: {
397 'content-type': `application/x-www-form-urlencoded; charset=${charset}`,
398 },
399 });
400 const results = [];
401
402 bb.on('field', (key, val, info) => {
403 results.push([key, val, info]);
404 });
405
406 bb.on('file', () => {
407 throw new Error(`[${what}] Unexpected file`);
408 });
409
410 bb.on('close', () => {
411 active.delete(test);
412
413 assert.deepStrictEqual(
414 results,
415 test.expected,
416 `[${what}] Results mismatch.\n`
417 + `Parsed: ${inspect(results)}\n`
418 + `Expected: ${inspect(test.expected)}`
419 );
420 });
421
422 for (const src of test.source) {
423 const buf = (typeof src === 'string' ? Buffer.from(src, 'utf8') : src);
424 bb.write(buf);
425 }
426 bb.end();
427}
428
429// Byte-by-byte versions
430for (let test of tests) {
431 test = { ...test };
432 test.what += ' (byte-by-byte)';
433 active.set(test, 1);
434
435 const { what } = test;
436 const charset = test.charset || 'utf-8';
437 const bb = busboy({
438 limits: test.limits,
439 headers: {
440 'content-type': `application/x-www-form-urlencoded; charset="${charset}"`,
441 },
442 });
443 const results = [];
444
445 bb.on('field', (key, val, info) => {
446 results.push([key, val, info]);
447 });
448
449 bb.on('file', () => {
450 throw new Error(`[${what}] Unexpected file`);
451 });
452
453 bb.on('close', () => {
454 active.delete(test);
455
456 assert.deepStrictEqual(
457 results,
458 test.expected,
459 `[${what}] Results mismatch.\n`
460 + `Parsed: ${inspect(results)}\n`
461 + `Expected: ${inspect(test.expected)}`
462 );
463 });
464
465 for (const src of test.source) {
466 const buf = (typeof src === 'string' ? Buffer.from(src, 'utf8') : src);
467 for (let i = 0; i < buf.length; ++i)
468 bb.write(buf.slice(i, i + 1));
469 }
470 bb.end();
471}
472
473{
474 let exception = false;
475 process.once('uncaughtException', (ex) => {
476 exception = true;
477 throw ex;
478 });
479 process.on('exit', () => {
480 if (exception || active.size === 0)
481 return;
482 process.exitCode = 1;
483 console.error('==========================');
484 console.error(`${active.size} test(s) did not finish:`);
485 console.error('==========================');
486 console.error(Array.from(active.keys()).map((v) => v.what).join('\n'));
487 });
488}