Skip to content

Commit 61fd2e8

Browse files
committed
websocket: improve performance of generate mask
1 parent 5d54543 commit 61fd2e8

File tree

2 files changed

+47
-8
lines changed

2 files changed

+47
-8
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { randomFillSync, randomBytes } from 'node:crypto'
2+
import { bench, group, run } from 'mitata'
3+
4+
const BUFFER_SIZE = 16384
5+
6+
const buf = randomFillSync(Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE)
7+
let bufIdx = 0
8+
9+
function generateMask () {
10+
if (bufIdx + 4 > BUFFER_SIZE) {
11+
bufIdx = 0
12+
randomFillSync(buf, 0, BUFFER_SIZE)
13+
}
14+
return [buf[bufIdx++], buf[bufIdx++], buf[bufIdx++], buf[bufIdx++]]
15+
}
16+
17+
group('generate', () => {
18+
bench('generateMask', () => generateMask())
19+
bench('crypto.randomBytes(4)', () => randomBytes(4))
20+
})
21+
22+
await run()

lib/web/websocket/frame.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,43 @@
22

33
const { maxUnsigned16Bit } = require('./constants')
44

5+
const BUFFER_SIZE = 16386
6+
57
/** @type {import('crypto')} */
68
let crypto
9+
let buffer = null
10+
let bufIdx = 0
11+
712
try {
813
crypto = require('node:crypto')
914
/* c8 ignore next 3 */
1015
} catch {
1116

1217
}
1318

19+
function generateMask () {
20+
if (buffer === null) {
21+
buffer = crypto.randomFillSync(Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE)
22+
}
23+
if (bufIdx + 4 > BUFFER_SIZE) {
24+
bufIdx = 0
25+
crypto.randomFillSync(buffer, 0, BUFFER_SIZE)
26+
}
27+
return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]
28+
}
29+
1430
class WebsocketFrameSend {
1531
/**
1632
* @param {Buffer|undefined} data
1733
*/
1834
constructor (data) {
1935
this.frameData = data
20-
this.maskKey = crypto.randomBytes(4)
2136
}
2237

2338
createFrame (opcode) {
24-
const bodyLength = this.frameData?.byteLength ?? 0
39+
const frameData = this.frameData
40+
const maskKey = generateMask()
41+
const bodyLength = frameData?.byteLength ?? 0
2542

2643
/** @type {number} */
2744
let payloadLength = bodyLength // 0-125
@@ -43,10 +60,10 @@ class WebsocketFrameSend {
4360
buffer[0] = (buffer[0] & 0xF0) + opcode // opcode
4461

4562
/*! ws. MIT License. Einar Otto Stangvik <[email protected]> */
46-
buffer[offset - 4] = this.maskKey[0]
47-
buffer[offset - 3] = this.maskKey[1]
48-
buffer[offset - 2] = this.maskKey[2]
49-
buffer[offset - 1] = this.maskKey[3]
63+
buffer[offset - 4] = maskKey[0]
64+
buffer[offset - 3] = maskKey[1]
65+
buffer[offset - 2] = maskKey[2]
66+
buffer[offset - 1] = maskKey[3]
5067

5168
buffer[1] = payloadLength
5269

@@ -61,8 +78,8 @@ class WebsocketFrameSend {
6178
buffer[1] |= 0x80 // MASK
6279

6380
// mask body
64-
for (let i = 0; i < bodyLength; i++) {
65-
buffer[offset + i] = this.frameData[i] ^ this.maskKey[i % 4]
81+
for (let i = 0; i < bodyLength; ++i) {
82+
buffer[offset + i] = frameData[i] ^ maskKey[i & 3]
6683
}
6784

6885
return buffer

0 commit comments

Comments
 (0)