Skip to content

Commit

Permalink
websocket: improve parseCloseBody
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzlopak committed Apr 15, 2024
1 parent 48af032 commit e5a9114
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 30 deletions.
28 changes: 16 additions & 12 deletions lib/web/websocket/receiver.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class ByteParser extends Writable {
// sending a Close frame in response, the endpoint typically echos the
// status code it received.)
let body = emptyBuffer
if (this.#info.closeInfo.code) {
if (this.#info.closeInfo?.code) {
body = Buffer.allocUnsafe(2)
body.writeUInt16BE(this.#info.closeInfo.code, 0)
}
Expand Down Expand Up @@ -298,26 +298,30 @@ class ByteParser extends Writable {
// defined as the status code (Section 7.4) contained in the first Close
// control frame received by the application
code = data.readUInt16BE(0)
if (!isValidStatusCode(code)) {
return null
}
} else {
return null
}

// https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6
/** @type {Buffer} */
let reason = data.subarray(2)
/** @type {number} */
let reasonOffset = 2

// Remove BOM
if (reason[0] === 0xEF && reason[1] === 0xBB && reason[2] === 0xBF) {
reason = reason.subarray(3)
// Detect BOM and ignore it
if (data.length >= 5 && data[2] === 0xEF && data[3] === 0xBB && data[4] === 0xBF) {
reasonOffset = 5
}

if (code !== undefined && !isValidStatusCode(code)) {
return null
if (data.length - reasonOffset < 1) {
return { code, reason: '' }
}

let reason = ''
try {
reason = utf8Decode(reason)
} catch {
return null
}
reason = utf8Decode(data.subarray(reasonOffset))
} catch { }

return { code, reason }
}
Expand Down
65 changes: 47 additions & 18 deletions test/websocket/receive.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
'use strict'

const { tspl } = require('@matteo.collina/tspl')
const { test } = require('node:test')
const assert = require('node:assert')
const { WebSocketServer } = require('ws')
const { WebSocket } = require('../..')

test('Receiving a frame with a payload length > 2^31-1 bytes', () => {
test('Receiving a frame with a payload length > 2^31-1 bytes', async (t) => {
t = tspl(t, { plan: 1 })

const server = new WebSocketServer({ port: 0 })

server.on('connection', (ws) => {
Expand All @@ -16,19 +18,20 @@ test('Receiving a frame with a payload length > 2^31-1 bytes', () => {

const ws = new WebSocket(`ws://localhost:${server.address().port}`)

return new Promise((resolve, reject) => {
ws.onmessage = reject
ws.onmessage = t.fail

ws.addEventListener('error', (event) => {
assert.ok(event.error instanceof Error) // error event is emitted
ws.close()
server.close()
resolve()
})
ws.addEventListener('error', (event) => {
ws.close()
server.close()
t.ok(event.error instanceof Error) // error event is emitted
})

await t.completed
})

test('Receiving an ArrayBuffer', () => {
test('Receiving an ArrayBuffer', async (t) => {
t = tspl(t, { plan: 3 })

const server = new WebSocketServer({ port: 0 })

server.on('connection', (ws) => {
Expand All @@ -43,18 +46,44 @@ test('Receiving an ArrayBuffer', () => {

ws.addEventListener('open', () => {
ws.binaryType = 'what'
assert.equal(ws.binaryType, 'blob')
t.strictEqual(ws.binaryType, 'blob')

ws.binaryType = 'arraybuffer' // <--
ws.send('Hello')
})

return new Promise((resolve) => {
ws.addEventListener('message', ({ data }) => {
assert.ok(data instanceof ArrayBuffer)
assert.deepStrictEqual(Buffer.from(data), Buffer.from('Hello'))
server.close()
resolve()
ws.addEventListener('message', ({ data }) => {
t.ok(data instanceof ArrayBuffer)
t.deepStrictEqual(Buffer.from(data), Buffer.from('Hello'))
server.close()
})

await t.completed
})

test('Receiving a close reason', async (t) => {
t = tspl(t, { plan: 1 })

const server = new WebSocketServer({ port: 0 })

server.on('connection', (ws) => {
ws.on('message', (data, isBinary) => {
ws.send(data, { binary: true })

ws.close(1000, Buffer.from('\uFEFFGood Bye!'))
})
})

const ws = new WebSocket(`ws://localhost:${server.address().port}`)

ws.addEventListener('open', () => {
ws.send('Hello')
})

ws.addEventListener('close', ({ reason }) => {
t.strictEqual(reason, 'Good Bye!')
server.close()
})

await t.completed
})

0 comments on commit e5a9114

Please sign in to comment.