Skip to content

Commit

Permalink
feat: websocketstream polyfill
Browse files Browse the repository at this point in the history
  • Loading branch information
Percslol committed Dec 30, 2024
1 parent e9ba8eb commit 8d4b67e
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class ScramjetClient {
constructor(public global: typeof globalThis) {
if (SCRAMJETCLIENT in global) {
console.error(
"attempted to initialize a scramjet cl ient, but one is already loaded - this is very bad"
"attempted to initialize a scramjet client, but one is already loaded - this is very bad"
);
throw new Error();
}
Expand Down
2 changes: 1 addition & 1 deletion src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const isshared = "SharedWorkerGlobalScope" in self;
export const isemulatedsw =
new URL(self.location.href).searchParams.get("dest") === "serviceworker";

dbg.log("scrammin");
dbg.log("initializing scramjet client");
// if it already exists, that means the handlers have probably already been setup by the parent document
if (!(SCRAMJETCLIENT in <Partial<typeof self>>self)) {
loadCodecs();
Expand Down
120 changes: 120 additions & 0 deletions src/client/shared/requests/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,21 @@ type FakeWebSocketState = {
onmessage?: (ev: MessageEvent) => any;
onopen?: (ev: Event) => any;
};
type FakeWebSocketStreamState = {
extensions: string;
protocol: string;
url: string;
barews: BareWebSocket;

opened: any;
closed: any;
readable: ReadableStream;
writable: WritableStream;
};
export default function (client: ScramjetClient, self: typeof globalThis) {
const socketmap: WeakMap<WebSocket, FakeWebSocketState> = new WeakMap();
const socketstreammap: WeakMap<object, FakeWebSocketStreamState> =
new WeakMap();
client.Proxy("WebSocket", {
construct(ctx) {
const fakeWebSocket = new EventTarget() as WebSocket;
Expand Down Expand Up @@ -215,4 +228,111 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
ctx.return(ws.barews.close(ctx.args[0], ctx.args[1]));
},
});

client.Proxy("WebSocketStream", {
construct(ctx) {
const fakeWebSocket = {};
Object.setPrototypeOf(fakeWebSocket, ctx.fn.prototype);
fakeWebSocket.constructor = ctx.fn;

const barews = client.bare.createWebSocket(
ctx.args[0],
ctx.args[1],
null,
{
"User-Agent": self.navigator.userAgent,
Origin: client.url.origin,
}
);
ctx.args[1]?.signal.addEventListener("abort", () => {
barews.close(1000, "");
});
let openResolver, closeResolver;
const state: FakeWebSocketStreamState = {
extensions: "",
protocol: "",
url: ctx.args[0],
barews,

opened: new Promise((resolve) => {
openResolver = resolve;
}),
closed: new Promise((resolve) => {
closeResolver = resolve;
}),
readable: new ReadableStream({
start(controller) {
barews.addEventListener("message", (ev: MessageEvent) => {
const payload = ev.data;
if (typeof payload === "string") {
// DO NOTHING
} else if ("byteLength" in payload) {
Object.setPrototypeOf(payload, ArrayBuffer.prototype);
}
controller.enqueue(payload);
});
},
}),
writable: new WritableStream({
write(chunk) {
barews.send(chunk);
},
}),
};
barews.addEventListener("open", () => {
openResolver({
readable: state.readable,
writable: state.writable,
extensions: state.extensions,
protocol: state.protocol,
});
});
barews.addEventListener("close", (ev: CloseEvent) => {
closeResolver({ code: ev.code, reason: ev.reason });
});

socketstreammap.set(fakeWebSocket, state);
ctx.return(fakeWebSocket);
},
});

client.Trap("WebSocketStream.prototype.closed", {
get(ctx) {
const ws = socketstreammap.get(ctx.this);

return ws.closed;
},
});

client.Trap("WebSocketStream.prototype.opened", {
get(ctx) {
const ws = socketstreammap.get(ctx.this);

return ws.opened;
},
});

client.Trap("WebSocketStream.prototype.url", {
get(ctx) {
const ws = socketstreammap.get(ctx.this);

return ws.url;
},
});

client.Proxy("WebSocketStream.prototype.close", {
apply(ctx) {
const ws = socketstreammap.get(ctx.this);
if (ctx.args[0]) {
if (ctx.args[0].closeCode === undefined) ctx.args[0].closeCode = 1000;
if (ctx.args[0].reason === undefined) ctx.args[0].reason = "";

return ctx.return(
ws.barews.close(ctx.args[0].closeCode, ctx.args[0].reason)
);
}

return ctx.return(ws.barews.close(1000, ""));
},
});
}
8 changes: 2 additions & 6 deletions src/controller/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,11 @@ export class ScramjetController {
$scramjet.config = deepMerge(defaultConfig, config);
}

async init(serviceWorkerPath: string): Promise<ServiceWorkerRegistration> {
async init(): Promise<void> {
loadCodecs();

await this.openIDB();

const reg = await navigator.serviceWorker.register(serviceWorkerPath);
dbg.log("service worker registered");

return reg;
dbg.log("config loaded");
}

createFrame(frame?: HTMLIFrameElement): ScramjetFrame {
Expand Down

0 comments on commit 8d4b67e

Please sign in to comment.