Skip to content

Commit

Permalink
Support multipart override (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
hueniverse authored and johnbrett committed Oct 19, 2016
1 parent 6dc7f75 commit 8e03aaa
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 19 deletions.
35 changes: 16 additions & 19 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ internals.Parser = function (req, tap, options, next) {
this.tap = tap;

this.result = {};

this.next = (err) => {

return next(err, this.result);
};
this.next = (err) => next(err, this.result);
};


Expand Down Expand Up @@ -126,6 +122,10 @@ internals.Parser.prototype.parse = function (contentType) {
// Multipart

if (this.result.contentType.mime === 'multipart/form-data') {
if (this.settings.multipart === false) { // Defaults to true
return next(Boom.unsupportedMediaType());
}

return this.multipart(source, contentType);
}

Expand Down Expand Up @@ -160,7 +160,7 @@ internals.Parser.prototype.parse = function (contentType) {
return next(err);
}

internals.object(payload, this.result.contentType.mime, this.settings, (err, result) => {
internals.object(payload, this.result.contentType.mime, (err, result) => {

if (err) {
this.result.payload = null;
Expand Down Expand Up @@ -243,7 +243,7 @@ internals.Parser.prototype.raw = function () {
};


internals.object = function (payload, mime, options, next) {
internals.object = function (payload, mime, next) {

// Binary

Expand Down Expand Up @@ -351,9 +351,11 @@ internals.Parser.prototype.multipart = function (source, contentType) {
let nextId = 0;
let closed = false;

const output = (this.settings.multipart ? this.settings.multipart.output : this.settings.output);

const onPart = (part) => {

if (this.settings.output === 'file') { // Output: 'file'
if (output === 'file') { // Output: 'file'
const id = nextId++;
pendingFiles[id] = true;
this.writeFile(part, (err, path, bytes) => {
Expand Down Expand Up @@ -385,7 +387,7 @@ internals.Parser.prototype.multipart = function (source, contentType) {

// Error handled by dispenser.once('error')

if (this.settings.output === 'stream') { // Output: 'stream'
if (output === 'stream') { // Output: 'stream'
const item = Wreck.toReadableStream(payload);

item.hapi = {
Expand All @@ -398,29 +400,24 @@ internals.Parser.prototype.multipart = function (source, contentType) {

const ct = part.headers['content-type'] || '';
const mime = ct.split(';')[0].trim().toLowerCase();
const annotate = (value) => set(part.name, output === 'annotated' ? { filename: part.filename, headers: part.headers, payload: value } : value);

if (!mime) {
return set(part.name, payload);
return annotate(payload);
}

if (!payload.length) {
return set(part.name, {});
return annotate({});
}

internals.object(payload, mime, this.settings, (err, result) => {

return set(part.name, err ? payload : result);
});
internals.object(payload, mime, (err, result) => annotate(err ? payload : result));
});
}
};

dispenser.on('part', onPart);

const onField = (name, value) => {

set(name, value);
};
const onField = (name, value) => set(name, value);

dispenser.on('field', onField);

Expand Down
76 changes: 76 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,28 @@ describe('parse()', () => {
});
});

it('errors on disabled multipart', (done) => {

const payload =
'--AaB03x\r\n' +
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
'Content-Type: text/plain\r\n' +
'\r\n' +
'\r\n' +
'--AaB03x--\r\n';

const request = Wreck.toReadableStream(payload);
request.headers = {
'content-type': 'multipart/form-data; boundary=AaB03x'
};

Subtext.parse(request, null, { parse: true, output: 'data', multipart: false }, (err, parsed) => {

expect(err).to.exist();
done();
});
});

it('errors on an invalid multipart header (missing boundary)', (done) => {

const payload =
Expand Down Expand Up @@ -1052,6 +1074,37 @@ describe('parse()', () => {
});
});

it('parses file with annotation', (done) => {

const payload =
'--AaB03x\r\n' +
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
'Content-Type: image/jpeg\r\n' +
'\r\n' +
'... contents of file1.txt ...\r\r\n' +
'--AaB03x--\r\n';

const request = Wreck.toReadableStream(payload);
request.headers = {
'content-type': 'multipart/form-data; boundary="AaB03x"'
};

Subtext.parse(request, null, { parse: true, output: 'data', multipart: { output: 'annotated' } }, (err, parsed) => {

expect(err).to.not.exist();
expect(parsed.payload.pics).to.equal({
payload: new Buffer('... contents of file1.txt ...\r'),
headers: {
'content-disposition': 'form-data; name="pics"; filename="file1.txt"',
'content-type': 'image/jpeg'
},
filename: 'file1.txt'
});

done();
});
});

it('errors on invalid uploads folder while processing multipart payload', (done) => {

const payload =
Expand Down Expand Up @@ -1148,6 +1201,29 @@ describe('parse()', () => {
});
});

it('parses a multipart file as file (multipart override)', (done) => {

const path = Path.join(__dirname, './file/image.jpg');
const stats = Fs.statSync(path);

const form = new FormData();
form.append('my_file', Fs.createReadStream(path));
form.headers = form.getHeaders();

Subtext.parse(form, null, { parse: true, output: 'data', multipart: { output: 'file' } }, (err, parsed) => {

expect(err).to.not.exist();

expect(parsed.payload.my_file.bytes).to.equal(stats.size);

const sourceContents = Fs.readFileSync(path);
const receivedContents = Fs.readFileSync(parsed.payload.my_file.path);
Fs.unlinkSync(parsed.payload.my_file.path);
expect(sourceContents).to.equal(receivedContents);
done();
});
});

it('parses multiple files as files', (done) => {

const path = Path.join(__dirname, './file/image.jpg');
Expand Down

0 comments on commit 8e03aaa

Please sign in to comment.