Skip to content

Commit

Permalink
[minor] Make extension.parse() use null-prototype objects
Browse files Browse the repository at this point in the history
  • Loading branch information
lpinca committed Apr 26, 2019
1 parent aca3858 commit 993b0cd
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 52 deletions.
16 changes: 8 additions & 8 deletions lib/extension.js
Expand Up @@ -34,8 +34,8 @@ const tokenChars = [
* @private
*/
function push(dest, name, elem) {
if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem);
else dest[name] = [elem];
if (dest[name] === undefined) dest[name] = [elem];
else dest[name].push(elem);
}

/**
Expand All @@ -46,11 +46,11 @@ function push(dest, name, elem) {
* @public
*/
function parse(header) {
const offers = {};
const offers = Object.create(null);

if (header === undefined || header === '') return offers;

let params = {};
let params = Object.create(null);
let mustUnescape = false;
let isEscaping = false;
let inQuotes = false;
Expand All @@ -77,7 +77,7 @@ function parse(header) {
const name = header.slice(start, end);
if (code === 0x2c) {
push(offers, name, params);
params = {};
params = Object.create(null);
} else {
extensionName = name;
}
Expand All @@ -100,7 +100,7 @@ function parse(header) {
push(params, header.slice(start, end), true);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
params = Object.create(null);
extensionName = undefined;
}

Expand Down Expand Up @@ -155,7 +155,7 @@ function parse(header) {
push(params, paramName, value);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
params = Object.create(null);
extensionName = undefined;
}

Expand All @@ -174,7 +174,7 @@ function parse(header) {
if (end === -1) end = i;
const token = header.slice(start, end);
if (extensionName === undefined) {
push(offers, token, {});
push(offers, token, params);
} else {
if (paramName === undefined) {
push(params, token, true);
Expand Down
84 changes: 44 additions & 40 deletions test/extension.test.js
Expand Up @@ -2,79 +2,83 @@

const assert = require('assert');

const extension = require('../lib/extension');
const { format, parse } = require('../lib/extension');

describe('extension', () => {
describe('parse', () => {
it('returns an empty object if the argument is `undefined`', () => {
assert.deepStrictEqual(extension.parse(), {});
assert.deepStrictEqual(extension.parse(''), {});
assert.deepStrictEqual(parse(), { __proto__: null });
assert.deepStrictEqual(parse(''), { __proto__: null });
});

it('parses a single extension', () => {
const extensions = extension.parse('foo');

assert.deepStrictEqual(extensions, { foo: [{}] });
assert.deepStrictEqual(parse('foo'), {
foo: [{ __proto__: null }],
__proto__: null
});
});

it('parses params', () => {
const extensions = extension.parse('foo;bar;baz=1;bar=2');

assert.deepStrictEqual(extensions, {
foo: [{ bar: [true, '2'], baz: ['1'] }]
assert.deepStrictEqual(parse('foo;bar;baz=1;bar=2'), {
foo: [{ bar: [true, '2'], baz: ['1'], __proto__: null }],
__proto__: null
});
});

it('parses multiple extensions', () => {
const extensions = extension.parse('foo,bar;baz,foo;baz');

assert.deepStrictEqual(extensions, {
foo: [{}, { baz: [true] }],
bar: [{ baz: [true] }]
assert.deepStrictEqual(parse('foo,bar;baz,foo;baz'), {
foo: [{ __proto__: null }, { baz: [true], __proto__: null }],
bar: [{ baz: [true], __proto__: null }],
__proto__: null
});
});

it('parses quoted params', () => {
assert.deepStrictEqual(extension.parse('foo;bar="hi"'), {
foo: [{ bar: ['hi'] }]
assert.deepStrictEqual(parse('foo;bar="hi"'), {
foo: [{ bar: ['hi'], __proto__: null }],
__proto__: null
});
assert.deepStrictEqual(extension.parse('foo;bar="\\0"'), {
foo: [{ bar: ['0'] }]
assert.deepStrictEqual(parse('foo;bar="\\0"'), {
foo: [{ bar: ['0'], __proto__: null }],
__proto__: null
});
assert.deepStrictEqual(extension.parse('foo;bar="b\\a\\z"'), {
foo: [{ bar: ['baz'] }]
assert.deepStrictEqual(parse('foo;bar="b\\a\\z"'), {
foo: [{ bar: ['baz'], __proto__: null }],
__proto__: null
});
assert.deepStrictEqual(extension.parse('foo;bar="b\\az";bar'), {
foo: [{ bar: ['baz', true] }]
assert.deepStrictEqual(parse('foo;bar="b\\az";bar'), {
foo: [{ bar: ['baz', true], __proto__: null }],
__proto__: null
});
assert.throws(
() => extension.parse('foo;bar="baz"qux'),
() => parse('foo;bar="baz"qux'),
/^SyntaxError: Unexpected character at index 13$/
);
assert.throws(
() => extension.parse('foo;bar="baz" qux'),
() => parse('foo;bar="baz" qux'),
/^SyntaxError: Unexpected character at index 14$/
);
});

it('works with names that match `Object.prototype` property names', () => {
const parse = extension.parse;

assert.deepStrictEqual(parse('hasOwnProperty, toString'), {
hasOwnProperty: [{}],
toString: [{}]
hasOwnProperty: [{ __proto__: null }],
toString: [{ __proto__: null }],
__proto__: null
});
assert.deepStrictEqual(parse('foo;constructor'), {
foo: [{ constructor: [true] }]
foo: [{ constructor: [true], __proto__: null }],
__proto__: null
});
});

it('ignores the optional white spaces', () => {
const header = 'foo; bar\t; \tbaz=1\t ; bar="1"\t\t, \tqux\t ;norf ';

assert.deepStrictEqual(extension.parse(header), {
foo: [{ bar: [true, '1'], baz: ['1'] }],
qux: [{ norf: [true] }]
assert.deepStrictEqual(parse(header), {
foo: [{ bar: [true, '1'], baz: ['1'], __proto__: null }],
qux: [{ norf: [true], __proto__: null }],
__proto__: null
});
});

Expand All @@ -91,7 +95,7 @@ describe('extension', () => {
['foo;bar=""', 9]
].forEach((element) => {
assert.throws(
() => extension.parse(element[0]),
() => parse(element[0]),
new RegExp(
`^SyntaxError: Unexpected character at index ${element[1]}$`
)
Expand All @@ -107,7 +111,7 @@ describe('extension', () => {
['foo;bar= ', 8]
].forEach((element) => {
assert.throws(
() => extension.parse(element[0]),
() => parse(element[0]),
new RegExp(
`^SyntaxError: Unexpected character at index ${element[1]}$`
)
Expand All @@ -133,7 +137,7 @@ describe('extension', () => {
['foo;bar="\\\\"', 10]
].forEach((element) => {
assert.throws(
() => extension.parse(element[0]),
() => parse(element[0]),
new RegExp(
`^SyntaxError: Unexpected character at index ${element[1]}$`
)
Expand All @@ -152,7 +156,7 @@ describe('extension', () => {
'foo;bar="1\\'
].forEach((header) => {
assert.throws(
() => extension.parse(header),
() => parse(header),
/^SyntaxError: Unexpected end of input$/
);
});
Expand All @@ -161,19 +165,19 @@ describe('extension', () => {

describe('format', () => {
it('formats a single extension', () => {
const extensions = extension.format({ foo: {} });
const extensions = format({ foo: {} });

assert.strictEqual(extensions, 'foo');
});

it('formats params', () => {
const extensions = extension.format({ foo: { bar: [true, 2], baz: 1 } });
const extensions = format({ foo: { bar: [true, 2], baz: 1 } });

assert.strictEqual(extensions, 'foo; bar; bar=2; baz=1');
});

it('formats multiple extensions', () => {
const extensions = extension.format({
const extensions = format({
foo: [{}, { baz: true }],
bar: { baz: true }
});
Expand Down
12 changes: 8 additions & 4 deletions test/permessage-deflate.test.js
Expand Up @@ -120,7 +120,8 @@ describe('PerMessageDeflate', () => {
server_no_context_takeover: true,
client_no_context_takeover: true,
server_max_window_bits: 10,
client_max_window_bits: 11
client_max_window_bits: 11,
__proto__: null
}
);
});
Expand All @@ -145,7 +146,8 @@ describe('PerMessageDeflate', () => {
server_no_context_takeover: true,
client_no_context_takeover: true,
server_max_window_bits: 12,
client_max_window_bits: 11
client_max_window_bits: 11,
__proto__: null
}
);
});
Expand All @@ -162,7 +164,8 @@ describe('PerMessageDeflate', () => {
assert.deepStrictEqual(
perMessageDeflate.accept(extensions['permessage-deflate']),
{
server_max_window_bits: 11
server_max_window_bits: 11,
__proto__: null
}
);
});
Expand Down Expand Up @@ -259,7 +262,8 @@ describe('PerMessageDeflate', () => {
server_no_context_takeover: true,
client_no_context_takeover: true,
server_max_window_bits: 10,
client_max_window_bits: 11
client_max_window_bits: 11,
__proto__: null
}
);
});
Expand Down

0 comments on commit 993b0cd

Please sign in to comment.