Skip to content

Commit 0906eed

Browse files
authored
Merge pull request #1900 from cjihrig/encoding
fix: support multi-byte characters in WebSocket handler
2 parents 5e0bc07 + f77bffe commit 0906eed

File tree

2 files changed

+54
-23
lines changed

2 files changed

+54
-23
lines changed

src/web-socket-handler.ts

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,11 @@ export class WebSocketHandler implements WebSocketInterface {
5151

5252
public static handleStandardInput(
5353
ws: WebSocket.WebSocket,
54-
stdin: stream.Readable | any,
54+
stdin: stream.Readable,
5555
streamNum: number = 0,
5656
): boolean {
5757
stdin.on('data', (data) => {
58-
const buff = Buffer.alloc(data.length + 1);
59-
buff.writeInt8(streamNum, 0);
60-
if (data instanceof Buffer) {
61-
data.copy(buff, 1);
62-
} else {
63-
buff.write(data, 1);
64-
}
65-
ws.send(buff);
58+
ws.send(copyChunkForWebSocket(streamNum, data, stdin.readableEncoding));
6659
});
6760

6861
stdin.on('end', () => {
@@ -78,16 +71,9 @@ export class WebSocketHandler implements WebSocketInterface {
7871
createWS: () => Promise<WebSocket.WebSocket>,
7972
streamNum: number = 0,
8073
retryCount: number = 3,
74+
encoding?: BufferEncoding | null,
8175
): Promise<WebSocket.WebSocket | null> {
82-
const buff = Buffer.alloc(data.length + 1);
83-
84-
buff.writeInt8(streamNum, 0);
85-
if (data instanceof Buffer) {
86-
data.copy(buff, 1);
87-
} else {
88-
buff.write(data, 1);
89-
}
90-
76+
const buff = copyChunkForWebSocket(streamNum, data, encoding);
9177
let i = 0;
9278
for (; i < retryCount; ++i) {
9379
if (ws !== null && ws.readyState === WebSocket.OPEN) {
@@ -109,7 +95,7 @@ export class WebSocketHandler implements WebSocketInterface {
10995

11096
public static restartableHandleStandardInput(
11197
createWS: () => Promise<WebSocket.WebSocket>,
112-
stdin: stream.Readable | any,
98+
stdin: stream.Readable,
11399
streamNum: number = 0,
114100
retryCount: number = 3,
115101
): () => WebSocket.WebSocket | null {
@@ -122,7 +108,14 @@ export class WebSocketHandler implements WebSocketInterface {
122108

123109
stdin.on('data', (data) => {
124110
queue = queue.then(async () => {
125-
ws = await WebSocketHandler.processData(data, ws, createWS, streamNum, retryCount);
111+
ws = await WebSocketHandler.processData(
112+
data,
113+
ws,
114+
createWS,
115+
streamNum,
116+
retryCount,
117+
stdin.readableEncoding,
118+
);
126119
});
127120
});
128121

@@ -201,3 +194,24 @@ export class WebSocketHandler implements WebSocketInterface {
201194
});
202195
}
203196
}
197+
198+
function copyChunkForWebSocket(
199+
streamNum: number,
200+
chunk: string | Buffer,
201+
encoding?: BufferEncoding | null,
202+
): Buffer {
203+
let buff: Buffer;
204+
205+
if (chunk instanceof Buffer) {
206+
buff = Buffer.alloc(chunk.length + 1);
207+
chunk.copy(buff, 1);
208+
} else {
209+
encoding ??= 'utf-8';
210+
const size = Buffer.byteLength(chunk, encoding);
211+
buff = Buffer.alloc(size + 1);
212+
buff.write(chunk, 1, size, encoding);
213+
}
214+
215+
buff.writeInt8(streamNum, 0);
216+
return buff;
217+
}

src/web-socket-handler_test.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Readable } from 'node:stream';
12
import { promisify } from 'util';
23
import { expect } from 'chai';
34
import WebSocket = require('isomorphic-ws');
@@ -303,14 +304,30 @@ describe('WebSocket', () => {
303304
expect(datum).to.equal(fill);
304305
}
305306
});
307+
it('handles multi-byte characters', () => {
308+
return new Promise((resolve) => {
309+
const stream = new Readable({ read() {} });
310+
const mockWs = {
311+
close() {},
312+
send(data) {
313+
expect(data).to.deep.equal(Buffer.from([0x0f, 0xe2, 0x98, 0x83]));
314+
resolve(undefined);
315+
},
316+
} as WebSocket.WebSocket;
317+
318+
stream.setEncoding('utf8');
319+
stream.push('☃');
320+
WebSocketHandler.handleStandardInput(mockWs, stream, 0x0f);
321+
});
322+
});
306323
});
307324

308325
describe('Restartable Handle Standard Input', () => {
309326
it('should throw on negative retry', () => {
310327
const p = new Promise<WebSocket.WebSocket>(() => {});
311-
expect(() => WebSocketHandler.restartableHandleStandardInput(() => p, null, 0, -1)).to.throw(
312-
"retryCount can't be lower than 0.",
313-
);
328+
expect(() =>
329+
WebSocketHandler.restartableHandleStandardInput(() => p, new Readable({ read() {} }), 0, -1),
330+
).to.throw("retryCount can't be lower than 0.");
314331
});
315332

316333
it('should retry n times', () => {

0 commit comments

Comments
 (0)