diff --git a/lib/Extensions.js b/lib/Extensions.js index 15fee1216..d59d39202 100644 --- a/lib/Extensions.js +++ b/lib/Extensions.js @@ -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); @@ -80,7 +82,7 @@ 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) { @@ -88,7 +90,9 @@ function parse (header) { } 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); @@ -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 { // @@ -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; @@ -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; @@ -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); @@ -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); diff --git a/lib/PerMessageDeflate.js b/lib/PerMessageDeflate.js index ccdb0f70c..b452b574b 100644 --- a/lib/PerMessageDeflate.js +++ b/lib/PerMessageDeflate.js @@ -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; @@ -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) { @@ -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; @@ -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; @@ -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(); diff --git a/lib/Receiver.js b/lib/Receiver.js index 9e0d86474..126ff9676 100644 --- a/lib/Receiver.js +++ b/lib/Receiver.js @@ -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; } @@ -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; } @@ -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(); } @@ -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; } @@ -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; } @@ -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; } @@ -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; } diff --git a/lib/Sender.js b/lib/Sender.js index 39e5a73f5..b01e4dba5 100644 --- a/lib/Sender.js +++ b/lib/Sender.js @@ -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); diff --git a/lib/WebSocket.js b/lib/WebSocket.js index b115b1477..4b7f044df 100644 --- a/lib/WebSocket.js +++ b/lib/WebSocket.js @@ -20,6 +20,7 @@ const constants = require('./Constants'); const Receiver = require('./Receiver'); const Sender = require('./Sender'); +const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; const protocolVersions = [8, 13]; const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly. @@ -229,7 +230,12 @@ class WebSocket extends EventEmitter { * @public */ pause () { - if (this.readyState !== WebSocket.OPEN) throw new Error('not opened'); + if (this.readyState !== WebSocket.OPEN) { + throw new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + } this._socket.pause(); } @@ -240,7 +246,12 @@ class WebSocket extends EventEmitter { * @public */ resume () { - if (this.readyState !== WebSocket.OPEN) throw new Error('not opened'); + if (this.readyState !== WebSocket.OPEN) { + throw new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + } this._socket.resume(); } @@ -272,7 +283,9 @@ class WebSocket extends EventEmitter { if (this.readyState === WebSocket.CLOSED) return; if (this.readyState === WebSocket.CONNECTING) { this._req.abort(); - this.finalize(new Error('closed before the connection is established')); + this.finalize( + new Error('WebSocket was closed before the connection was established') + ); return; } @@ -314,7 +327,10 @@ class WebSocket extends EventEmitter { ping (data, mask, failSilently) { if (this.readyState !== WebSocket.OPEN) { if (failSilently) return; - throw new Error('not opened'); + throw new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); } if (typeof data === 'number') data = data.toString(); @@ -333,7 +349,10 @@ class WebSocket extends EventEmitter { pong (data, mask, failSilently) { if (this.readyState !== WebSocket.OPEN) { if (failSilently) return; - throw new Error('not opened'); + throw new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); } if (typeof data === 'number') data = data.toString(); @@ -360,8 +379,13 @@ class WebSocket extends EventEmitter { } if (this.readyState !== WebSocket.OPEN) { - if (cb) cb(new Error('not opened')); - else throw new Error('not opened'); + const err = new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + + if (cb) cb(err); + else throw err; return; } @@ -390,7 +414,9 @@ class WebSocket extends EventEmitter { if (this.readyState === WebSocket.CLOSED) return; if (this.readyState === WebSocket.CONNECTING) { this._req.abort(); - this.finalize(new Error('closed before the connection is established')); + this.finalize( + new Error('WebSocket was closed before the connection was established') + ); return; } @@ -398,10 +424,9 @@ class WebSocket extends EventEmitter { } } -WebSocket.CONNECTING = 0; -WebSocket.OPEN = 1; -WebSocket.CLOSING = 2; -WebSocket.CLOSED = 3; +readyStates.forEach((readyState, i) => { + WebSocket[readyStates[i]] = i; +}); // // Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. @@ -524,9 +549,9 @@ function initAsClient (address, protocols, options) { }, options); if (protocolVersions.indexOf(options.protocolVersion) === -1) { - throw new Error( - `unsupported protocol version: ${options.protocolVersion} ` + - `(supported versions: ${protocolVersions.join(', ')})` + throw new RangeError( + `Unsupported protocol version: ${options.protocolVersion} ` + + `(supported versions: ${protocolVersions.join(', ')})` ); } @@ -538,7 +563,7 @@ function initAsClient (address, protocols, options) { const isUnixSocket = serverUrl.protocol === 'ws+unix:'; if (!serverUrl.host && (!isUnixSocket || !serverUrl.path)) { - throw new Error('invalid url'); + throw new Error(`Invalid URL: ${address}`); } const isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:'; @@ -640,7 +665,7 @@ function initAsClient (address, protocols, options) { if (options.handshakeTimeout) { this._req.setTimeout(options.handshakeTimeout, () => { this._req.abort(); - this.finalize(new Error('opening handshake has timed out')); + this.finalize(new Error('Opening handshake has timed out')); }); } @@ -654,7 +679,7 @@ function initAsClient (address, protocols, options) { this._req.on('response', (res) => { if (!this.emit('unexpected-response', this._req, res)) { this._req.abort(); - this.finalize(new Error(`unexpected server response (${res.statusCode})`)); + this.finalize(new Error(`Unexpected server response: ${res.statusCode}`)); } }); @@ -675,7 +700,7 @@ function initAsClient (address, protocols, options) { if (res.headers['sec-websocket-accept'] !== digest) { socket.destroy(); - return this.finalize(new Error('invalid server key')); + return this.finalize(new Error('Invalid Sec-WebSocket-Accept header')); } const serverProt = res.headers['sec-websocket-protocol']; @@ -683,11 +708,11 @@ function initAsClient (address, protocols, options) { var protError; if (!options.protocol && serverProt) { - protError = 'server sent a subprotocol even though none requested'; + protError = 'Server sent a subprotocol but none was requested'; } else if (options.protocol && !serverProt) { - protError = 'server sent no subprotocol even though requested'; + protError = 'Server sent no subprotocol'; } else if (serverProt && protList.indexOf(serverProt) === -1) { - protError = 'server responded with an invalid protocol'; + protError = 'Server sent an invalid subprotocol'; } if (protError) { @@ -711,7 +736,7 @@ function initAsClient (address, protocols, options) { } } catch (err) { socket.destroy(); - this.finalize(new Error('invalid Sec-WebSocket-Extensions header')); + this.finalize(new Error('Invalid Sec-WebSocket-Extensions header')); return; } } diff --git a/lib/WebSocketServer.js b/lib/WebSocketServer.js index 8ebd6bf81..b5efb1df7 100644 --- a/lib/WebSocketServer.js +++ b/lib/WebSocketServer.js @@ -60,7 +60,9 @@ class WebSocketServer extends EventEmitter { }, options); if (options.port == null && !options.server && !options.noServer) { - throw new TypeError('missing or invalid options'); + throw new TypeError( + 'One of the "port", "server", or "noServer" options must be specified' + ); } if (options.port != null) { diff --git a/test/Extensions.test.js b/test/Extensions.test.js index f08ab0852..8f23d05e2 100644 --- a/test/Extensions.test.js +++ b/test/Extensions.test.js @@ -49,11 +49,11 @@ describe('Extensions', function () { }); assert.throws( () => Extensions.parse('foo;bar="baz"qux'), - /^Error: unexpected character at index 13$/ + /^SyntaxError: Unexpected character at index 13$/ ); assert.throws( () => Extensions.parse('foo;bar="baz" qux'), - /^Error: unexpected character at index 14$/ + /^SyntaxError: Unexpected character at index 14$/ ); }); @@ -92,7 +92,7 @@ describe('Extensions', function () { ].forEach((element) => { assert.throws( () => Extensions.parse(element[0]), - new RegExp(`^Error: unexpected character at index ${element[1]}$`) + new RegExp(`^SyntaxError: Unexpected character at index ${element[1]}$`) ); }); }); @@ -106,7 +106,7 @@ describe('Extensions', function () { ].forEach((element) => { assert.throws( () => Extensions.parse(element[0]), - new RegExp(`^Error: unexpected character at index ${element[1]}$`) + new RegExp(`^SyntaxError: Unexpected character at index ${element[1]}$`) ); }); }); @@ -130,7 +130,7 @@ describe('Extensions', function () { ].forEach((element) => { assert.throws( () => Extensions.parse(element[0]), - new RegExp(`^Error: unexpected character at index ${element[1]}$`) + new RegExp(`^SyntaxError: Unexpected character at index ${element[1]}$`) ); }); }); @@ -147,7 +147,7 @@ describe('Extensions', function () { ].forEach((header) => { assert.throws( () => Extensions.parse(header), - /^Error: unexpected end of input$/ + /^SyntaxError: Unexpected end of input$/ ); }); }); diff --git a/test/PerMessageDeflate.test.js b/test/PerMessageDeflate.test.js index 0065c7bee..5f74b9d3d 100644 --- a/test/PerMessageDeflate.test.js +++ b/test/PerMessageDeflate.test.js @@ -224,7 +224,7 @@ describe('PerMessageDeflate', function () { assert.throws( () => perMessageDeflate.accept(extensions['permessage-deflate']), - /^Error: Not defined extension parameter \(foo\)$/ + /^Error: Unknown parameter "foo"$/ ); }); }); @@ -429,8 +429,8 @@ describe('PerMessageDeflate', function () { perMessageDeflate.decompress(data, true, (err) => errors.push(err)); perMessageDeflate._inflate.flush(() => { assert.strictEqual(errors.length, 1); - assert.ok(errors[0] instanceof Error); - assert.strictEqual(errors[0].message, 'max payload size exceeded'); + assert.ok(errors[0] instanceof RangeError); + assert.strictEqual(errors[0].message, 'Max payload size exceeded'); done(); }); }); diff --git a/test/Receiver.test.js b/test/Receiver.test.js index cbb61e6fe..b6affbd00 100644 --- a/test/Receiver.test.js +++ b/test/Receiver.test.js @@ -447,8 +447,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'RSV1 must be clear'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: RSV1 must be clear' + ); assert.strictEqual(code, 1002); done(); }; @@ -463,8 +466,11 @@ describe('Receiver', function () { const p = new Receiver({ 'permessage-deflate': perMessageDeflate }); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'RSV1 must be clear'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: RSV1 must be clear' + ); assert.strictEqual(code, 1002); done(); }; @@ -476,8 +482,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'RSV2 and RSV3 must be clear'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: RSV2 and RSV3 must be clear' + ); assert.strictEqual(code, 1002); done(); }; @@ -489,8 +498,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'RSV2 and RSV3 must be clear'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: RSV2 and RSV3 must be clear' + ); assert.strictEqual(code, 1002); done(); }; @@ -502,8 +514,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid opcode: 0'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid opcode 0' + ); assert.strictEqual(code, 1002); done(); }; @@ -515,8 +530,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid opcode: 1'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid opcode 1' + ); assert.strictEqual(code, 1002); done(); }; @@ -529,8 +547,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid opcode: 2'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid opcode 2' + ); assert.strictEqual(code, 1002); done(); }; @@ -543,8 +564,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'FIN must be set'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: FIN must be set' + ); assert.strictEqual(code, 1002); done(); }; @@ -559,8 +583,11 @@ describe('Receiver', function () { const p = new Receiver({ 'permessage-deflate': perMessageDeflate }); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'RSV1 must be clear'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: RSV1 must be clear' + ); assert.strictEqual(code, 1002); done(); }; @@ -572,8 +599,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'FIN must be set'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: FIN must be set' + ); assert.strictEqual(code, 1002); done(); }; @@ -585,8 +615,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid payload length'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid payload length 126' + ); assert.strictEqual(code, 1002); done(); }; @@ -598,8 +631,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'max payload size exceeded'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Unsupported WebSocket frame: payload length > 2^53 - 1' + ); assert.strictEqual(code, 1009); done(); }; @@ -616,7 +652,10 @@ describe('Receiver', function () { p.onerror = function (err, code) { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid utf8 sequence'); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid UTF-8 sequence' + ); assert.strictEqual(code, 1007); done(); }; @@ -628,8 +667,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid payload length'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid payload length 1' + ); assert.strictEqual(code, 1002); done(); }; @@ -641,8 +683,11 @@ describe('Receiver', function () { const p = new Receiver(); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid status code: 0'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid status code 0' + ); assert.strictEqual(code, 1002); done(); }; @@ -655,7 +700,10 @@ describe('Receiver', function () { p.onerror = function (err, code) { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid utf8 sequence'); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid UTF-8 sequence' + ); assert.strictEqual(code, 1007); done(); }; @@ -678,8 +726,8 @@ describe('Receiver', function () { const frame = Buffer.concat(list); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'max payload size exceeded'); + assert.ok(err instanceof RangeError); + assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(code, 1009); done(); }; @@ -702,8 +750,8 @@ describe('Receiver', function () { const frame = Buffer.concat(list); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'max payload size exceeded'); + assert.ok(err instanceof RangeError); + assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(code, 1009); done(); }; @@ -719,8 +767,8 @@ describe('Receiver', function () { const buf = Buffer.from('A'.repeat(50)); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'max payload size exceeded'); + assert.ok(err instanceof RangeError); + assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(code, 1009); done(); }; @@ -741,8 +789,8 @@ describe('Receiver', function () { const buf = Buffer.from('A'.repeat(15)); p.onerror = function (err, code) { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'max payload size exceeded'); + assert.ok(err instanceof RangeError); + assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(code, 1009); done(); }; @@ -871,7 +919,7 @@ describe('Receiver', function () { assert.deepStrictEqual(results, [ 'Hello', 'Hello', - 'RSV2 and RSV3 must be clear', + 'Invalid WebSocket frame: RSV2 and RSV3 must be clear', 1002 ]); assert.strictEqual(p.onmessage, null); diff --git a/test/WebSocket.test.js b/test/WebSocket.test.js index ca2d34a36..aa4fced44 100644 --- a/test/WebSocket.test.js +++ b/test/WebSocket.test.js @@ -26,7 +26,7 @@ describe('WebSocket', function () { it('throws an error when using an invalid url', function () { assert.throws( () => new WebSocket('echo.websocket.org'), - /^Error: invalid url$/ + /^Error: Invalid URL: echo\.websocket\.org$/ ); }); }); @@ -61,7 +61,7 @@ describe('WebSocket', function () { assert.throws( () => new WebSocket('ws://localhost', options), - /^Error: unsupported protocol version: 1000 \(supported versions: 8, 13\)$/ + /^RangeError: Unsupported protocol version: 1000 \(supported versions: 8, 13\)$/ ); }); @@ -403,7 +403,7 @@ describe('WebSocket', function () { ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid server key'); + assert.strictEqual(err.message, 'Invalid Sec-WebSocket-Accept header'); done(); }); }); @@ -448,7 +448,7 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'unexpected server response (401)'); + assert.strictEqual(err.message, 'Unexpected server response: 401'); done(); }); }); @@ -520,7 +520,7 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'opening handshake has timed out'); + assert.strictEqual(err.message, 'Opening handshake has timed out'); done(); }); }); @@ -546,7 +546,7 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid Sec-WebSocket-Extensions header'); + assert.strictEqual(err.message, 'Invalid Sec-WebSocket-Extensions header'); ws.on('close', () => done()); }); }); @@ -574,7 +574,7 @@ describe('WebSocket', function () { assert.ok(err instanceof Error); assert.strictEqual( err.message, - 'server sent a subprotocol even though none requested' + 'Server sent a subprotocol but none was requested' ); ws.on('close', () => done()); }); @@ -605,13 +605,19 @@ describe('WebSocket', function () { it('throws an error when `readyState` is not `OPEN` (pause)', function () { const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); - assert.throws(() => ws.pause(), /^Error: not opened$/); + assert.throws( + () => ws.pause(), + /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ + ); }); it('throws an error when `readyState` is not `OPEN` (resume)', function () { const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); - assert.throws(() => ws.resume(), /^Error: not opened$/); + assert.throws( + () => ws.resume(), + /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ + ); }); it('pauses the underlying stream', function (done) { @@ -662,7 +668,10 @@ describe('WebSocket', function () { agent: new CustomAgent() }); - assert.throws(() => ws.ping(), /^Error: not opened$/); + assert.throws( + () => ws.ping(), + /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ + ); }); it('before connect can silently fail', function () { @@ -729,7 +738,10 @@ describe('WebSocket', function () { agent: new CustomAgent() }); - assert.throws(() => ws.pong(), /^Error: not opened$/); + assert.throws( + () => ws.pong(), + /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ + ); }); it('before connect can silently fail', function () { @@ -958,7 +970,10 @@ describe('WebSocket', function () { agent: new CustomAgent() }); - assert.throws(() => ws.send('hi'), /^Error: not opened$/); + assert.throws( + () => ws.send('hi'), + /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ + ); }); it('before connect should pass error through callback, if present', function () { @@ -968,7 +983,10 @@ describe('WebSocket', function () { ws.send('hi', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'not opened'); + assert.strictEqual( + err.message, + 'WebSocket is not open: readyState 0 (CONNECTING)' + ); }); }); @@ -1088,7 +1106,10 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'closed before the connection is established'); + assert.strictEqual( + err.message, + 'WebSocket was closed before the connection was established' + ); ws.on('close', () => wss.close(done)); }); ws.close(1001); @@ -1106,7 +1127,10 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'closed before the connection is established'); + assert.strictEqual( + err.message, + 'WebSocket was closed before the connection was established' + ); ws.on('close', () => wss.close(done)); }); setTimeout(() => ws.close(1001), 150); @@ -1133,7 +1157,10 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'closed before the connection is established'); + assert.strictEqual( + err.message, + 'WebSocket was closed before the connection was established' + ); ws.on('close', () => wss.close(done)); }); ws.on('headers', () => ws.close()); @@ -1148,7 +1175,7 @@ describe('WebSocket', function () { ws.on('open', () => { assert.throws( () => ws.close('error'), - /^Error: first argument must be a valid error code number$/ + /^TypeError: First argument must be a valid error code number$/ ); wss.close(done); @@ -1164,7 +1191,7 @@ describe('WebSocket', function () { ws.on('open', () => { assert.throws( () => ws.close(1004), - /^Error: first argument must be a valid error code number$/ + /^TypeError: First argument must be a valid error code number$/ ); wss.close(done); @@ -1350,7 +1377,10 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'closed before the connection is established'); + assert.strictEqual( + err.message, + 'WebSocket was closed before the connection was established' + ); ws.on('close', () => wss.close(done)); }); ws.terminate(); @@ -1368,7 +1398,10 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'closed before the connection is established'); + assert.strictEqual( + err.message, + 'WebSocket was closed before the connection was established' + ); ws.on('close', () => wss.close(done)); }); setTimeout(() => ws.terminate(), 150); @@ -1395,7 +1428,10 @@ describe('WebSocket', function () { ws.on('open', () => done(new Error("unexpected 'open' event"))); ws.on('error', (err) => { assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'closed before the connection is established'); + assert.strictEqual( + err.message, + 'WebSocket was closed before the connection was established' + ); ws.on('close', () => wss.close(done)); }); ws.on('headers', () => ws.terminate()); diff --git a/test/WebSocketServer.test.js b/test/WebSocketServer.test.js index 2bfa09cb0..8e97c9bb0 100644 --- a/test/WebSocketServer.test.js +++ b/test/WebSocketServer.test.js @@ -879,8 +879,11 @@ describe('WebSocketServer', function () { wss.on('connection', (ws) => { ws.on('error', (err) => { - assert.ok(err instanceof Error); - assert.strictEqual(err.message, 'invalid opcode: 5'); + assert.ok(err instanceof RangeError); + assert.strictEqual( + err.message, + 'Invalid WebSocket frame: invalid opcode 5' + ); ws.on('close', (code, reason) => { assert.strictEqual(code, 1002);