Skip to content

Commit defb083

Browse files
didinelealmeidxkodiakhq[bot]
authored
fix(WebSocketShard): buffer native zlib decompression payload (#10416)
* fix(WebSocketShard): buffer native zlib decompression payload * refactor: nit Co-authored-by: Almeida <[email protected]> --------- Co-authored-by: Almeida <[email protected]> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent a6de270 commit defb083

File tree

1 file changed

+30
-3
lines changed

1 file changed

+30
-3
lines changed

packages/ws/src/ws/WebSocketShard.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
9898

9999
private zLibSyncInflate: ZlibSync.Inflate | null = null;
100100

101+
/**
102+
* @privateRemarks
103+
*
104+
* Used only for native zlib inflate, zlib-sync buffering is handled by the library itself.
105+
*/
106+
private inflateBuffer: Buffer[] = [];
107+
101108
private readonly textDecoder = new TextDecoder();
102109

103110
private replayedEvents = 0;
@@ -198,11 +205,17 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
198205
case CompressionMethod.ZlibNative: {
199206
const zlib = await getNativeZlib();
200207
if (zlib) {
208+
this.inflateBuffer = [];
209+
201210
const inflate = zlib.createInflate({
202211
chunkSize: 65_535,
203212
flush: zlib.constants.Z_SYNC_FLUSH,
204213
});
205214

215+
inflate.on('data', (chunk) => {
216+
this.inflateBuffer.push(chunk);
217+
});
218+
206219
inflate.on('error', (error) => {
207220
this.emit(WebSocketShardEvents.Error, error);
208221
});
@@ -627,14 +640,28 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
627640
decompressable.at(-1) === 0xff;
628641

629642
if (this.nativeInflate) {
630-
this.nativeInflate.write(decompressable, 'binary');
643+
const doneWriting = new Promise<void>((resolve) => {
644+
// eslint-disable-next-line promise/prefer-await-to-callbacks
645+
this.nativeInflate!.write(decompressable, 'binary', (error) => {
646+
if (error) {
647+
this.emit(WebSocketShardEvents.Error, error);
648+
}
649+
650+
resolve();
651+
});
652+
});
631653

632654
if (!flush) {
633655
return null;
634656
}
635657

636-
const [result] = await once(this.nativeInflate, 'data');
637-
return this.parseInflateResult(result);
658+
// This way we're ensuring the latest write has flushed and our buffer is ready
659+
await doneWriting;
660+
661+
const result = this.parseInflateResult(Buffer.concat(this.inflateBuffer));
662+
this.inflateBuffer = [];
663+
664+
return result;
638665
} else if (this.zLibSyncInflate) {
639666
const zLibSync = (await getZlibSync())!;
640667
this.zLibSyncInflate.push(Buffer.from(decompressable), flush ? zLibSync.Z_SYNC_FLUSH : zLibSync.Z_NO_FLUSH);

0 commit comments

Comments
 (0)