From ff56ba6a3aa7d7acbc305fe8ca678a8374645274 Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Mon, 14 Jun 2021 19:35:28 +0200 Subject: [PATCH 1/5] Attach error codes to all receiver errors --- lib/permessage-deflate.js | 1 + lib/receiver.js | 138 +++++++++++++++++++++++---- test/create-websocket-stream.test.js | 1 + test/receiver.test.js | 22 +++++ test/websocket.test.js | 1 + 5 files changed, 144 insertions(+), 19 deletions(-) diff --git a/lib/permessage-deflate.js b/lib/permessage-deflate.js index a8974b988..8229280f1 100644 --- a/lib/permessage-deflate.js +++ b/lib/permessage-deflate.js @@ -495,6 +495,7 @@ function inflateOnData(chunk) { } this[kError] = new RangeError('Max payload size exceeded'); + this[kError].code = 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'; this[kError][kStatusCode] = 1009; this.removeListener('data', inflateOnData); this.reset(); diff --git a/lib/receiver.js b/lib/receiver.js index 65a5ab45f..848a52561 100644 --- a/lib/receiver.js +++ b/lib/receiver.js @@ -168,14 +168,26 @@ class Receiver extends Writable { if ((buf[0] & 0x30) !== 0x00) { this._loop = false; - return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002); + return error( + RangeError, + 'RSV2 and RSV3 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_2_3' + ); } const compressed = (buf[0] & 0x40) === 0x40; if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { this._loop = false; - return error(RangeError, 'RSV1 must be clear', true, 1002); + return error( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ); } this._fin = (buf[0] & 0x80) === 0x80; @@ -185,31 +197,61 @@ class Receiver extends Writable { if (this._opcode === 0x00) { if (compressed) { this._loop = false; - return error(RangeError, 'RSV1 must be clear', true, 1002); + return error( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ); } if (!this._fragmented) { this._loop = false; - return error(RangeError, 'invalid opcode 0', true, 1002); + return error( + RangeError, + 'invalid opcode 0', + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ); } this._opcode = this._fragmented; } else if (this._opcode === 0x01 || this._opcode === 0x02) { if (this._fragmented) { this._loop = false; - return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + return error( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ); } this._compressed = compressed; } else if (this._opcode > 0x07 && this._opcode < 0x0b) { if (!this._fin) { this._loop = false; - return error(RangeError, 'FIN must be set', true, 1002); + return error( + RangeError, + 'FIN must be set', + true, + 1002, + 'WS_ERR_EXPECTED_FIN' + ); } if (compressed) { this._loop = false; - return error(RangeError, 'RSV1 must be clear', true, 1002); + return error( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ); } if (this._payloadLength > 0x7d) { @@ -218,12 +260,19 @@ class Receiver extends Writable { RangeError, `invalid payload length ${this._payloadLength}`, true, - 1002 + 1002, + 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH' ); } } else { this._loop = false; - return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + return error( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ); } if (!this._fin && !this._fragmented) this._fragmented = this._opcode; @@ -232,11 +281,23 @@ class Receiver extends Writable { if (this._isServer) { if (!this._masked) { this._loop = false; - return error(RangeError, 'MASK must be set', true, 1002); + return error( + RangeError, + 'MASK must be set', + true, + 1002, + 'WS_ERR_EXPECTED_MASK' + ); } } else if (this._masked) { this._loop = false; - return error(RangeError, 'MASK must be clear', true, 1002); + return error( + RangeError, + 'MASK must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_MASK' + ); } if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; @@ -285,7 +346,8 @@ class Receiver extends Writable { RangeError, 'Unsupported WebSocket frame: payload length > 2^53 - 1', false, - 1009 + 1009, + 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH' ); } @@ -304,7 +366,13 @@ class Receiver extends Writable { this._totalPayloadLength += this._payloadLength; if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { this._loop = false; - return error(RangeError, 'Max payload size exceeded', false, 1009); + return error( + RangeError, + 'Max payload size exceeded', + false, + 1009, + 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH' + ); } } @@ -384,7 +452,13 @@ class Receiver extends Writable { this._messageLength += buf.length; if (this._messageLength > this._maxPayload && this._maxPayload > 0) { return cb( - error(RangeError, 'Max payload size exceeded', false, 1009) + error( + RangeError, + 'Max payload size exceeded', + false, + 1009, + 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH' + ) ); } @@ -431,7 +505,13 @@ class Receiver extends Writable { if (!isValidUTF8(buf)) { this._loop = false; - return error(Error, 'invalid UTF-8 sequence', true, 1007); + return error( + Error, + 'invalid UTF-8 sequence', + true, + 1007, + 'WS_ERR_INVALID_UTF8' + ); } this.emit('message', buf.toString()); @@ -456,18 +536,36 @@ class Receiver extends Writable { this.emit('conclude', 1005, ''); this.end(); } else if (data.length === 1) { - return error(RangeError, 'invalid payload length 1', true, 1002); + return error( + RangeError, + 'invalid payload length 1', + true, + 1002, + 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH' + ); } else { const code = data.readUInt16BE(0); if (!isValidStatusCode(code)) { - return error(RangeError, `invalid status code ${code}`, true, 1002); + return error( + RangeError, + `invalid status code ${code}`, + true, + 1002, + 'WS_ERR_INVALID_CLOSE_CODE' + ); } const buf = data.slice(2); if (!isValidUTF8(buf)) { - return error(Error, 'invalid UTF-8 sequence', true, 1007); + return error( + Error, + 'invalid UTF-8 sequence', + true, + 1007, + 'WS_ERR_INVALID_UTF8' + ); } this.emit('conclude', code, buf.toString()); @@ -493,15 +591,17 @@ module.exports = Receiver; * @param {Boolean} prefix Specifies whether or not to add a default prefix to * `message` * @param {Number} statusCode The status code + * @param {String} errorCode The exposed error code * @return {(Error|RangeError)} The error * @private */ -function error(ErrorCtor, message, prefix, statusCode) { +function error(ErrorCtor, message, prefix, statusCode, errorCode) { const err = new ErrorCtor( prefix ? `Invalid WebSocket frame: ${message}` : message ); Error.captureStackTrace(err, error); + err.code = errorCode; err[kStatusCode] = statusCode; return err; } diff --git a/test/create-websocket-stream.test.js b/test/create-websocket-stream.test.js index ddccb56b2..bcd240974 100644 --- a/test/create-websocket-stream.test.js +++ b/test/create-websocket-stream.test.js @@ -210,6 +210,7 @@ describe('createWebSocketStream', () => { duplex.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid opcode 5' diff --git a/test/receiver.test.js b/test/receiver.test.js index a70cc8dbe..54f32b04b 100644 --- a/test/receiver.test.js +++ b/test/receiver.test.js @@ -513,6 +513,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_UNEXPECTED_RSV_1'); assert.strictEqual( err.message, 'Invalid WebSocket frame: RSV1 must be clear' @@ -534,6 +535,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_UNEXPECTED_RSV_1'); assert.strictEqual( err.message, 'Invalid WebSocket frame: RSV1 must be clear' @@ -550,6 +552,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_UNEXPECTED_RSV_2_3'); assert.strictEqual( err.message, 'Invalid WebSocket frame: RSV2 and RSV3 must be clear' @@ -566,6 +569,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_UNEXPECTED_RSV_2_3'); assert.strictEqual( err.message, 'Invalid WebSocket frame: RSV2 and RSV3 must be clear' @@ -582,6 +586,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid opcode 0' @@ -598,6 +603,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid opcode 1' @@ -615,6 +621,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid opcode 2' @@ -632,6 +639,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_EXPECTED_FIN'); assert.strictEqual( err.message, 'Invalid WebSocket frame: FIN must be set' @@ -653,6 +661,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_UNEXPECTED_RSV_1'); assert.strictEqual( err.message, 'Invalid WebSocket frame: RSV1 must be clear' @@ -669,6 +678,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_EXPECTED_FIN'); assert.strictEqual( err.message, 'Invalid WebSocket frame: FIN must be set' @@ -685,6 +695,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_EXPECTED_MASK'); assert.strictEqual( err.message, 'Invalid WebSocket frame: MASK must be set' @@ -701,6 +712,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_UNEXPECTED_MASK'); assert.strictEqual( err.message, 'Invalid WebSocket frame: MASK must be clear' @@ -719,6 +731,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid payload length 126' @@ -735,6 +748,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); assert.strictEqual( err.message, 'Unsupported WebSocket frame: payload length > 2^53 - 1' @@ -756,6 +770,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'WS_ERR_INVALID_UTF8'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid UTF-8 sequence' @@ -778,6 +793,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'WS_ERR_INVALID_UTF8'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid UTF-8 sequence' @@ -799,6 +815,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid payload length 1' @@ -815,6 +832,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_CLOSE_CODE'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid status code 0' @@ -831,6 +849,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof Error); + assert.strictEqual(err.code, 'WS_ERR_INVALID_UTF8'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid UTF-8 sequence' @@ -860,6 +879,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(err[kStatusCode], 1009); done(); @@ -884,6 +904,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(err[kStatusCode], 1009); done(); @@ -913,6 +934,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(err[kStatusCode], 1009); done(); diff --git a/test/websocket.test.js b/test/websocket.test.js index a0557980e..03f984fcb 100644 --- a/test/websocket.test.js +++ b/test/websocket.test.js @@ -435,6 +435,7 @@ describe('WebSocket', () => { ws.on('error', (err) => { assert.ok(err instanceof RangeError); + assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); assert.strictEqual( err.message, 'Invalid WebSocket frame: invalid opcode 5' From 12e9995a05bbe46fe31be905db75abe6b3947536 Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Mon, 14 Jun 2021 20:39:44 +0200 Subject: [PATCH 2/5] Add docs for error codes --- doc/ws.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/doc/ws.md b/doc/ws.md index de62e6756..08c483072 100644 --- a/doc/ws.md +++ b/doc/ws.md @@ -44,6 +44,17 @@ - [websocket.terminate()](#websocketterminate) - [websocket.url](#websocketurl) - [WebSocket.createWebSocketStream(websocket[, options])](#websocketcreatewebsocketstreamwebsocket-options) +- [WS Error Codes](#ws-error-codes) + - [WS_ERR_INVALID_DATA_PAYLOAD_LENGTH](#wserrinvaliddatapayloadlength) + - [WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH](#wserrinvalidcontrolpayloadlength) + - [WS_ERR_INVALID_UTF8](#wserrinvalidutf8) + - [WS_ERR_INVALID_OPCODE](#wserrinvalidopcode) + - [WS_ERR_INVALID_CLOSE_CODE](#wserrinvalidclosecode) + - [WS_ERR_UNEXPECTED_RSV_1](#wserrunexpectedrsv1) + - [WS_ERR_UNEXPECTED_RSV_2_3](#wserrunexpectedrsv23) + - [WS_ERR_EXPECTED_FIN](#wserrexpectedfin) + - [WS_ERR_EXPECTED_MASK](#wserrexpectedmask) + - [WS_ERR_UNEXPECTED_MASK](#wserrunexpectedmask) ## Class: WebSocket.Server @@ -298,7 +309,7 @@ human-readable string explaining why the connection has been closed. - `error` {Error} -Emitted when an error occurs. +Emitted when an error occurs. Errors may have a `.code` property, matching one of the string values defined below under [WS Error Codes](#ws-error-codes). ### Event: 'message' @@ -493,6 +504,50 @@ The URL of the WebSocket server. Server clients don't have this attribute. Returns a `Duplex` stream that allows to use the Node.js streams API on top of a given `WebSocket`. +## WS Error Codes + +Errors emitted by the websocket may have a `.code` property, describing the specific type of error that has occurred: + +### WS_ERR_INVALID_DATA_PAYLOAD_LENGTH + +A data frame with an unexpectedly long payload was received. + +### WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH + +A control frame with an unexpectedly long payload was received. + +### WS_ERR_INVALID_UTF8 + +A text frame was received containing invalid UTF-8 data. + +### WS_ERR_INVALID_OPCODE + +A WebSocket frame was received with an invalid opcode. + +### WS_ERR_INVALID_CLOSE_CODE + +A WebSocket close frame was received with an invalid close code. + +### WS_ERR_UNEXPECTED_RSV_1 + +A WebSocket frame was received with the RSV 1 bit set unexpectedly. + +### WS_ERR_UNEXPECTED_RSV_2_3 + +A WebSocket frame was received with the RSV 2 or 3 bits set unexpectedly. + +### WS_ERR_EXPECTED_FIN + +A WebSocket frame was received with the FIN bit not set when it was expected. + +### WS_ERR_EXPECTED_MASK + +An unmasked WebSocket frame was received by a WebSocket server. + +### WS_ERR_UNEXPECTED_MASK + +A masked WebSocket frame was received by a WebSocket client. + [concurrency-limit]: https://github.com/websockets/ws/issues/1202 [duplex-options]: https://nodejs.org/api/stream.html#stream_new_stream_duplex_options From c8187263547186089e747be5777fa56d2d8311d3 Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Tue, 15 Jun 2021 11:53:16 +0200 Subject: [PATCH 3/5] Change 'invalid data payload' error code to 'unsupported message' It's not technically invalid - the standard does not disallow the length that's used - it's just that this specific endpoint won't accept it. It's also not necessarily a single data payload - the total length might come from a series of separate data frames comprising a single message. --- doc/ws.md | 13 ++++++++----- lib/permessage-deflate.js | 2 +- lib/receiver.js | 6 +++--- test/receiver.test.js | 8 ++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/doc/ws.md b/doc/ws.md index 08c483072..118e4d55c 100644 --- a/doc/ws.md +++ b/doc/ws.md @@ -45,7 +45,7 @@ - [websocket.url](#websocketurl) - [WebSocket.createWebSocketStream(websocket[, options])](#websocketcreatewebsocketstreamwebsocket-options) - [WS Error Codes](#ws-error-codes) - - [WS_ERR_INVALID_DATA_PAYLOAD_LENGTH](#wserrinvaliddatapayloadlength) + - [WS_ERR_UNSUPPORTED_MESSAGE_LENGTH](#wserrunsupporteddatapayloadlength) - [WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH](#wserrinvalidcontrolpayloadlength) - [WS_ERR_INVALID_UTF8](#wserrinvalidutf8) - [WS_ERR_INVALID_OPCODE](#wserrinvalidopcode) @@ -309,7 +309,8 @@ human-readable string explaining why the connection has been closed. - `error` {Error} -Emitted when an error occurs. Errors may have a `.code` property, matching one of the string values defined below under [WS Error Codes](#ws-error-codes). +Emitted when an error occurs. Errors may have a `.code` property, matching one +of the string values defined below under [WS Error Codes](#ws-error-codes). ### Event: 'message' @@ -506,11 +507,13 @@ given `WebSocket`. ## WS Error Codes -Errors emitted by the websocket may have a `.code` property, describing the specific type of error that has occurred: +Errors emitted by the websocket may have a `.code` property, describing the +specific type of error that has occurred: -### WS_ERR_INVALID_DATA_PAYLOAD_LENGTH +### WS_ERR_UNSUPPORTED_MESSAGE_LENGTH -A data frame with an unexpectedly long payload was received. +A message was received with a length longer than the maximum supported length +for this endpoint. ### WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH diff --git a/lib/permessage-deflate.js b/lib/permessage-deflate.js index 8229280f1..ce9178429 100644 --- a/lib/permessage-deflate.js +++ b/lib/permessage-deflate.js @@ -495,7 +495,7 @@ function inflateOnData(chunk) { } this[kError] = new RangeError('Max payload size exceeded'); - this[kError].code = 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'; + this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'; this[kError][kStatusCode] = 1009; this.removeListener('data', inflateOnData); this.reset(); diff --git a/lib/receiver.js b/lib/receiver.js index 848a52561..e32676b1e 100644 --- a/lib/receiver.js +++ b/lib/receiver.js @@ -347,7 +347,7 @@ class Receiver extends Writable { 'Unsupported WebSocket frame: payload length > 2^53 - 1', false, 1009, - 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH' + 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' ); } @@ -371,7 +371,7 @@ class Receiver extends Writable { 'Max payload size exceeded', false, 1009, - 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH' + 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' ); } } @@ -457,7 +457,7 @@ class Receiver extends Writable { 'Max payload size exceeded', false, 1009, - 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH' + 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' ) ); } diff --git a/test/receiver.test.js b/test/receiver.test.js index 54f32b04b..07fded28a 100644 --- a/test/receiver.test.js +++ b/test/receiver.test.js @@ -748,7 +748,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); - assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); + assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'); assert.strictEqual( err.message, 'Unsupported WebSocket frame: payload length > 2^53 - 1' @@ -879,7 +879,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); - assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); + assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'); assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(err[kStatusCode], 1009); done(); @@ -904,7 +904,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); - assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); + assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'); assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(err[kStatusCode], 1009); done(); @@ -934,7 +934,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); - assert.strictEqual(err.code, 'WS_ERR_INVALID_DATA_PAYLOAD_LENGTH'); + assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'); assert.strictEqual(err.message, 'Max payload size exceeded'); assert.strictEqual(err[kStatusCode], 1009); done(); From 11954cc941915f12b1af7321b231abf3d6619ded Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Tue, 15 Jun 2021 14:46:11 +0200 Subject: [PATCH 4/5] Add an separate WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH error code --- doc/ws.md | 11 ++++++++--- lib/receiver.js | 2 +- test/receiver.test.js | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/ws.md b/doc/ws.md index 118e4d55c..7fede7246 100644 --- a/doc/ws.md +++ b/doc/ws.md @@ -512,12 +512,17 @@ specific type of error that has occurred: ### WS_ERR_UNSUPPORTED_MESSAGE_LENGTH -A message was received with a length longer than the maximum supported length -for this endpoint. +A message was received with a length longer than the maximum supported length, +as configured by the `maxPayload` option. + +### WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH + +A data frame was received with a length longer the max supported length (2^53-1, +due to JavaScript language limitations). ### WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH -A control frame with an unexpectedly long payload was received. +A control frame with an invalid payload length was received. ### WS_ERR_INVALID_UTF8 diff --git a/lib/receiver.js b/lib/receiver.js index e32676b1e..7ed992214 100644 --- a/lib/receiver.js +++ b/lib/receiver.js @@ -347,7 +347,7 @@ class Receiver extends Writable { 'Unsupported WebSocket frame: payload length > 2^53 - 1', false, 1009, - 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' + 'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH' ); } diff --git a/test/receiver.test.js b/test/receiver.test.js index 07fded28a..cd5770dfb 100644 --- a/test/receiver.test.js +++ b/test/receiver.test.js @@ -748,7 +748,7 @@ describe('Receiver', () => { receiver.on('error', (err) => { assert.ok(err instanceof RangeError); - assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'); + assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'); assert.strictEqual( err.message, 'Unsupported WebSocket frame: payload length > 2^53 - 1' From 2acd3cb2a2bc29c880736a2587e5d2b702750423 Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Tue, 15 Jun 2021 06:16:13 -0700 Subject: [PATCH 5/5] Update error docs from review suggestions Co-authored-by: Luigi Pinca --- doc/ws.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/ws.md b/doc/ws.md index 7fede7246..7212a1c01 100644 --- a/doc/ws.md +++ b/doc/ws.md @@ -46,6 +46,7 @@ - [WebSocket.createWebSocketStream(websocket[, options])](#websocketcreatewebsocketstreamwebsocket-options) - [WS Error Codes](#ws-error-codes) - [WS_ERR_UNSUPPORTED_MESSAGE_LENGTH](#wserrunsupporteddatapayloadlength) + - [WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH](#wserrunsupporteddatapayloadlength) - [WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH](#wserrinvalidcontrolpayloadlength) - [WS_ERR_INVALID_UTF8](#wserrinvalidutf8) - [WS_ERR_INVALID_OPCODE](#wserrinvalidopcode) @@ -526,7 +527,7 @@ A control frame with an invalid payload length was received. ### WS_ERR_INVALID_UTF8 -A text frame was received containing invalid UTF-8 data. +A text or close frame was received containing invalid UTF-8 data. ### WS_ERR_INVALID_OPCODE @@ -538,11 +539,11 @@ A WebSocket close frame was received with an invalid close code. ### WS_ERR_UNEXPECTED_RSV_1 -A WebSocket frame was received with the RSV 1 bit set unexpectedly. +A WebSocket frame was received with the RSV1 bit set unexpectedly. ### WS_ERR_UNEXPECTED_RSV_2_3 -A WebSocket frame was received with the RSV 2 or 3 bits set unexpectedly. +A WebSocket frame was received with the RSV2 or RSV3 bit set unexpectedly. ### WS_ERR_EXPECTED_FIN