| 145 | } |
| 146 | |
| 147 | private drainBuffer(): void { |
| 148 | for (;;) { |
| 149 | if (this.recvLen < HEADER_SIZE) break; |
| 150 | |
| 151 | // Materialise the flat buffer only when we actually have enough data to inspect. |
| 152 | if (this.recvBuf.length < this.recvLen) { |
| 153 | this.recvBuf = Buffer.concat(this.recvChunks, this.recvLen); |
| 154 | this.recvChunks = [this.recvBuf]; |
| 155 | } |
| 156 | |
| 157 | if ( |
| 158 | this.recvBuf[0] !== 0x33 || |
| 159 | this.recvBuf[1] !== 0xc6 || |
| 160 | this.recvBuf[2] !== 0x00 || |
| 161 | this.recvBuf[3] !== 0x01 |
| 162 | ) { |
| 163 | this.socket.destroy(new Error('ValdiPacket: bad magic')); |
| 164 | break; |
| 165 | } |
| 166 | |
| 167 | const payloadLen = this.recvBuf.readUInt32LE(4); |
| 168 | if (this.recvLen < HEADER_SIZE + payloadLen) break; |
| 169 | |
| 170 | const raw = this.recvBuf.subarray(HEADER_SIZE, HEADER_SIZE + payloadLen).toString('utf8'); |
| 171 | const consumed = HEADER_SIZE + payloadLen; |
| 172 | this.recvBuf = this.recvBuf.subarray(consumed); |
| 173 | this.recvLen -= consumed; |
| 174 | this.recvChunks = this.recvLen > 0 ? [this.recvBuf] : []; |
| 175 | |
| 176 | try { |
| 177 | this.dispatchMessage(JSON.parse(raw) as Record<string, unknown>); |
| 178 | } catch { |
| 179 | // ignore malformed packets |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | private dispatchMessage(msg: Record<string, unknown>): void { |
| 185 | if (msg['request']) { |