Initial commit
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="_method"
|
||||
|
||||
put
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[blog]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[public_email]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[interests]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[bio]"
|
||||
|
||||
hello
|
||||
|
||||
"quote"
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="commit"
|
||||
|
||||
Save
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="media"; filename=""
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
put
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"_method\""]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[blog]\""]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[public_email]\""]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[interests]\""]}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
hello
|
||||
|
||||
"quote"
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[bio]\""]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Save
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"commit\""]}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{"content-disposition": ["form-data; name=\"media\"; filename=\"\""],
|
||||
"content-type": ["application/octet-stream"]}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="_method"
|
||||
|
||||
put
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[blog]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[public_email]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[interests]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[bio]"
|
||||
|
||||
hello
|
||||
|
||||
"quote"
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="media"; filename=""
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="commit"
|
||||
|
||||
Save
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR--
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="_method"
|
||||
|
||||
put
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[blog]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[public_email]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[interests]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[bio]"
|
||||
|
||||
hello
|
||||
|
||||
"quote"
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="media"; filename=""
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="commit"
|
||||
|
||||
Save
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR--
|
||||
+1
@@ -0,0 +1 @@
|
||||
Preamble terminated early due to unexpected end of multipart data
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="_method"
|
||||
|
||||
put
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[blog]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[public_email]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[interests]"
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="profile[bio]"
|
||||
|
||||
hello
|
||||
|
||||
"quote"
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="media"; filename=""
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
||||
Content-Disposition: form-data; name="commit"
|
||||
|
||||
Save
|
||||
------WebKitFormBoundaryWLHCs9qmcJJoyjKR--
|
||||
+1
@@ -0,0 +1 @@
|
||||
put
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"_method\""]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[blog]\""]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[public_email]\""]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[interests]\""]}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
hello
|
||||
|
||||
"quote"
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"profile[bio]\""]}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{"content-disposition": ["form-data; name=\"media\"; filename=\"\""],
|
||||
"content-type": ["application/octet-stream"]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Save
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"commit\""]}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
User-Agent: foo bar baz
|
||||
Content-Type: multipart/form-data; boundary=AaB03x
|
||||
|
||||
--AaB03x
|
||||
Content-Disposition: form-data; name="foo"
|
||||
|
||||
bar
|
||||
--AaB03x
|
||||
Content-Disposition: form-data; name="files"
|
||||
Content-Type: multipart/mixed, boundary=BbC04y
|
||||
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="file.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
contents
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="flowers.jpg"
|
||||
Content-Type: image/jpeg
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
contents
|
||||
--BbC04y--
|
||||
--AaB03x--
|
||||
+1
@@ -0,0 +1 @@
|
||||
bar
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"foo\""]}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="file.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
contents
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="flowers.jpg"
|
||||
Content-Type: image/jpeg
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
contents
|
||||
--BbC04y--
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{"content-disposition": ["form-data; name=\"files\""],
|
||||
"content-type": ["multipart/mixed, boundary=BbC04y"]}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{"user-agent": ["foo bar baz"],
|
||||
"content-type": ["multipart/form-data; boundary=AaB03x"]}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
--AaB03x
|
||||
Content-Disposition: form-data; name="foo"
|
||||
|
||||
bar
|
||||
--AaB03x
|
||||
Content-Disposition: form-data; name="files"
|
||||
Content-Type: multipart/mixed, boundary=BbC04y
|
||||
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="file.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
contents
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="flowers.jpg"
|
||||
Content-Type: image/jpeg
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
contents
|
||||
--BbC04y--
|
||||
--AaB03x--
|
||||
+1
@@ -0,0 +1 @@
|
||||
bar
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"content-disposition": ["form-data; name=\"foo\""]}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="file.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
contents
|
||||
--BbC04y
|
||||
Content-Disposition: attachment; filename="flowers.jpg"
|
||||
Content-Type: image/jpeg
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
contents
|
||||
--BbC04y--
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{"content-disposition": ["form-data; name=\"files\""],
|
||||
"content-type": ["multipart/mixed, boundary=BbC04y"]}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const Dicer = require('..');
|
||||
|
||||
const CRLF = '\r\n';
|
||||
const boundary = 'boundary';
|
||||
|
||||
const writeSep = `--${boundary}`;
|
||||
|
||||
const writePart = [
|
||||
writeSep,
|
||||
'Content-Type: text/plain',
|
||||
'Content-Length: 0'
|
||||
].join(CRLF) + `${CRLF}${CRLF}some data${CRLF}`;
|
||||
|
||||
const writeEnd = `--${CRLF}`;
|
||||
|
||||
let firedEnd = false;
|
||||
let firedFinish = false;
|
||||
|
||||
const dicer = new Dicer({ boundary });
|
||||
dicer.on('part', partListener);
|
||||
dicer.on('finish', finishListener);
|
||||
dicer.write(writePart + writeSep);
|
||||
|
||||
function partListener(partReadStream) {
|
||||
partReadStream.on('data', () => {});
|
||||
partReadStream.on('end', partEndListener);
|
||||
}
|
||||
|
||||
function partEndListener() {
|
||||
firedEnd = true;
|
||||
setImmediate(afterEnd);
|
||||
}
|
||||
|
||||
function afterEnd() {
|
||||
dicer.end(writeEnd);
|
||||
setImmediate(afterWrite);
|
||||
}
|
||||
|
||||
function finishListener() {
|
||||
assert(firedEnd, 'Failed to end before finishing');
|
||||
firedFinish = true;
|
||||
test2();
|
||||
}
|
||||
|
||||
function afterWrite() {
|
||||
assert(firedFinish, 'Failed to finish');
|
||||
}
|
||||
|
||||
let isPausePush = true;
|
||||
|
||||
let firedPauseCallback = false;
|
||||
let firedPauseFinish = false;
|
||||
|
||||
let dicer2 = null;
|
||||
|
||||
function test2() {
|
||||
dicer2 = new Dicer({ boundary });
|
||||
dicer2.on('part', pausePartListener);
|
||||
dicer2.on('finish', pauseFinish);
|
||||
dicer2.write(writePart + writeSep, 'utf8', pausePartCallback);
|
||||
setImmediate(pauseAfterWrite);
|
||||
}
|
||||
|
||||
function pausePartListener(partReadStream) {
|
||||
partReadStream.on('data', () => {});
|
||||
partReadStream.on('end', () => {});
|
||||
const realPush = partReadStream.push;
|
||||
partReadStream.push = (...args) => {
|
||||
realPush.apply(partReadStream, args);
|
||||
if (!isPausePush)
|
||||
return true;
|
||||
isPausePush = false;
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
function pauseAfterWrite() {
|
||||
dicer2.end(writeEnd);
|
||||
setImmediate(pauseAfterEnd);
|
||||
}
|
||||
|
||||
function pauseAfterEnd() {
|
||||
assert(firedPauseCallback, 'Failed to call callback after pause');
|
||||
assert(firedPauseFinish, 'Failed to finish after pause');
|
||||
}
|
||||
|
||||
function pauseFinish() {
|
||||
firedPauseFinish = true;
|
||||
}
|
||||
|
||||
function pausePartCallback() {
|
||||
firedPauseCallback = true;
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
|
||||
const HeaderParser = require('../lib/HeaderParser');
|
||||
|
||||
const DCRLF = '\r\n\r\n';
|
||||
const MAXED_BUFFER = Buffer.allocUnsafe(128 * 1024);
|
||||
MAXED_BUFFER.fill(0x41); // 'A'
|
||||
|
||||
const group = path.basename(__filename, '.js') + '/';
|
||||
|
||||
function makeMsg(what, msg) {
|
||||
return `[${group}${what}]: ${msg}`;
|
||||
}
|
||||
|
||||
[
|
||||
{ source: DCRLF,
|
||||
expected: {},
|
||||
what: 'No header'
|
||||
},
|
||||
{ source: [
|
||||
'Content-Type:\t text/plain',
|
||||
'Content-Length:0',
|
||||
].join('\r\n') + DCRLF,
|
||||
expected: {
|
||||
'content-type': [' text/plain'],
|
||||
'content-length': ['0'],
|
||||
},
|
||||
what: 'Value spacing',
|
||||
},
|
||||
{ source: [
|
||||
'Content-Type:\r\n text/plain',
|
||||
'Foo:\r\n bar\r\n baz',
|
||||
].join('\r\n') + DCRLF,
|
||||
expected: {
|
||||
'content-type': [' text/plain'],
|
||||
'foo': [' bar baz'],
|
||||
},
|
||||
what: 'Folded values',
|
||||
},
|
||||
{ source: [
|
||||
'Content-Type:',
|
||||
'Foo: ',
|
||||
].join('\r\n') + DCRLF,
|
||||
expected: {
|
||||
'content-type': [''],
|
||||
'foo': [''],
|
||||
},
|
||||
what: 'Empty values',
|
||||
},
|
||||
{ source: MAXED_BUFFER.toString('ascii') + DCRLF,
|
||||
expected: {},
|
||||
what: 'Max header size (single chunk)',
|
||||
},
|
||||
{ source: [
|
||||
'ABCDEFGHIJ',
|
||||
MAXED_BUFFER.toString('ascii'),
|
||||
DCRLF,
|
||||
],
|
||||
expected: {},
|
||||
what: 'Max header size (multiple chunks #1)',
|
||||
},
|
||||
{ source: [
|
||||
MAXED_BUFFER.toString('ascii'),
|
||||
MAXED_BUFFER.toString('ascii'),
|
||||
DCRLF,
|
||||
],
|
||||
expected: {},
|
||||
what: 'Max header size (multiple chunk #2)',
|
||||
},
|
||||
].forEach((v) => {
|
||||
const parser = new HeaderParser();
|
||||
let fired = false;
|
||||
|
||||
parser.on('header', (header) => {
|
||||
assert(!fired, makeMsg(v.what, 'Header event fired more than once'));
|
||||
fired = true;
|
||||
assert.deepEqual(header,
|
||||
v.expected,
|
||||
makeMsg(v.what, 'Parsed result mismatch'));
|
||||
});
|
||||
if (!Array.isArray(v.source))
|
||||
v.source = [v.source];
|
||||
for (const chunk of v.source)
|
||||
parser.push(chunk);
|
||||
assert(fired, makeMsg(v.what, 'Did not receive header from parser'));
|
||||
});
|
||||
|
||||
{
|
||||
const source = [
|
||||
' Content-Disposition: form-data; name="bildbeschreibung"',
|
||||
DCRLF,
|
||||
DCRLF,
|
||||
DCRLF,
|
||||
];
|
||||
const parser = new HeaderParser();
|
||||
let hadError = false;
|
||||
|
||||
parser.on('header', (header) => {
|
||||
assert(false, 'Should not have seen header');
|
||||
});
|
||||
parser.on('error', (err) => {
|
||||
assert(!hadError, 'Unexpected multiple errors');
|
||||
hadError = true;
|
||||
assert(/unexpected folded/i.test(err.message),
|
||||
`Wrong error message: ${err.message}`);
|
||||
});
|
||||
for (const chunk of source)
|
||||
parser.push(chunk);
|
||||
assert(hadError, 'Expected error');
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const Dicer = require('..');
|
||||
|
||||
const FIXTURES_ROOT = `${__dirname}/fixtures/`;
|
||||
|
||||
let t = 0;
|
||||
const group = path.basename(__filename, '.js') + '/';
|
||||
|
||||
function makeMsg(what, msg) {
|
||||
return '[' + group + what + ']: ' + msg;
|
||||
}
|
||||
|
||||
process.on('exit', () => {
|
||||
assert(t === tests.length,
|
||||
makeMsg('_exit', 'Only ran ' + t + '/' + tests.length + ' tests'));
|
||||
});
|
||||
|
||||
const tests = [
|
||||
{ source: 'many',
|
||||
opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' },
|
||||
chsize: 16,
|
||||
nparts: 7,
|
||||
what: 'Extra trailer data pushed after finished'
|
||||
},
|
||||
];
|
||||
|
||||
function next() {
|
||||
if (t === tests.length)
|
||||
return;
|
||||
const v = tests[t];
|
||||
const fixtureBase = FIXTURES_ROOT + v.source;
|
||||
let n = 0;
|
||||
const buffer = Buffer.allocUnsafe(v.chsize);
|
||||
const state = { parts: [] };
|
||||
const fd = fs.openSync(fixtureBase + '/original', 'r');
|
||||
|
||||
const dicer = new Dicer(v.opts);
|
||||
let error;
|
||||
let partErrors = 0;
|
||||
let finishes = 0;
|
||||
|
||||
dicer.on('part', (p) => {
|
||||
const part = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: undefined,
|
||||
header: undefined
|
||||
};
|
||||
|
||||
p.on('header', (h) => {
|
||||
part.header = h;
|
||||
}).on('data', (data) => {
|
||||
// Make a copy because we are using readSync which re-uses a buffer ...
|
||||
const copy = Buffer.allocUnsafe(data.length);
|
||||
data.copy(copy);
|
||||
data = copy;
|
||||
if (!part.body)
|
||||
part.body = [ data ];
|
||||
else
|
||||
part.body.push(data);
|
||||
part.bodylen += data.length;
|
||||
}).on('error', (err) => {
|
||||
part.error = err;
|
||||
++partErrors;
|
||||
}).on('end', () => {
|
||||
if (part.body)
|
||||
part.body = Buffer.concat(part.body, part.bodylen);
|
||||
state.parts.push(part);
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
error = err;
|
||||
}).on('finish', () => {
|
||||
assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times'));
|
||||
|
||||
if (v.dicerError)
|
||||
assert(error !== undefined, makeMsg(v.what, 'Expected error'));
|
||||
else
|
||||
assert(error === undefined, makeMsg(v.what, 'Unexpected error'));
|
||||
|
||||
if (v.events && v.events.indexOf('part') > -1) {
|
||||
assert.equal(state.parts.length,
|
||||
v.nparts,
|
||||
makeMsg(v.what,
|
||||
'Part count mismatch:\nActual: '
|
||||
+ state.parts.length
|
||||
+ '\nExpected: '
|
||||
+ v.nparts));
|
||||
|
||||
if (!v.npartErrors)
|
||||
v.npartErrors = 0;
|
||||
assert.equal(partErrors,
|
||||
v.npartErrors,
|
||||
makeMsg(v.what,
|
||||
'Part errors mismatch:\nActual: '
|
||||
+ partErrors
|
||||
+ '\nExpected: '
|
||||
+ v.npartErrors));
|
||||
|
||||
for (let i = 0, header, body; i < v.nparts; ++i) {
|
||||
if (fs.existsSync(fixtureBase + '/part' + (i + 1))) {
|
||||
body = fs.readFileSync(fixtureBase + '/part' + (i + 1));
|
||||
if (body.length === 0)
|
||||
body = undefined;
|
||||
} else {
|
||||
body = undefined;
|
||||
}
|
||||
assert.deepEqual(state.parts[i].body,
|
||||
body,
|
||||
makeMsg(v.what,
|
||||
'Part #' + (i + 1) + ' body mismatch'));
|
||||
if (fs.existsSync(fixtureBase + '/part' + (i + 1) + '.header')) {
|
||||
header = fs.readFileSync(fixtureBase
|
||||
+ '/part' + (i + 1) + '.header', 'latin1');
|
||||
header = JSON.parse(header);
|
||||
} else {
|
||||
header = undefined;
|
||||
}
|
||||
assert.deepEqual(state.parts[i].header,
|
||||
header,
|
||||
makeMsg(v.what,
|
||||
'Part #' + (i + 1)
|
||||
+ ' parsed header mismatch:\nActual: '
|
||||
+ inspect(state.parts[i].header)
|
||||
+ '\nExpected: '
|
||||
+ inspect(header)));
|
||||
}
|
||||
}
|
||||
++t;
|
||||
next();
|
||||
});
|
||||
|
||||
while (true) {
|
||||
n = fs.readSync(fd, buffer, 0, buffer.length, null);
|
||||
if (n === 0) {
|
||||
setTimeout(() => {
|
||||
dicer.write('\r\n\r\n\r\n');
|
||||
dicer.end();
|
||||
}, 50);
|
||||
break;
|
||||
}
|
||||
dicer.write(n === buffer.length ? buffer : buffer.slice(0, n));
|
||||
}
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
next();
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const Dicer = require('..');
|
||||
|
||||
const FIXTURES_ROOT = `${__dirname}/fixtures/`;
|
||||
|
||||
let t = 0;
|
||||
const group = path.basename(__filename, '.js') + '/';
|
||||
|
||||
function makeMsg(what, msg) {
|
||||
return '[' + group + what + ']: ' + msg;
|
||||
}
|
||||
|
||||
process.on('exit', () => {
|
||||
assert(t === tests.length,
|
||||
makeMsg('_exit', 'Only ran ' + t + '/' + tests.length + ' tests'));
|
||||
});
|
||||
|
||||
const tests = [
|
||||
{ source: 'many',
|
||||
opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' },
|
||||
chsize: 16,
|
||||
nparts: 0,
|
||||
what: 'No preamble or part listeners'
|
||||
},
|
||||
];
|
||||
|
||||
function next() {
|
||||
if (t === tests.length)
|
||||
return;
|
||||
const v = tests[t];
|
||||
const fixtureBase = FIXTURES_ROOT + v.source;
|
||||
let n = 0;
|
||||
const buffer = Buffer.allocUnsafe(v.chsize);
|
||||
const state = { done: false, parts: [], preamble: undefined };
|
||||
const fd = fs.openSync(fixtureBase + '/original', 'r');
|
||||
|
||||
const dicer = new Dicer(v.opts);
|
||||
let error;
|
||||
let partErrors = 0;
|
||||
let finishes = 0;
|
||||
|
||||
if (v.events && v.events.indexOf('preamble') > -1) {
|
||||
dicer.on('preamble', (p) => {
|
||||
const preamble = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: undefined,
|
||||
header: undefined
|
||||
};
|
||||
|
||||
p.on('header', (h) => {
|
||||
preamble.header = h;
|
||||
}).on('data', (data) => {
|
||||
// Make a copy because we are using readSync which re-uses a buffer ...
|
||||
const copy = Buffer.allocUnsafe(data.length);
|
||||
data.copy(copy);
|
||||
data = copy;
|
||||
if (!preamble.body)
|
||||
preamble.body = [ data ];
|
||||
else
|
||||
preamble.body.push(data);
|
||||
preamble.bodylen += data.length;
|
||||
}).on('error', (err) => {
|
||||
preamble.error = err;
|
||||
}).on('end', () => {
|
||||
if (preamble.body)
|
||||
preamble.body = Buffer.concat(preamble.body, preamble.bodylen);
|
||||
if (preamble.body || preamble.header)
|
||||
state.preamble = preamble;
|
||||
});
|
||||
});
|
||||
}
|
||||
if (v.events && v.events.indexOf('part') > -1) {
|
||||
dicer.on('part', (p) => {
|
||||
const part = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: undefined,
|
||||
header: undefined
|
||||
};
|
||||
|
||||
p.on('header', (h) => {
|
||||
part.header = h;
|
||||
}).on('data', (data) => {
|
||||
// Make a copy because we are using readSync which re-uses a buffer ...
|
||||
const copy = Buffer.allocUnsafe(data.length);
|
||||
data.copy(copy);
|
||||
data = copy;
|
||||
if (!part.body)
|
||||
part.body = [ data ];
|
||||
else
|
||||
part.body.push(data);
|
||||
part.bodylen += data.length;
|
||||
}).on('error', (err) => {
|
||||
part.error = err;
|
||||
++partErrors;
|
||||
}).on('end', () => {
|
||||
if (part.body)
|
||||
part.body = Buffer.concat(part.body, part.bodylen);
|
||||
state.parts.push(part);
|
||||
});
|
||||
});
|
||||
}
|
||||
dicer.on('error', (err) => {
|
||||
error = err;
|
||||
}).on('finish', () => {
|
||||
assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times'));
|
||||
|
||||
if (v.dicerError)
|
||||
assert(error !== undefined, makeMsg(v.what, 'Expected error'));
|
||||
else
|
||||
assert(error === undefined, makeMsg(v.what, 'Unexpected error'));
|
||||
|
||||
if (v.events && v.events.indexOf('preamble') > -1) {
|
||||
let preamble;
|
||||
if (fs.existsSync(fixtureBase + '/preamble')) {
|
||||
const prebody = fs.readFileSync(fixtureBase + '/preamble');
|
||||
if (prebody.length) {
|
||||
preamble = {
|
||||
body: prebody,
|
||||
bodylen: prebody.length,
|
||||
error: undefined,
|
||||
header: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(fixtureBase + '/preamble.header')) {
|
||||
const prehead = JSON.parse(fs.readFileSync(
|
||||
fixtureBase + '/preamble.header', 'latin1'
|
||||
));
|
||||
if (!preamble) {
|
||||
preamble = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: undefined,
|
||||
header: prehead
|
||||
};
|
||||
} else {
|
||||
preamble.header = prehead;
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(fixtureBase + '/preamble.error')) {
|
||||
const err = new Error(fs.readFileSync(
|
||||
fixtureBase + '/preamble.error', 'latin1'
|
||||
));
|
||||
if (!preamble) {
|
||||
preamble = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: err,
|
||||
header: undefined
|
||||
};
|
||||
} else {
|
||||
preamble.error = err;
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(state.preamble,
|
||||
preamble,
|
||||
makeMsg(v.what,
|
||||
'Preamble mismatch:\nActual:'
|
||||
+ inspect(state.preamble)
|
||||
+ '\nExpected: '
|
||||
+ inspect(preamble)));
|
||||
}
|
||||
|
||||
if (v.events && v.events.indexOf('part') > -1) {
|
||||
assert.equal(state.parts.length,
|
||||
v.nparts,
|
||||
makeMsg(v.what,
|
||||
'Part count mismatch:\nActual: '
|
||||
+ state.parts.length
|
||||
+ '\nExpected: '
|
||||
+ v.nparts));
|
||||
|
||||
if (!v.npartErrors)
|
||||
v.npartErrors = 0;
|
||||
assert.equal(partErrors,
|
||||
v.npartErrors,
|
||||
makeMsg(v.what,
|
||||
'Part errors mismatch:\nActual: '
|
||||
+ partErrors
|
||||
+ '\nExpected: '
|
||||
+ v.npartErrors));
|
||||
|
||||
for (let i = 0, header, body; i < v.nparts; ++i) {
|
||||
if (fs.existsSync(fixtureBase + '/part' + (i + 1))) {
|
||||
body = fs.readFileSync(fixtureBase + '/part' + (i + 1));
|
||||
if (body.length === 0)
|
||||
body = undefined;
|
||||
} else {
|
||||
body = undefined;
|
||||
}
|
||||
assert.deepEqual(state.parts[i].body,
|
||||
body,
|
||||
makeMsg(v.what,
|
||||
'Part #' + (i + 1) + ' body mismatch'));
|
||||
if (fs.existsSync(fixtureBase + '/part' + (i + 1) + '.header')) {
|
||||
header = fs.readFileSync(fixtureBase
|
||||
+ '/part' + (i + 1) + '.header', 'latin1');
|
||||
header = JSON.parse(header);
|
||||
} else {
|
||||
header = undefined;
|
||||
}
|
||||
assert.deepEqual(state.parts[i].header,
|
||||
header,
|
||||
makeMsg(v.what,
|
||||
'Part #' + (i + 1)
|
||||
+ ' parsed header mismatch:\nActual: '
|
||||
+ inspect(state.parts[i].header)
|
||||
+ '\nExpected: '
|
||||
+ inspect(header)));
|
||||
}
|
||||
}
|
||||
++t;
|
||||
next();
|
||||
});
|
||||
|
||||
while (true) {
|
||||
n = fs.readSync(fd, buffer, 0, buffer.length, null);
|
||||
if (n === 0) {
|
||||
dicer.end();
|
||||
break;
|
||||
}
|
||||
dicer.write(n === buffer.length ? buffer : buffer.slice(0, n));
|
||||
}
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
next();
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const Dicer = require('..');
|
||||
|
||||
const FIXTURES_ROOT = `${__dirname}/fixtures/`;
|
||||
|
||||
let t = 0;
|
||||
const group = path.basename(__filename, '.js') + '/';
|
||||
|
||||
function makeMsg(what, msg) {
|
||||
return '[' + group + what + ']: ' + msg;
|
||||
}
|
||||
|
||||
process.on('exit', function() {
|
||||
assert(t === tests.length,
|
||||
makeMsg('_exit', 'Only ran ' + t + '/' + tests.length + ' tests'));
|
||||
});
|
||||
|
||||
const tests = [
|
||||
{ source: 'nested',
|
||||
opts: { boundary: 'AaB03x' },
|
||||
chsize: 32,
|
||||
nparts: 2,
|
||||
what: 'One nested multipart'
|
||||
},
|
||||
{ source: 'many',
|
||||
opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' },
|
||||
chsize: 16,
|
||||
nparts: 7,
|
||||
what: 'Many parts'
|
||||
},
|
||||
{ source: 'many-wrongboundary',
|
||||
opts: { boundary: 'LOLOLOL' },
|
||||
chsize: 8,
|
||||
nparts: 0,
|
||||
dicerError: true,
|
||||
what: 'Many parts, wrong boundary'
|
||||
},
|
||||
{ source: 'many-noend',
|
||||
opts: { boundary: '----WebKitFormBoundaryWLHCs9qmcJJoyjKR' },
|
||||
chsize: 16,
|
||||
nparts: 7,
|
||||
npartErrors: 1,
|
||||
dicerError: true,
|
||||
what: 'Many parts, end boundary missing, 1 file open'
|
||||
},
|
||||
{ source: 'nested-full',
|
||||
opts: { boundary: 'AaB03x', headerFirst: true },
|
||||
chsize: 32,
|
||||
nparts: 2,
|
||||
what: 'One nested multipart with preceding header'
|
||||
},
|
||||
{ source: 'nested-full',
|
||||
opts: { headerFirst: true },
|
||||
chsize: 32,
|
||||
nparts: 2,
|
||||
setBoundary: 'AaB03x',
|
||||
what: 'One nested multipart with preceding header, using setBoundary'
|
||||
},
|
||||
];
|
||||
|
||||
function next() {
|
||||
if (t === tests.length)
|
||||
return;
|
||||
const v = tests[t];
|
||||
const fixtureBase = FIXTURES_ROOT + v.source;
|
||||
const state = { parts: [], preamble: undefined };
|
||||
|
||||
const dicer = new Dicer(v.opts);
|
||||
let error;
|
||||
let partErrors = 0;
|
||||
let finishes = 0;
|
||||
|
||||
dicer.on('preamble', (p) => {
|
||||
const preamble = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: undefined,
|
||||
header: undefined
|
||||
};
|
||||
|
||||
p.on('header', (h) => {
|
||||
preamble.header = h;
|
||||
if (v.setBoundary)
|
||||
dicer.setBoundary(v.setBoundary);
|
||||
}).on('data', (data) => {
|
||||
// Make a copy because we are using readSync which re-uses a buffer ...
|
||||
const copy = Buffer.allocUnsafe(data.length);
|
||||
data.copy(copy);
|
||||
data = copy;
|
||||
if (!preamble.body)
|
||||
preamble.body = [ data ];
|
||||
else
|
||||
preamble.body.push(data);
|
||||
preamble.bodylen += data.length;
|
||||
}).on('error', (err) => {
|
||||
preamble.error = err;
|
||||
}).on('end', () => {
|
||||
if (preamble.body)
|
||||
preamble.body = Buffer.concat(preamble.body, preamble.bodylen);
|
||||
if (preamble.body || preamble.header)
|
||||
state.preamble = preamble;
|
||||
});
|
||||
});
|
||||
dicer.on('part', (p) => {
|
||||
const part = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: undefined,
|
||||
header: undefined
|
||||
};
|
||||
|
||||
p.on('header', (h) => {
|
||||
part.header = h;
|
||||
}).on('data', (data) => {
|
||||
if (!part.body)
|
||||
part.body = [ data ];
|
||||
else
|
||||
part.body.push(data);
|
||||
part.bodylen += data.length;
|
||||
}).on('error', (err) => {
|
||||
part.error = err;
|
||||
++partErrors;
|
||||
}).on('end', () => {
|
||||
if (part.body)
|
||||
part.body = Buffer.concat(part.body, part.bodylen);
|
||||
state.parts.push(part);
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
error = err;
|
||||
}).on('finish', () => {
|
||||
assert(finishes++ === 0, makeMsg(v.what, 'finish emitted multiple times'));
|
||||
|
||||
if (v.dicerError) {
|
||||
assert(error !== undefined, makeMsg(v.what, 'Expected error'));
|
||||
} else {
|
||||
assert(error === undefined,
|
||||
makeMsg(v.what, 'Unexpected error: ' + error));
|
||||
}
|
||||
|
||||
let preamble;
|
||||
if (fs.existsSync(fixtureBase + '/preamble')) {
|
||||
const prebody = fs.readFileSync(fixtureBase + '/preamble');
|
||||
if (prebody.length) {
|
||||
preamble = {
|
||||
body: prebody,
|
||||
bodylen: prebody.length,
|
||||
error: undefined,
|
||||
header: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(fixtureBase + '/preamble.header')) {
|
||||
const prehead = JSON.parse(fs.readFileSync(
|
||||
fixtureBase + '/preamble.header', 'latin1'
|
||||
));
|
||||
if (!preamble) {
|
||||
preamble = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: undefined,
|
||||
header: prehead
|
||||
};
|
||||
} else {
|
||||
preamble.header = prehead;
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(fixtureBase + '/preamble.error')) {
|
||||
const err = new Error(fs.readFileSync(
|
||||
fixtureBase + '/preamble.error', 'latin1'
|
||||
));
|
||||
if (!preamble) {
|
||||
preamble = {
|
||||
body: undefined,
|
||||
bodylen: 0,
|
||||
error: err,
|
||||
header: undefined
|
||||
};
|
||||
} else {
|
||||
preamble.error = err;
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(state.preamble,
|
||||
preamble,
|
||||
makeMsg(v.what,
|
||||
'Preamble mismatch:\nActual:'
|
||||
+ inspect(state.preamble)
|
||||
+ '\nExpected: '
|
||||
+ inspect(preamble)));
|
||||
|
||||
assert.equal(state.parts.length,
|
||||
v.nparts,
|
||||
makeMsg(v.what,
|
||||
'Part count mismatch:\nActual: '
|
||||
+ state.parts.length
|
||||
+ '\nExpected: '
|
||||
+ v.nparts));
|
||||
|
||||
if (!v.npartErrors)
|
||||
v.npartErrors = 0;
|
||||
assert.equal(partErrors,
|
||||
v.npartErrors,
|
||||
makeMsg(v.what,
|
||||
'Part errors mismatch:\nActual: '
|
||||
+ partErrors
|
||||
+ '\nExpected: '
|
||||
+ v.npartErrors));
|
||||
|
||||
for (let i = 0, header, body; i < v.nparts; ++i) {
|
||||
if (fs.existsSync(fixtureBase + '/part' + (i + 1))) {
|
||||
body = fs.readFileSync(fixtureBase + '/part' + (i + 1));
|
||||
if (body.length === 0)
|
||||
body = undefined;
|
||||
} else {
|
||||
body = undefined;
|
||||
}
|
||||
assert.deepEqual(state.parts[i].body,
|
||||
body,
|
||||
makeMsg(v.what,
|
||||
'Part #' + (i + 1) + ' body mismatch'));
|
||||
if (fs.existsSync(fixtureBase + '/part' + (i + 1) + '.header')) {
|
||||
header = fs.readFileSync(fixtureBase
|
||||
+ '/part' + (i + 1) + '.header', 'latin1');
|
||||
header = JSON.parse(header);
|
||||
} else {
|
||||
header = undefined;
|
||||
}
|
||||
assert.deepEqual(state.parts[i].header,
|
||||
header,
|
||||
makeMsg(v.what,
|
||||
'Part #' + (i + 1)
|
||||
+ ' parsed header mismatch:\nActual: '
|
||||
+ inspect(state.parts[i].header)
|
||||
+ '\nExpected: '
|
||||
+ inspect(header)));
|
||||
}
|
||||
++t;
|
||||
next();
|
||||
});
|
||||
|
||||
fs.createReadStream(fixtureBase + '/original').pipe(dicer);
|
||||
}
|
||||
next();
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
require('fs').readdirSync(__dirname).forEach((f) => {
|
||||
if (f.substr(0, 5) === 'test-')
|
||||
require(`./${f}`);
|
||||
});
|
||||
Reference in New Issue
Block a user