Skip to content

Commit

Permalink
Merge pull request #25 from hapijs/qs
Browse files Browse the repository at this point in the history
Remove qs support. Closes #24
  • Loading branch information
johnbrett committed Dec 22, 2015
2 parents 131b4f1 + f401a78 commit dbdff43
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 175 deletions.
17 changes: 5 additions & 12 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

const Fs = require('fs');
const Os = require('os');
const Querystring = require('querystring');
const Stream = require('stream');
const Zlib = require('zlib');
const Boom = require('boom');
const Content = require('content');
const Hoek = require('hoek');
const Pez = require('pez');
const Qs = require('qs');
const Wreck = require('wreck');


Expand Down Expand Up @@ -257,7 +257,7 @@ internals.object = function (payload, mime, options, next) {
// Form-encoded

if (mime === 'application/x-www-form-urlencoded') {
return next(null, payload.length ? Qs.parse(payload.toString('utf8'), options.qs) : {});
return next(null, payload.length ? Querystring.parse(payload.toString('utf8')) : {});
}

return next(Boom.unsupportedMediaType());
Expand Down Expand Up @@ -302,8 +302,7 @@ internals.Parser.prototype.multipart = function (source, contentType) {

dispenser.once('error', onError);

let arrayFields = false;
let data = {};
const data = {};
const finalize = () => {

clearTimeout(clientTimeoutId);
Expand All @@ -312,10 +311,6 @@ internals.Parser.prototype.multipart = function (source, contentType) {
dispenser.removeListener('field', onField);
dispenser.removeListener('close', onClose);

if (arrayFields) {
data = Qs.parse(data, this.settings.qs);
}

this.result.payload = data;
return next();
};
Expand All @@ -331,8 +326,6 @@ internals.Parser.prototype.multipart = function (source, contentType) {

const set = (name, value) => {

arrayFields = arrayFields || (name.indexOf('[') !== -1);

if (!data.hasOwnProperty(name)) {
data[name] = value;
}
Expand Down Expand Up @@ -378,9 +371,9 @@ internals.Parser.prototype.multipart = function (source, contentType) {
});
}
else { // Output: 'data'
Wreck.read(part, {}, (err, payload) => {
Wreck.read(part, {}, (ignoreErr, payload) => {

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

if (this.settings.output === 'stream') { // Output: 'stream'
const item = Wreck.toReadableStream(payload);
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "subtext",
"description": "HTTP payload parsing",
"version": "3.0.1",
"version": "4.0.0",
"repository": "git://github.com/hapijs/subtext",
"main": "lib/index.js",
"keywords": [
Expand All @@ -19,13 +19,12 @@
"content": "3.x.x",
"hoek": "3.x.x",
"pez": "2.x.x",
"qs": "6.x.x",
"wreck": "7.x.x"
},
"devDependencies": {
"code": "2.x.x",
"form-data": "0.1.x",
"lab": "7.x.x"
"lab": "8.x.x"
},
"scripts": {
"test": "lab -a code -t 100 -L",
Expand Down
178 changes: 18 additions & 160 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ describe('parse()', () => {
expect(parsed.mime).to.equal('application/json');
Wreck.read(parsed.payload, null, (err, result) => {

expect(err).to.not.exist();
expect(result.toString()).to.equal(payload);
done();
});
Expand All @@ -99,6 +100,7 @@ describe('parse()', () => {
expect(parsed.mime).to.equal('application/json');
Wreck.read(parsed.payload, null, (err, result) => {

expect(err).to.not.exist();
expect(result.toString()).to.equal(payload);
done();
});
Expand Down Expand Up @@ -316,6 +318,7 @@ describe('parse()', () => {

Zlib.gzip(sourceContents, (err, compressed) => {

expect(err).to.not.exist();
const request = Wreck.toReadableStream(compressed);
request.headers = {
'content-encoding': 'gzip'
Expand All @@ -341,6 +344,7 @@ describe('parse()', () => {

Zlib.gzip(sourceContents, (err, compressed) => {

expect(err).to.not.exist();
const request = Wreck.toReadableStream(compressed);
request.headers = {
'content-encoding': 'gzip',
Expand Down Expand Up @@ -587,57 +591,6 @@ describe('parse()', () => {
});
});

it('parses form encoded payload (array keys)', (done) => {

const payload = 'x[y]=1&x[z]=2';
const request = Wreck.toReadableStream(payload);
request.headers = {
'content-type': 'application/x-www-form-urlencoded'
};

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

expect(err).to.not.exist();
expect(parsed.mime).to.equal('application/x-www-form-urlencoded');
expect(parsed.payload).to.deep.equal({ x: { y: '1', z: '2' } });
done();
});
});

it('parses form encoded payload (with qs arraylimit set to 0)', (done) => {

const payload = 'x[0]=1&x[100]=2';
const request = Wreck.toReadableStream(payload);
request.headers = {
'content-type': 'application/x-www-form-urlencoded'
};

Subtext.parse(request, null, { parse: true, output: 'data', qs: { arrayLimit: 0 } }, (err, parsed) => {

expect(err).to.not.exist();
expect(parsed.mime).to.equal('application/x-www-form-urlencoded');
expect(parsed.payload).to.deep.equal({ x: { 0: '1', 100: '2' } });
done();
});
});

it('parses form encoded payload (with qs arraylimit set to 30) as flat zero indexed array', (done) => {

const payload = 'x[0]=0&x[1]=1&x[2]=2&x[3]=3&x[4]=4&x[5]=5&x[6]=6&x[7]=7&x[8]=8&x[9]=9&x[10]=10&x[11]=11&x[12]=12&x[13]=13&x[14]=14&x[15]=15&x[16]=16&x[17]=17&x[18]=18&x[19]=19&x[20]=20&x[21]=21&x[22]=22&x[23]=23&x[24]=24&x[25]=25&x[26]=26&x[27]=27&x[28]=28&x[29]=29&';
const request = Wreck.toReadableStream(payload);
request.headers = {
'content-type': 'application/x-www-form-urlencoded'
};

Subtext.parse(request, null, { parse: true, output: 'data', qs: { arrayLimit: 30 } }, (err, parsed) => {

expect(err).to.not.exist();
expect(parsed.mime).to.equal('application/x-www-form-urlencoded');
expect(parsed.payload).to.deep.equal({ x: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29'] });
done();
});
});

it('errors on malformed zipped payload', (done) => {

const payload = '7d8d78347h8347d58w347hd58w374d58w37h5d8w37hd4';
Expand Down Expand Up @@ -677,6 +630,7 @@ describe('parse()', () => {
const payload = '{"x":"1","y":"2","z":"3"}';
Zlib.gzip(payload, (err, compressed) => {

expect(err).to.not.exist();
const request = Wreck.toReadableStream(compressed);
request.headers = {
'content-encoding': 'gzip',
Expand All @@ -697,6 +651,7 @@ describe('parse()', () => {
const payload = '{"x":"1","y":"2","z":"3"}';
Zlib.gzip(payload, (err, compressed) => {

expect(err).to.not.exist();
const request = Wreck.toReadableStream(compressed);
request.headers = {
'content-encoding': 'gzip',
Expand All @@ -717,6 +672,7 @@ describe('parse()', () => {
const payload = '{"x":"1","y":"2","z":"3"}';
Zlib.deflate(payload, (err, compressed) => {

expect(err).to.not.exist();
const request = Wreck.toReadableStream(compressed);
request.headers = {
'content-encoding': 'deflate',
Expand All @@ -737,6 +693,7 @@ describe('parse()', () => {
const payload = '{"x":"1","y":"2","z":"3"}';
Zlib.deflate(payload, (err, compressed) => {

expect(err).to.not.exist();
const request = Wreck.toReadableStream(compressed);
request.headers = {
'content-encoding': 'deflate',
Expand Down Expand Up @@ -800,55 +757,7 @@ describe('parse()', () => {
});
});

it('parses a multipart payload with qs arraylimit set to zero', (done) => {

const payload =
'--AaB03x\r\n' +
'content-disposition: form-data; name="x[0]"\r\n' +
'\r\n' +
'First\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="x[1]"\r\n' +
'\r\n' +
'Second\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="x[30]"\r\n' +
'\r\n' +
'Third\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="field1"\r\n' +
'\r\n' +
'Joe Blow\r\nalmost tricked you!\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="field1"\r\n' +
'\r\n' +
'Repeated name segment\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
'Content-Type: text/plain\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', qs: { arrayLimit: 0 } }, (err, parsed) => {

expect(err).to.not.exist();
expect(parsed.payload).to.deep.equal({
x: { '0': 'First', '1': 'Second', '30': 'Third' },
field1: ['Joe Blow\r\nalmost tricked you!', 'Repeated name segment'],
pics: '... contents of file1.txt ...\r'
});

done();
});
});

it('parses a multipart payload', (done) => {
it('parses a multipart payload (ignores unknown mime type)', (done) => {

const payload =
'--AaB03x\r\n' +
Expand All @@ -873,7 +782,7 @@ describe('parse()', () => {
'Repeated name segment\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
'Content-Type: text/plain\r\n' +
'Content-Type: unknown/X\r\n' +
'\r\n' +
'... contents of file1.txt ...\r\r\n' +
'--AaB03x--\r\n';
Expand All @@ -889,7 +798,7 @@ describe('parse()', () => {
expect(parsed.payload).to.deep.equal({
x: ['First', 'Second', 'Third'],
field1: ['Joe Blow\r\nalmost tricked you!', 'Repeated name segment'],
pics: '... contents of file1.txt ...\r'
pics: new Buffer('... contents of file1.txt ...\r')
});

done();
Expand Down Expand Up @@ -1061,10 +970,13 @@ describe('parse()', () => {

Wreck.read(parsed.payload.files[1], null, (err, payload2) => {

expect(err).to.not.exist();
Wreck.read(parsed.payload.files[0], null, (err, payload1) => {

expect(err).to.not.exist();
Wreck.read(parsed.payload.files[2], null, (err, payload3) => {

expect(err).to.not.exist();
expect(payload1.toString()).to.equal('one');
expect(payload2.toString()).to.equal('two');
expect(payload3.toString()).to.equal('three');
Expand Down Expand Up @@ -1349,61 +1261,6 @@ describe('parse()', () => {
});
});

it('parses field names with arrays', (done) => {

const payload = '--AaB03x\r\n' +
'Content-Disposition: form-data; name="a[b]"\r\n' +
'\r\n' +
'3\r\n' +
'--AaB03x\r\n' +
'Content-Disposition: form-data; name="a[c]"\r\n' +
'\r\n' +
'4\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' }, (err, parsed) => {

expect(err).to.not.exist();
expect(parsed.payload.a.b + parsed.payload.a.c).to.equal('34');
done();
});
});

it('parses field names with arrays and file', (done) => {

const payload = '----WebKitFormBoundaryE19zNvXGzXaLvS5C\r\n' +
'Content-Disposition: form-data; name="a[b]"\r\n' +
'\r\n' +
'3\r\n' +
'----WebKitFormBoundaryE19zNvXGzXaLvS5C\r\n' +
'Content-Disposition: form-data; name="a[c]"\r\n' +
'\r\n' +
'4\r\n' +
'----WebKitFormBoundaryE19zNvXGzXaLvS5C\r\n' +
'Content-Disposition: form-data; name="file"; filename="test.txt"\r\n' +
'Content-Type: plain/text\r\n' +
'\r\n' +
'and\r\n' +
'----WebKitFormBoundaryE19zNvXGzXaLvS5C--\r\n';

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

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

expect(err).to.not.exist();
expect(parsed.payload.a.b + parsed.payload.file + parsed.payload.a.c).to.equal('3and4');
done();
});
});

it('cleans file when stream is aborted', (done) => {

const path = Path.join(__dirname, 'file');
Expand All @@ -1414,6 +1271,7 @@ describe('parse()', () => {

Subtext.parse(req, null, { parse: false, output: 'file', uploads: path }, (err, parsed) => {

expect(err).to.exist();
expect(Fs.readdirSync(path).length).to.equal(count);
done();
});
Expand All @@ -1431,7 +1289,7 @@ describe('parse()', () => {

const req = Http.request(options, (res) => { });

req.on('error', (err) => { });
req.on('error', () => { });

const random = new Buffer(100000);
req.write(random);
Expand Down Expand Up @@ -1480,7 +1338,7 @@ describe('parse()', () => {
Subtext.parse(form, null, { parse: true, output: 'stream', timeout: 1 }, (err, parsed) => {

expect(err).to.exist();
expect(err.message).to.equal('Request Timeout');
expect(err.message).to.equal('Request Time-out');
expect(err.output.statusCode).to.equal(408);
done();
});
Expand All @@ -1498,7 +1356,7 @@ describe('parse()', () => {
Subtext.parse(form, null, { parse: true, output: 'file', timeout: 1 }, (err, parsed) => {

expect(err).to.exist();
expect(err.message).to.equal('Request Timeout');
expect(err.message).to.equal('Request Time-out');
expect(err.output.statusCode).to.equal(408);
done();
});
Expand Down

0 comments on commit dbdff43

Please sign in to comment.