async-multipart-iterator
Version:
Iterate over the parts of a multipart document
159 lines (140 loc) • 4.66 kB
JavaScript
const assert = require('assert');
const {multipartIterator} = require('../async-multipart-iterator');
const boundary = 'separator'
function toCRLF(s) {
return s.replace(/\n/g, "\r\n")
}
const baseBodies = [
'part 1',
`
part 2's data
and more!
Part 2 includes a blank line before and after
`,
'part 3 has no header!'
];
const baseHeaders = [
[
'key1: value1',
'key2: value2'
],
[
'keya: something',
'keyb: something else'
],
[]
];
let baseDoc = toCRLF(`
--separator
key1: value1
key2: value2
part 1
--separator
keya: something
keyb: something else
part 2's data
and more!
Part 2 includes a blank line before and after
--separator
part 3 has no header!
--separator--
`);
const chunkedDoc = baseDoc.replace(/--separator/g,'|--separator').split('|')
const expectedHeaders = baseHeaders;
const expectedBodies = baseBodies.map(s => s.replace(/\n/g, "\r\n"))
async function testBaseDocument(input,options) {
let n = 1;
const headers = [];
const bodies = [];
try {
for await (let [header, iterator] of multipartIterator((options && options.substituteBoundary) || boundary, input)) {
headers.push(header);
const accum = [];
for await (let chunk of iterator) {
accum.push(chunk.toString());
}
bodies.push(accum.join(''));
}
assert.deepStrictEqual(headers, expectedHeaders);
assert.deepStrictEqual(bodies, expectedBodies);
}
catch(e) {
if (options && options.substituteBoundary) {
assert(e.message == 'Boundary not found in document')
}
else {
throw e;
}
}
}
describe('Multipart iterator', async function () {
it('should throw an error if the boundary was not found in the document', async function () {
await testBaseDocument(baseDoc, {substituteBoundary: boundary+'x'});
});
it('should parse, given a single input chunk', async function () {
await testBaseDocument(baseDoc);
});
it('should parse the basic document, broken into even several chunks', async function () {
const chunks = [];
const chunksize = 32;
for (let i = 0; i < baseDoc.length; i+= chunksize) {
chunks.push(baseDoc.substr(i,chunksize))
}
await testBaseDocument(chunks);
});
it('should parse the basic document, where there is a chunk break just before the "--" of each boundary', async function () {
const testcase = chunkedDoc;
await testBaseDocument(testcase);
});
for (let i= 0; i<15; i++) {
it(`should parse the basic document, where the chunk split is ${i} chars into the boundary`, async function () {
const t = Array.from(chunkedDoc);
if (i != 0) {
t[0] = t[0]+t[1].substr(0,i);
for (let j= 1; j<t.length-1; j++) {
t[j] = t[j].substr(i) + t[j+1].substr(0,i);
}
t[t.length-1] = t[t.length-1].substr(i);
}
await testBaseDocument(t);
});
}
const minusBase = Array.from(chunkedDoc);
const mbfirst = minusBase.shift();
minusBase[0] = mbfirst + minusBase[0];
for (let i= 1; i<15; i++) {
it(`should parse the basic document, where the chunk split is ${i} chars before the boundary`, async function () {
const t = Array.from(minusBase);
let toNext = t[0].substr(-i);
t[0] = t[0].substr(0,t[0].length-i);
for (let j= 1; j<t.length-1; j++) {
let toNext2 = t[j].substr(-i);
t[j] = toNext + t[j].substr(0,t[j].length-i);
toNext = toNext2;
}
t[t.length-1] = toNext + t[t.length-1]
await testBaseDocument(t);
});
}
it('should parse the basic document, where there is a chunk break between the "--" of a boundary', async function () {
const testcase = baseDoc.replace(/--separator/g, '-|-separator').split('|');
await testBaseDocument(testcase);
});
it('should parse the basic document, broken into 1 byte chunks', async function () {
// Note: a subset of this test would be testing if a boundary is split across two chunks. This test does
// check that possibility plus other "what if (feature) straddles two (or more) chunks" scenarios.
const chunks = [];
const chunksize = 1; // await never satisfied with this small of a chunk! (or may go into a tight loop)
for (let i = 0; i < baseDoc.length; i+= chunksize) {
chunks.push(baseDoc.substr(i,chunksize))
}
await testBaseDocument(chunks);
});
it('should allow accessing the raw, undecoded, buffer lines of the headers', async function () {
for await (let [, , rawHeader] of multipartIterator(boundary, baseDoc)) {
assert(Array.isArray(rawHeader));
assert(Buffer.isBuffer(rawHeader[0]));
return;
};
});
});