mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com> Co-authored-by: Ashcon Partovi <ashcon@partovi.net> Co-authored-by: Electroid <Electroid@users.noreply.github.com> Co-authored-by: Meghan Denny <meghan@bun.sh> Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
80 lines
2.5 KiB
TypeScript
80 lines
2.5 KiB
TypeScript
interface Socket<T = any> {
|
|
data: T;
|
|
write(data: string | Buffer): void;
|
|
}
|
|
|
|
const enum FramerState {
|
|
WaitingForLength,
|
|
WaitingForMessage,
|
|
}
|
|
|
|
let socketFramerMessageLengthBuffer: Buffer;
|
|
export class SocketFramer {
|
|
private state: FramerState = FramerState.WaitingForLength;
|
|
private pendingLength: number = 0;
|
|
private sizeBuffer: Buffer = Buffer.alloc(0);
|
|
private sizeBufferIndex: number = 0;
|
|
private bufferedData: Buffer = Buffer.alloc(0);
|
|
|
|
constructor(private onMessage: (message: string) => void) {
|
|
if (!socketFramerMessageLengthBuffer) {
|
|
socketFramerMessageLengthBuffer = Buffer.alloc(4);
|
|
}
|
|
this.reset();
|
|
}
|
|
|
|
reset(): void {
|
|
this.state = FramerState.WaitingForLength;
|
|
this.bufferedData = Buffer.alloc(0);
|
|
this.sizeBufferIndex = 0;
|
|
this.sizeBuffer = Buffer.alloc(4);
|
|
}
|
|
|
|
send(socket: Socket, data: string): void {
|
|
socketFramerMessageLengthBuffer.writeUInt32BE(data.length, 0);
|
|
socket.write(socketFramerMessageLengthBuffer);
|
|
socket.write(data);
|
|
}
|
|
|
|
onData(socket: Socket, data: Buffer): void {
|
|
this.bufferedData = this.bufferedData.length > 0 ? Buffer.concat([this.bufferedData, data]) : data;
|
|
|
|
let messagesToDeliver: string[] = [];
|
|
|
|
while (this.bufferedData.length > 0) {
|
|
if (this.state === FramerState.WaitingForLength) {
|
|
if (this.sizeBufferIndex + this.bufferedData.length < 4) {
|
|
const remainingBytes = Math.min(4 - this.sizeBufferIndex, this.bufferedData.length);
|
|
this.bufferedData.copy(this.sizeBuffer, this.sizeBufferIndex, 0, remainingBytes);
|
|
this.sizeBufferIndex += remainingBytes;
|
|
this.bufferedData = this.bufferedData.slice(remainingBytes);
|
|
break;
|
|
}
|
|
|
|
const remainingBytes = 4 - this.sizeBufferIndex;
|
|
this.bufferedData.copy(this.sizeBuffer, this.sizeBufferIndex, 0, remainingBytes);
|
|
this.pendingLength = this.sizeBuffer.readUInt32BE(0);
|
|
|
|
this.state = FramerState.WaitingForMessage;
|
|
this.sizeBufferIndex = 0;
|
|
this.bufferedData = this.bufferedData.slice(remainingBytes);
|
|
}
|
|
|
|
if (this.bufferedData.length < this.pendingLength) {
|
|
break;
|
|
}
|
|
|
|
const message = this.bufferedData.toString("utf-8", 0, this.pendingLength);
|
|
this.bufferedData = this.bufferedData.slice(this.pendingLength);
|
|
this.state = FramerState.WaitingForLength;
|
|
this.pendingLength = 0;
|
|
this.sizeBufferIndex = 0;
|
|
messagesToDeliver.push(message);
|
|
}
|
|
|
|
for (const message of messagesToDeliver) {
|
|
this.onMessage(message);
|
|
}
|
|
}
|
|
}
|