open-collaboration-protocol
Version:
Open Collaboration Protocol implementation, part of the Open Collaboration Tools project
74 lines (60 loc) • 2.69 kB
text/typescript
// ******************************************************************************
// Copyright 2024 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.
// ******************************************************************************
import { Emitter, Event } from '../utils/event.js';
import { Deferred } from '../utils/promise.js';
import { MessageTransport, MessageTransportProvider } from './transport.js';
export interface WebSocketTransportProvider extends MessageTransportProvider {
Constructor: typeof WebSocket;
}
export const WebSocketTransportProvider: WebSocketTransportProvider = {
id: 'websocket',
createTransport: (url, headers) => {
if (url.startsWith('https')) {
url = url.replace('https', 'wss');
} else if (url.startsWith('http')) {
url = url.replace('http', 'ws');
}
if (url.endsWith('/')) {
url = url.slice(0, -1);
}
const query = Object.entries(headers).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&');
const socket = new WebSocketTransportProvider.Constructor(url + '/websocket' + (query ? '?' + query : ''));
socket.binaryType = 'arraybuffer';
const transport = new WebSocketTransport(socket);
return transport;
},
Constructor: typeof WebSocket === 'undefined' ? undefined! : WebSocket
};
export class WebSocketTransport implements MessageTransport {
readonly id = 'websocket';
private onDisconnectEmitter = new Emitter<void>();
private onErrorEmitter = new Emitter<string>();
private ready = new Deferred();
get onReconnect(): Event<void> {
return Event.None;
}
get onDisconnect(): Event<void> {
return this.onDisconnectEmitter.event;
}
get onError(): Event<string> {
return this.onErrorEmitter.event;
}
constructor(protected socket: WebSocket) {
this.socket.onclose = () => this.onDisconnectEmitter.fire();
this.socket.onerror = () => this.onErrorEmitter.fire('Websocket connection closed unexpectedly.');
this.socket.onopen = () => this.ready.resolve();
}
async write(data: Uint8Array): Promise<void> {
await this.ready.promise.then(() => this.socket.send(data));
}
read(cb: (data: Uint8Array) => void): void {
this.socket.onmessage = event => cb(event.data);
}
dispose(): void {
this.onDisconnectEmitter.dispose();
this.socket.close();
}
}