Skip to content

Commit

Permalink
Browser client (coder#8)
Browse files Browse the repository at this point in the history
Add browser reference implementation
  • Loading branch information
cmoog authored Jun 4, 2020
1 parent 5cbe721 commit 77b7026
Showing 1 changed file with 99 additions and 0 deletions.
99 changes: 99 additions & 0 deletions browser/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// "wsep" browser client reference implementation

const DELIMITER = '\n'.charCodeAt(0);

// Command describes initialization parameters for a remote command
export interface Command {
command: string;
args?: string[];
tty?: boolean;
uid?: number;
gid?: number;
env?: string[];
working_dir?: string;
}

export type ClientHeader =
| { type: 'start'; command: Command }
| { type: 'stdin' }
| { type: 'close_stdin' }
| { type: 'resize'; cols: number; rows: number };

export type ServerHeader =
| { type: 'stdout' }
| { type: 'stderr' }
| { type: 'pid'; pid: number }
| { type: 'exit_code'; exit_code: number };

export type Header = ClientHeader | ServerHeader;

export const setBinaryType = (ws: WebSocket) => {
ws.binaryType = 'arraybuffer';
};

export const sendStdin = (ws: WebSocket, data: Uint8Array) => {
if (data.byteLength < 1) return;
const msg = joinMessage({ type: 'stdin' }, data);
ws.send(msg.buffer);
};

export const closeStdin = (ws: WebSocket) => {
const msg = joinMessage({ type: 'close_stdin' });
ws.send(msg.buffer);
};

export const startCommand = (ws: WebSocket, command: Command) => {
const msg = joinMessage({ type: 'start', command: command });
ws.send(msg.buffer);
};

export const parseServerMessage = (
ev: MessageEvent
): [ServerHeader, Uint8Array] => {
const [header, body] = splitMessage(ev.data);
return [header as ServerHeader, body];
};

export const resizeTerminal = (
ws: WebSocket,
rows: number,
cols: number
): void => {
const msg = joinMessage({ type: 'resize', cols, rows });
ws.send(msg.buffer);
};

const joinMessage = (header: ClientHeader, body?: Uint8Array): Uint8Array => {
const encodedHeader = new TextEncoder().encode(JSON.stringify(header));
if (body && body.length > 0) {
const tmp = new Uint8Array(encodedHeader.byteLength + 1 + body.byteLength);
tmp.set(encodedHeader, 0);
tmp.set([DELIMITER], encodedHeader.byteLength);
tmp.set(body, encodedHeader.byteLength + 1);
return tmp;
}
return encodedHeader;
};

const splitMessage = (message: ArrayBuffer): [Header, Uint8Array] => {
let array: Uint8Array;
if (typeof message === 'string') {
array = new TextEncoder().encode(message);
} else {
array = new Uint8Array(message);
}

for (let i = 0; i < array.length; i++) {
if (array[i] === DELIMITER) {
const headerText = new TextDecoder().decode(array.slice(0, i));
const header: ServerHeader = JSON.parse(headerText);
const body =
array.length > i + 1
? array.slice(i + 1, array.length)
: new Uint8Array(0);
return [header, body];
}
}

return [JSON.parse(new TextDecoder().decode(array)), new Uint8Array(0)];
};

0 comments on commit 77b7026

Please sign in to comment.