123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- 'use strict';
- const assert = require('assert');
- const { transcode } = require('buffer');
- const { inspect } = require('util');
- const busboy = require('..');
- const active = new Map();
- const tests = [
- { source: ['foo'],
- expected: [
- ['foo',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Unassigned value'
- },
- { source: ['foo=bar'],
- expected: [
- ['foo',
- 'bar',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Assigned value'
- },
- { source: ['foo&bar=baz'],
- expected: [
- ['foo',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['bar',
- 'baz',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Unassigned and assigned value'
- },
- { source: ['foo=bar&baz'],
- expected: [
- ['foo',
- 'bar',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['baz',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Assigned and unassigned value'
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['foo',
- 'bar',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['baz',
- 'bla',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Two assigned values'
- },
- { source: ['foo&bar'],
- expected: [
- ['foo',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['bar',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Two unassigned values'
- },
- { source: ['foo&bar&'],
- expected: [
- ['foo',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['bar',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Two unassigned values and ampersand'
- },
- { source: ['foo+1=bar+baz%2Bquux'],
- expected: [
- ['foo 1',
- 'bar baz+quux',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Assigned key and value with (plus) space'
- },
- { source: ['foo=bar%20baz%21'],
- expected: [
- ['foo',
- 'bar baz!',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Assigned value with encoded bytes'
- },
- { source: ['foo%20bar=baz%20bla%21'],
- expected: [
- ['foo bar',
- 'baz bla!',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Assigned value with encoded bytes #2'
- },
- { source: ['foo=bar%20baz%21&num=1000'],
- expected: [
- ['foo',
- 'bar baz!',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['num',
- '1000',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Two assigned values, one with encoded bytes'
- },
- { source: [
- Array.from(transcode(Buffer.from('foo'), 'utf8', 'utf16le')).map(
- (n) => `%${n.toString(16).padStart(2, '0')}`
- ).join(''),
- '=',
- Array.from(transcode(Buffer.from('😀!'), 'utf8', 'utf16le')).map(
- (n) => `%${n.toString(16).padStart(2, '0')}`
- ).join(''),
- ],
- expected: [
- ['foo',
- '😀!',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'UTF-16LE',
- mimeType: 'text/plain' },
- ],
- ],
- charset: 'UTF-16LE',
- what: 'Encoded value with multi-byte charset'
- },
- { source: [
- 'foo=<',
- Array.from(transcode(Buffer.from('©:^þ'), 'utf8', 'latin1')).map(
- (n) => `%${n.toString(16).padStart(2, '0')}`
- ).join(''),
- ],
- expected: [
- ['foo',
- '<©:^þ',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'ISO-8859-1',
- mimeType: 'text/plain' },
- ],
- ],
- charset: 'ISO-8859-1',
- what: 'Encoded value with single-byte, ASCII-compatible, non-UTF8 charset'
- },
- { source: ['foo=bar&baz=bla'],
- expected: [],
- what: 'Limits: zero fields',
- limits: { fields: 0 }
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['foo',
- 'bar',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Limits: one field',
- limits: { fields: 1 }
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['foo',
- 'bar',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['baz',
- 'bla',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Limits: field part lengths match limits',
- limits: { fieldNameSize: 3, fieldSize: 3 }
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['fo',
- 'bar',
- { nameTruncated: true,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['ba',
- 'bla',
- { nameTruncated: true,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Limits: truncated field name',
- limits: { fieldNameSize: 2 }
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['foo',
- 'ba',
- { nameTruncated: false,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['baz',
- 'bl',
- { nameTruncated: false,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Limits: truncated field value',
- limits: { fieldSize: 2 }
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['fo',
- 'ba',
- { nameTruncated: true,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['ba',
- 'bl',
- { nameTruncated: true,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Limits: truncated field name and value',
- limits: { fieldNameSize: 2, fieldSize: 2 }
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['fo',
- '',
- { nameTruncated: true,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['ba',
- '',
- { nameTruncated: true,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Limits: truncated field name and zero value limit',
- limits: { fieldNameSize: 2, fieldSize: 0 }
- },
- { source: ['foo=bar&baz=bla'],
- expected: [
- ['',
- '',
- { nameTruncated: true,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ['',
- '',
- { nameTruncated: true,
- valueTruncated: true,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Limits: truncated zero field name and zero value limit',
- limits: { fieldNameSize: 0, fieldSize: 0 }
- },
- { source: ['&'],
- expected: [],
- what: 'Ampersand'
- },
- { source: ['&&&&&'],
- expected: [],
- what: 'Many ampersands'
- },
- { source: ['='],
- expected: [
- ['',
- '',
- { nameTruncated: false,
- valueTruncated: false,
- encoding: 'utf-8',
- mimeType: 'text/plain' },
- ],
- ],
- what: 'Assigned value, empty name and value'
- },
- { source: [''],
- expected: [],
- what: 'Nothing'
- },
- ];
- for (const test of tests) {
- active.set(test, 1);
- const { what } = test;
- const charset = test.charset || 'utf-8';
- const bb = busboy({
- limits: test.limits,
- headers: {
- 'content-type': `application/x-www-form-urlencoded; charset=${charset}`,
- },
- });
- const results = [];
- bb.on('field', (key, val, info) => {
- results.push([key, val, info]);
- });
- bb.on('file', () => {
- throw new Error(`[${what}] Unexpected file`);
- });
- bb.on('close', () => {
- active.delete(test);
- assert.deepStrictEqual(
- results,
- test.expected,
- `[${what}] Results mismatch.\n`
- + `Parsed: ${inspect(results)}\n`
- + `Expected: ${inspect(test.expected)}`
- );
- });
- for (const src of test.source) {
- const buf = (typeof src === 'string' ? Buffer.from(src, 'utf8') : src);
- bb.write(buf);
- }
- bb.end();
- }
- // Byte-by-byte versions
- for (let test of tests) {
- test = { ...test };
- test.what += ' (byte-by-byte)';
- active.set(test, 1);
- const { what } = test;
- const charset = test.charset || 'utf-8';
- const bb = busboy({
- limits: test.limits,
- headers: {
- 'content-type': `application/x-www-form-urlencoded; charset="${charset}"`,
- },
- });
- const results = [];
- bb.on('field', (key, val, info) => {
- results.push([key, val, info]);
- });
- bb.on('file', () => {
- throw new Error(`[${what}] Unexpected file`);
- });
- bb.on('close', () => {
- active.delete(test);
- assert.deepStrictEqual(
- results,
- test.expected,
- `[${what}] Results mismatch.\n`
- + `Parsed: ${inspect(results)}\n`
- + `Expected: ${inspect(test.expected)}`
- );
- });
- for (const src of test.source) {
- const buf = (typeof src === 'string' ? Buffer.from(src, 'utf8') : src);
- for (let i = 0; i < buf.length; ++i)
- bb.write(buf.slice(i, i + 1));
- }
- bb.end();
- }
- {
- let exception = false;
- process.once('uncaughtException', (ex) => {
- exception = true;
- throw ex;
- });
- process.on('exit', () => {
- if (exception || active.size === 0)
- return;
- process.exitCode = 1;
- console.error('==========================');
- console.error(`${active.size} test(s) did not finish:`);
- console.error('==========================');
- console.error(Array.from(active.keys()).map((v) => v.what).join('\n'));
- });
- }
|