websocket support

This commit is contained in:
Toshit Chawda 2024-07-07 14:10:19 -07:00
parent 5355f49957
commit e2b85c3d99
No known key found for this signature in database
GPG key ID: 91480ED99E2B3D9D
3 changed files with 298 additions and 19 deletions

View file

@ -13,13 +13,20 @@ function tryGetPort(client: SWClient): Promise<MessagePort> {
}
export type WorkerMessage = {
type: "fetch" | "set",
type: "fetch" | "websocket" | "set",
fetch?: {
remote: string,
method: string,
body: ReadableStream | null,
headers: BareHeaders,
},
fetchBody?: ReadableStream,
websocket?: {
url: string,
origin: string,
protocols: string[],
requestHeaders: BareHeaders,
},
websocketChannel?: MessagePort,
client?: string,
};
@ -29,21 +36,32 @@ export type WorkerRequest = {
}
export type WorkerResponse = {
type: "fetch" | "set" | "error",
type: "fetch" | "websocket" | "set" | "error",
fetch?: TransferrableResponse,
error?: Error,
}
type BroadcastMessage = {
type: "getPath" | "path",
path?: string,
}
export class WorkerConnection {
channel: BroadcastChannel;
port: MessagePort | Promise<MessagePort>;
constructor(workerPath?: string) {
this.channel = new BroadcastChannel("bare-mux");
// @ts-expect-error
if (self.clients) {
// running in a ServiceWorker
// ask a window for the worker port
// @ts-expect-error
const clients: Promise<SWClient[]> = self.clients.matchAll({ type: "window", includeUncontrolled: true });
this.port = clients.then(clients => Promise.any(clients.map((x: SWClient) => tryGetPort(x))));
} else if (workerPath && SharedWorker) {
// running in a window, was passed a workerPath
// create the SharedWorker and help other bare-mux clients get the workerPath
navigator.serviceWorker.addEventListener("message", event => {
if (event.data.type === "getPort" && event.data.port) {
const worker = new SharedWorker(workerPath);
@ -51,18 +69,41 @@ export class WorkerConnection {
}
});
this.channel.onmessage = (event: MessageEvent) => {
if (event.data.type === "getPath") {
this.channel.postMessage(<BroadcastMessage>{ type: "path", path: workerPath });
}
}
const worker = new SharedWorker(workerPath, "bare-mux-worker");
this.port = worker.port;
} else if (SharedWorker) {
// running in a window, was not passed a workerPath
// ask other bare-mux clients for the workerPath
this.port = new Promise(resolve => {
this.channel.onmessage = (event: MessageEvent) => {
if (event.data.type === "path") {
const worker = new SharedWorker(event.data.path, "bare-mux-worker");
this.channel.onmessage = (event: MessageEvent) => {
if (event.data.type === "getPath") {
this.channel.postMessage(<BroadcastMessage>{ type: "path", path: event.data.path });
}
}
resolve(worker.port);
}
}
this.channel.postMessage(<BroadcastMessage>{ type: "getPath" });
});
} else {
throw new Error("workerPath was not passed or SharedWorker does not exist and am not running in a Service Worker.");
// SharedWorker does not exist
throw new Error("Unable to get a channel to the SharedWorker.");
}
}
async sendMessage(message: WorkerMessage): Promise<WorkerResponse> {
async sendMessage(message: WorkerMessage, transferable?: Transferable[]): Promise<WorkerResponse> {
if (this.port instanceof Promise) this.port = await this.port;
let channel = new MessageChannel();
let toTransfer: Transferable[] = [channel.port2];
if (message.fetch && message.fetch.body) toTransfer.push(message.fetch.body);
let toTransfer: Transferable[] = [channel.port2, ...(transferable || [])];
this.port.postMessage(<WorkerRequest>{ message: message, port: channel.port2 }, toTransfer);