Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[major] Improve error messages and use specific error types
  • Loading branch information
lpinca committed Jan 5, 2018
1 parent a31b1f6 commit 695c5ea
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 139 deletions.
26 changes: 17 additions & 9 deletions lib/Extensions.js
Expand Up @@ -67,7 +67,9 @@ function parse (header) {
} else if (code === 0x20/* ' ' */|| code === 0x09/* '\t' */) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b/* ';' */ || code === 0x2c/* ',' */) {
if (start === -1) throw new Error(`unexpected character at index ${i}`);
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}

if (end === -1) end = i;
const name = header.slice(start, end);
Expand All @@ -80,15 +82,17 @@ function parse (header) {

start = end = -1;
} else {
throw new Error(`unexpected character at index ${i}`);
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else if (paramName === undefined) {
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x20 || code === 0x09) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b || code === 0x2c) {
if (start === -1) throw new Error(`unexpected character at index ${i}`);
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}

if (end === -1) end = i;
push(params, header.slice(start, end), true);
Expand All @@ -103,7 +107,7 @@ function parse (header) {
paramName = header.slice(start, i);
start = end = -1;
} else {
throw new Error(`unexpected character at index ${i}`);
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else {
//
Expand All @@ -113,7 +117,7 @@ function parse (header) {
//
if (isEscaping) {
if (tokenChars[code] !== 1) {
throw new Error(`unexpected character at index ${i}`);
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (start === -1) start = i;
else if (!mustUnescape) mustUnescape = true;
Expand All @@ -127,7 +131,7 @@ function parse (header) {
} else if (code === 0x5c/* '\' */) {
isEscaping = true;
} else {
throw new Error(`unexpected character at index ${i}`);
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
inQuotes = true;
Expand All @@ -136,7 +140,9 @@ function parse (header) {
} else if (start !== -1 && (code === 0x20 || code === 0x09)) {
if (end === -1) end = i;
} else if (code === 0x3b || code === 0x2c) {
if (start === -1) throw new Error(`unexpected character at index ${i}`);
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}

if (end === -1) end = i;
var value = header.slice(start, end);
Expand All @@ -154,12 +160,14 @@ function parse (header) {
paramName = undefined;
start = end = -1;
} else {
throw new Error(`unexpected character at index ${i}`);
throw new SyntaxError(`Unexpected character at index ${i}`);
}
}
}

if (start === -1 || inQuotes) throw new Error('unexpected end of input');
if (start === -1 || inQuotes) {
throw new SyntaxError('Unexpected end of input');
}

if (end === -1) end = i;
const token = header.slice(start, end);
Expand Down
42 changes: 25 additions & 17 deletions lib/PerMessageDeflate.js
Expand Up @@ -175,7 +175,9 @@ class PerMessageDeflate {
return true;
});

if (!accepted) throw new Error("Doesn't support the offered configuration");
if (!accepted) {
throw new Error('None of the extension offers can be accepted');
}

if (opts.serverNoContextTakeover) {
accepted.server_no_context_takeover = true;
Expand Down Expand Up @@ -212,7 +214,7 @@ class PerMessageDeflate {
this._options.clientNoContextTakeover === false &&
params.client_no_context_takeover
) {
throw new Error('Invalid value for "client_no_context_takeover"');
throw new Error('Unexpected parameter "client_no_context_takeover"');
}

if (!params.client_max_window_bits) {
Expand All @@ -224,7 +226,9 @@ class PerMessageDeflate {
(typeof this._options.clientMaxWindowBits === 'number' &&
params.client_max_window_bits > this._options.clientMaxWindowBits)
) {
throw new Error('Invalid value for "client_max_window_bits"');
throw new Error(
'Unexpected or invalid parameter "client_max_window_bits"'
);
}

return params;
Expand All @@ -243,40 +247,44 @@ class PerMessageDeflate {
var value = params[key];

if (value.length > 1) {
throw new Error(`Multiple extension parameters for ${key}`);
throw new Error(`Parameter "${key}" must have only a single value`);
}

value = value[0];

if (key === 'client_max_window_bits') {
if (value !== true) {
value = +value;
if (!Number.isInteger(value) || value < 8 || value > 15) {
throw new Error(
`invalid extension parameter value for ${key} (${value})`
const num = +value;
if (!Number.isInteger(num) || num < 8 || num > 15) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
value = num;
} else if (!this._isServer) {
throw new Error(`Missing extension parameter value for ${key}`);
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
} else if (key === 'server_max_window_bits') {
value = +value;
if (!Number.isInteger(value) || value < 8 || value > 15) {
throw new Error(
`invalid extension parameter value for ${key} (${value})`
const num = +value;
if (!Number.isInteger(num) || num < 8 || num > 15) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
value = num;
} else if (
key === 'client_no_context_takeover' ||
key === 'server_no_context_takeover'
) {
if (value !== true) {
throw new Error(
`invalid extension parameter value for ${key} (${value})`
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
} else {
throw new Error(`Not defined extension parameter (${key})`);
throw new Error(`Unknown parameter "${key}"`);
}

params[key] = value;
Expand Down Expand Up @@ -480,7 +488,7 @@ function inflateOnData (chunk) {
return;
}

this[kError] = new Error('max payload size exceeded');
this[kError] = new RangeError('Max payload size exceeded');
this[kError].closeCode = 1009;
this.removeListener('data', inflateOnData);
this.reset();
Expand Down
87 changes: 70 additions & 17 deletions lib/Receiver.js
Expand Up @@ -181,14 +181,20 @@ class Receiver {
const buf = this.readBuffer(2);

if ((buf[0] & 0x30) !== 0x00) {
this.error(new Error('RSV2 and RSV3 must be clear'), 1002);
this.error(
new RangeError('Invalid WebSocket frame: RSV2 and RSV3 must be clear'),
1002
);
return;
}

const compressed = (buf[0] & 0x40) === 0x40;

if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
this.error(new Error('RSV1 must be clear'), 1002);
this.error(
new RangeError('Invalid WebSocket frame: RSV1 must be clear'),
1002
);
return;
}

Expand All @@ -198,40 +204,68 @@ class Receiver {

if (this._opcode === 0x00) {
if (compressed) {
this.error(new Error('RSV1 must be clear'), 1002);
this.error(
new RangeError('Invalid WebSocket frame: RSV1 must be clear'),
1002
);
return;
}

if (!this._fragmented) {
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
this.error(
new RangeError('Invalid WebSocket frame: invalid opcode 0'),
1002
);
return;
} else {
this._opcode = this._fragmented;
}
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
if (this._fragmented) {
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
this.error(
new RangeError(
`Invalid WebSocket frame: invalid opcode ${this._opcode}`
),
1002
);
return;
}

this._compressed = compressed;
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
if (!this._fin) {
this.error(new Error('FIN must be set'), 1002);
this.error(
new RangeError('Invalid WebSocket frame: FIN must be set'),
1002
);
return;
}

if (compressed) {
this.error(new Error('RSV1 must be clear'), 1002);
this.error(
new RangeError('Invalid WebSocket frame: RSV1 must be clear'),
1002
);
return;
}

if (this._payloadLength > 0x7d) {
this.error(new Error('invalid payload length'), 1002);
this.error(
new RangeError(
`Invalid WebSocket frame: invalid payload length ` +
`${this._payloadLength}`
),
1002
);
return;
}
} else {
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
this.error(
new RangeError(
`Invalid WebSocket frame: invalid opcode ${this._opcode}`
),
1002
);
return;
}

Expand Down Expand Up @@ -272,11 +306,16 @@ class Receiver {
// if payload length is greater than this number.
//
if (num > Math.pow(2, 53 - 32) - 1) {
this.error(new Error('max payload size exceeded'), 1009);
this.error(
new RangeError(
'Unsupported WebSocket frame: payload length > 2^53 - 1'
),
1009
);
return;
}

this._payloadLength = (num * Math.pow(2, 32)) + buf.readUInt32BE(4, true);
this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4, true);
this.haveLength();
}

Expand Down Expand Up @@ -382,7 +421,10 @@ class Receiver {
const buf = toBuffer(fragments, messageLength);

if (!isValidUTF8(buf)) {
this.error(new Error('invalid utf8 sequence'), 1007);
this.error(
new Error('Invalid WebSocket frame: invalid UTF-8 sequence'),
1007
);
return;
}

Expand All @@ -406,19 +448,30 @@ class Receiver {
this._loop = false;
this.cleanup(this._cleanupCallback);
} else if (data.length === 1) {
this.error(new Error('invalid payload length'), 1002);
this.error(
new RangeError('Invalid WebSocket frame: invalid payload length 1'),
1002
);
} else {
const code = data.readUInt16BE(0, true);

if (!ErrorCodes.isValidErrorCode(code)) {
this.error(new Error(`invalid status code: ${code}`), 1002);
this.error(
new RangeError(
`Invalid WebSocket frame: invalid status code ${code}`
),
1002
);
return;
}

const buf = data.slice(2);

if (!isValidUTF8(buf)) {
this.error(new Error('invalid utf8 sequence'), 1007);
this.error(
new Error('Invalid WebSocket frame: invalid UTF-8 sequence'),
1007
);
return;
}

Expand Down Expand Up @@ -466,7 +519,7 @@ class Receiver {
return false;
}

this.error(new Error('max payload size exceeded'), 1009);
this.error(new RangeError('Max payload size exceeded'), 1009);
return true;
}

Expand All @@ -489,7 +542,7 @@ class Receiver {
return true;
}

this.error(new Error('max payload size exceeded'), 1009);
this.error(new RangeError('Max payload size exceeded'), 1009);
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Sender.js
Expand Up @@ -118,7 +118,7 @@ class Sender {
if (code === undefined) {
buf = constants.EMPTY_BUFFER;
} else if (typeof code !== 'number' || !ErrorCodes.isValidErrorCode(code)) {
throw new Error('first argument must be a valid error code number');
throw new TypeError('First argument must be a valid error code number');
} else if (data === undefined || data === '') {
buf = Buffer.allocUnsafe(2);
buf.writeUInt16BE(code, 0, true);
Expand Down

0 comments on commit 695c5ea

Please sign in to comment.