mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-13 06:20:02 -04:00
feat: websocketstream polyfill
This commit is contained in:
parent
e9ba8eb952
commit
8d4b67e457
4 changed files with 124 additions and 8 deletions
|
@ -97,7 +97,7 @@ export class ScramjetClient {
|
||||||
constructor(public global: typeof globalThis) {
|
constructor(public global: typeof globalThis) {
|
||||||
if (SCRAMJETCLIENT in global) {
|
if (SCRAMJETCLIENT in global) {
|
||||||
console.error(
|
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();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const isshared = "SharedWorkerGlobalScope" in self;
|
||||||
export const isemulatedsw =
|
export const isemulatedsw =
|
||||||
new URL(self.location.href).searchParams.get("dest") === "serviceworker";
|
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 it already exists, that means the handlers have probably already been setup by the parent document
|
||||||
if (!(SCRAMJETCLIENT in <Partial<typeof self>>self)) {
|
if (!(SCRAMJETCLIENT in <Partial<typeof self>>self)) {
|
||||||
loadCodecs();
|
loadCodecs();
|
||||||
|
|
|
@ -13,8 +13,21 @@ type FakeWebSocketState = {
|
||||||
onmessage?: (ev: MessageEvent) => any;
|
onmessage?: (ev: MessageEvent) => any;
|
||||||
onopen?: (ev: Event) => 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) {
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
const socketmap: WeakMap<WebSocket, FakeWebSocketState> = new WeakMap();
|
const socketmap: WeakMap<WebSocket, FakeWebSocketState> = new WeakMap();
|
||||||
|
const socketstreammap: WeakMap<object, FakeWebSocketStreamState> =
|
||||||
|
new WeakMap();
|
||||||
client.Proxy("WebSocket", {
|
client.Proxy("WebSocket", {
|
||||||
construct(ctx) {
|
construct(ctx) {
|
||||||
const fakeWebSocket = new EventTarget() as WebSocket;
|
const fakeWebSocket = new EventTarget() as WebSocket;
|
||||||
|
@ -215,4 +228,111 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
ctx.return(ws.barews.close(ctx.args[0], ctx.args[1]));
|
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, ""));
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,15 +59,11 @@ export class ScramjetController {
|
||||||
$scramjet.config = deepMerge(defaultConfig, config);
|
$scramjet.config = deepMerge(defaultConfig, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(serviceWorkerPath: string): Promise<ServiceWorkerRegistration> {
|
async init(): Promise<void> {
|
||||||
loadCodecs();
|
loadCodecs();
|
||||||
|
|
||||||
await this.openIDB();
|
await this.openIDB();
|
||||||
|
dbg.log("config loaded");
|
||||||
const reg = await navigator.serviceWorker.register(serviceWorkerPath);
|
|
||||||
dbg.log("service worker registered");
|
|
||||||
|
|
||||||
return reg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createFrame(frame?: HTMLIFrameElement): ScramjetFrame {
|
createFrame(frame?: HTMLIFrameElement): ScramjetFrame {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue