From b79a4e1b85ba65c479c5608b653ff664312e883a Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:32:21 +0900 Subject: [PATCH 1/6] websocket: add fast-path for string input --- lib/web/websocket/frame.js | 43 +++++++++++++++++++++++++++++++++++++ lib/web/websocket/sender.js | 14 ++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index fe54646cc7d..e85303b1376 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -89,6 +89,49 @@ class WebsocketFrameSend { return buffer } + + /** + * @param {string | Uint8Array} body + */ + static createFastTextFrame (body) { + const maskKey = generateMask() + + const buffer = typeof body === 'string' ? Buffer.from(body) : body + const bodyLength = buffer.length + + // mask body + for (let i = 0; i < bodyLength; ++i) { + buffer[i] ^= maskKey[i & 3] + } + + let payloadLength = bodyLength + let offset = 6 + + if (bodyLength > maxUnsigned16Bit) { + offset += 8 // payload length is next 8 bytes + payloadLength = 127 + } else if (bodyLength > 125) { + offset += 2 // payload length is next 2 bytes + payloadLength = 126 + } + const head = Buffer.allocUnsafeSlow(offset) + + head[0] = 0x80 /* FIN */ | 0x1 /* opcode TEXT */ + head[1] = payloadLength + head[offset - 4] = maskKey[0] + head[offset - 3] = maskKey[1] + head[offset - 2] = maskKey[2] + head[offset - 1] = maskKey[3] + + if (payloadLength === 126) { + head.writeUInt16BE(bodyLength, 2) + } else if (payloadLength === 127) { + head[2] = head[3] = 0 + head.writeUIntBE(bodyLength, 4, 6) + } + + return [head, buffer] + } } module.exports = { diff --git a/lib/web/websocket/sender.js b/lib/web/websocket/sender.js index 1b1468d4ab9..a5c761c589a 100644 --- a/lib/web/websocket/sender.js +++ b/lib/web/websocket/sender.js @@ -36,8 +36,18 @@ class SendQueue { if (hint !== sendHints.blob) { const frame = createFrame(item, hint) if (!this.#running) { - // fast-path - this.#socket.write(frame, cb) + // TODO(@tsctx): support fast-path for string on running + if (hint === sendHints.string) { + // special fast-path for string + const { 0: head, 1: body } = WebsocketFrameSend.createFastTextFrame(item) + this.#socket.cork() + this.#socket.write(head) + this.#socket.write(body, cb) + this.#socket.uncork() + } else { + // direct writing + this.#socket.write(createFrame(item, hint), cb) + } } else { /** @type {SendQueueNode} */ const node = { From 79b37736acab4487b8ed88a45b0d997cd25cb97e Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:37:57 +0900 Subject: [PATCH 2/6] fix copy miss --- lib/web/websocket/sender.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/websocket/sender.js b/lib/web/websocket/sender.js index a5c761c589a..e3d3470b196 100644 --- a/lib/web/websocket/sender.js +++ b/lib/web/websocket/sender.js @@ -34,7 +34,6 @@ class SendQueue { add (item, cb, hint) { if (hint !== sendHints.blob) { - const frame = createFrame(item, hint) if (!this.#running) { // TODO(@tsctx): support fast-path for string on running if (hint === sendHints.string) { @@ -53,7 +52,7 @@ class SendQueue { const node = { promise: null, callback: cb, - frame + frame: createFrame(item, hint) } this.#queue.push(node) } From 577ac48ca0dcef6e4b59bfabdcd25a03ac64ae2b Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:51:36 +0900 Subject: [PATCH 3/6] Update frame.js --- lib/web/websocket/frame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index e85303b1376..a37caae0bf4 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -117,7 +117,7 @@ class WebsocketFrameSend { const head = Buffer.allocUnsafeSlow(offset) head[0] = 0x80 /* FIN */ | 0x1 /* opcode TEXT */ - head[1] = payloadLength + head[1] = payloadLength | 0x80 /* MASK */ head[offset - 4] = maskKey[0] head[offset - 3] = maskKey[1] head[offset - 2] = maskKey[2] From 54f78f7198ffb9c117a5e988e6a3db85e9836406 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 7 Jul 2024 05:51:57 +0900 Subject: [PATCH 4/6] Update frame.js --- lib/web/websocket/frame.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index a37caae0bf4..88c7abfd73b 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -1,6 +1,6 @@ 'use strict' -const { maxUnsigned16Bit } = require('./constants') +const { maxUnsigned16Bit, opcodes } = require('./constants') const BUFFER_SIZE = 16386 @@ -116,7 +116,7 @@ class WebsocketFrameSend { } const head = Buffer.allocUnsafeSlow(offset) - head[0] = 0x80 /* FIN */ | 0x1 /* opcode TEXT */ + head[0] = 0x80 /* FIN */ | opcodes.TEXT /* opcode TEXT */ head[1] = payloadLength | 0x80 /* MASK */ head[offset - 4] = maskKey[0] head[offset - 3] = maskKey[1] From 433d3c31fc2442a49ae411469ae0683df6babe12 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Mon, 8 Jul 2024 12:12:06 +0900 Subject: [PATCH 5/6] Update frame.js --- lib/web/websocket/frame.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index 163a40a38c1..e773b33e1a6 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -91,12 +91,11 @@ class WebsocketFrameSend { } /** - * @param {string | Uint8Array} body + * @param {Uint8Array} buffer */ - static createFastTextFrame (body) { + static createFastTextFrame (buffer) { const maskKey = generateMask() - const buffer = typeof body === 'string' ? Buffer.from(body) : body const bodyLength = buffer.length // mask body From e07d1f4e483cf7f0bc7af58db39d75537a7245be Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Mon, 8 Jul 2024 12:40:57 +0900 Subject: [PATCH 6/6] Update sender.js Co-authored-by: Khafra --- lib/web/websocket/sender.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/websocket/sender.js b/lib/web/websocket/sender.js index a49356765a7..c647bf629d7 100644 --- a/lib/web/websocket/sender.js +++ b/lib/web/websocket/sender.js @@ -33,7 +33,7 @@ class SendQueue { if (hint !== sendHints.blob) { if (!this.#running) { // TODO(@tsctx): support fast-path for string on running - if (hint === sendHints.string) { + if (hint === sendHints.text) { // special fast-path for string const { 0: head, 1: body } = WebsocketFrameSend.createFastTextFrame(item) this.#socket.cork()