Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 43 additions & 6 deletions packages/compass-web/polyfills/net/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { Duplex } from 'stream';
* used when running compass-web in a local sandbox, mms has their own
* implementation
*/
enum MESSAGE_TYPE {
JSON = 0x01,
BINARY = 0x02,
}

class Socket extends Duplex {
private _ws: WebSocket | null = null;
constructor() {
Expand Down Expand Up @@ -36,7 +41,7 @@ class Socket extends Duplex {
ok: 1,
});
setTimeout(() => {
this._ws?.send(connectMsg);
this._ws?.send(this.encodeStringMessageWithTypeByte(connectMsg));
});
},
{ once: true }
Expand All @@ -49,10 +54,11 @@ class Socket extends Duplex {
});
this._ws.addEventListener(
'message',
({ data }: MessageEvent<string | ArrayBuffer>) => {
if (typeof data === 'string') {
({ data }: MessageEvent<ArrayBuffer>) => {
const dataView = new Uint8Array(data);
if (dataView[0] === 0x01) {
try {
const res = JSON.parse(data) as { preMessageOk: 1 };
const res = this.decodeMessageWithTypeByte(dataView);
if (res.preMessageOk) {
setTimeout(() => {
this.emit(options.tls ? 'secureConnect' : 'connect');
Expand All @@ -64,7 +70,7 @@ class Socket extends Duplex {
}
} else {
setTimeout(() => {
this.emit('data', Buffer.from(data));
this.emit('data', this.decodeMessageWithTypeByte(dataView));
});
}
}
Expand All @@ -75,7 +81,7 @@ class Socket extends Duplex {
// noop
}
_write(chunk: ArrayBufferLike, _encoding: BufferEncoding, cb: () => void) {
this._ws?.send(chunk);
this._ws?.send(this.encodeBinaryMessageWithTypeByte(new Uint8Array(chunk)));
setTimeout(() => {
cb();
});
Expand Down Expand Up @@ -110,6 +116,37 @@ class Socket extends Duplex {
setNoDelay() {
return this;
}

encodeStringMessageWithTypeByte(message: string) {
const utf8Encoder = new TextEncoder();
const utf8Array = utf8Encoder.encode(message);
return this.encodeMessageWithTypeByte(utf8Array, MESSAGE_TYPE.JSON);
}

encodeBinaryMessageWithTypeByte(message: Uint8Array) {
return this.encodeMessageWithTypeByte(message, MESSAGE_TYPE.BINARY);
}

encodeMessageWithTypeByte(message: Uint8Array, type: MESSAGE_TYPE) {
const encoded = new Uint8Array(message.length + 1);
encoded[0] = type;
encoded.set(message, 1);
return encoded;
}

decodeMessageWithTypeByte(message: Uint8Array) {
const typeByte = message[0];
if (typeByte === MESSAGE_TYPE.JSON) {
const jsonBytes = message.subarray(1);
const textDecoder = new TextDecoder('utf-8');
const jsonStr = textDecoder.decode(jsonBytes);
return JSON.parse(jsonStr);
} else if (typeByte === MESSAGE_TYPE.BINARY) {
return message.subarray(1);
} else {
console.error('message does not have valid type byte "%s":', message);
Copy link
Member

@Anemy Anemy Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check script for this package is failing because of the no-console lint rule:

error  Unexpected console statement  no-console

To try it locally you can run npm run check in the packages/compass-web folder.
If these are useful we could add eslint ignores. Do we want to know when this happens though? Would it be worth doing a more prominent error here so folks run into it?

Also just noticed is that : at the end of the message extra?

There's another of these in ws-proxy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it's important enough to leave since it would mean the protocol really screwed up OR some very unexpected type of message is coming through the pipeline. I'll add // eslint-disable-next-line no-console. And thanks for the : catch! Removing

}
}
}

export { isIPv4, isIPv6 } from 'is-ip';
Expand Down
46 changes: 40 additions & 6 deletions packages/compass-web/scripts/ws-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ function createWebSocketProxy(port = 1337, logger = console) {
});
ws.on('message', async (data) => {
if (socket) {
socket.write(data, 'binary');
socket.write(decodeMessageWithTypeByte(data), 'binary');
} else {
// First message before socket is created is with connection info
const { tls: useSecureConnection, ...connectOptions } = JSON.parse(
data.toString()
);
const { tls: useSecureConnection, ...connectOptions } =
decodeMessageWithTypeByte(data);

logger.log(
'setting up new%s connection to %s:%s',
useSecureConnection ? ' secure' : '',
Expand Down Expand Up @@ -61,10 +61,13 @@ function createWebSocketProxy(port = 1337, logger = console) {
connectOptions.port
);
socket.setTimeout(0);
ws.send(JSON.stringify({ preMessageOk: 1 }));
const encoded = encodeStringMessageWithTypeByte(
JSON.stringify({ preMessageOk: 1 })
);
ws.send(encoded);
});
socket.on('data', async (data) => {
ws.send(data);
ws.send(encodeBinaryMessageWithTypeByte(data));
});
}
});
Expand All @@ -73,4 +76,35 @@ function createWebSocketProxy(port = 1337, logger = console) {
return wsServer;
}

function encodeStringMessageWithTypeByte(message) {
const utf8Encoder = new TextEncoder();
const utf8Array = utf8Encoder.encode(message);
return encodeMessageWithTypeByte(utf8Array, 0x01);
}

function encodeBinaryMessageWithTypeByte(message) {
return encodeMessageWithTypeByte(message, 0x02);
}

function encodeMessageWithTypeByte(message, type) {
const encoded = new Uint8Array(message.length + 1);
encoded[0] = type;
encoded.set(message, 1);
return encoded;
}

function decodeMessageWithTypeByte(message) {
const typeByte = message[0];
if (typeByte === 0x01) {
const jsonBytes = message.subarray(1);
const textDecoder = new TextDecoder('utf-8');
const jsonStr = textDecoder.decode(jsonBytes);
return JSON.parse(jsonStr);
} else if (typeByte === 0x02) {
return message.subarray(1);
} else {
console.error('message does not have valid type byte "%s":', message);
}
}

module.exports = { createWebSocketProxy };
Loading