File size: 4,488 Bytes
bee6636 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
import { unrewriteUrl } from "../shared/rewriters/url";
import { ScramjetClient } from "./client";
export class ScramjetServiceWorkerRuntime {
recvport: MessagePort;
constructor(public client: ScramjetClient) {
// @ts-ignore
self.onconnect = (cevent: MessageEvent) => {
const port = cevent.ports[0];
dbg.log("sw", "connected");
port.addEventListener("message", (event) => {
console.log("sw", event.data);
if ("scramjet$type" in event.data) {
if (event.data.scramjet$type === "init") {
this.recvport = event.data.scramjet$port;
this.recvport.postMessage({ scramjet$type: "init" });
} else {
handleMessage.call(this, client, event.data);
}
}
});
port.start();
};
}
hook() {
// @ts-ignore
this.client.global.registration = {
// TODO IMPLEMENT SCOPES
scope: this.client.url.href,
active: {
scriptURL: this.client.url.href,
state: "activated",
onstatechange: null,
onerror: null,
postMessage: () => {},
addEventListener: () => {},
removeEventListener: () => {},
dispatchEvent: (_e: Event): boolean => {
return false;
},
},
showNotification: async () => {},
unregister: async () => true,
//@ts-ignore
update: async () => {},
installing: null,
waiting: null,
};
// @ts-ignore
this.client.global.ServiceWorkerGlobalScope = this.client.global;
}
}
function handleMessage(
this: ScramjetServiceWorkerRuntime,
client: ScramjetClient,
data: MessageW2R
) {
const port = this.recvport;
const type = data.scramjet$type;
const token = data.scramjet$token;
const handlers = client.eventcallbacks.get(self);
if (type === "fetch") {
dbg.log("ee", data);
const fetchhandlers = handlers.filter((event) => event.event === "fetch");
if (!fetchhandlers) return;
for (const handler of fetchhandlers) {
const request = data.scramjet$request;
const Request = client.natives["Request"];
const fakeRequest = new Request(unrewriteUrl(request.url), {
body: request.body,
headers: new Headers(request.headers),
method: request.method,
mode: "same-origin",
});
Object.defineProperty(fakeRequest, "destination", {
value: request.destinitation,
});
// TODO: clean up, maybe put into a class
const fakeFetchEvent: any = new Event("fetch");
fakeFetchEvent.request = fakeRequest;
let responded = false;
fakeFetchEvent.respondWith = (response: Response | Promise<Response>) => {
responded = true;
(async () => {
response = await response;
const message: MessageR2W = {
scramjet$type: "fetch",
scramjet$token: token,
scramjet$response: {
body: response.body,
headers: Array.from(response.headers.entries()),
status: response.status,
statusText: response.statusText,
},
};
dbg.log("sw", "responding", message);
port.postMessage(message, [response.body]);
})();
};
dbg.log("to fn", fakeFetchEvent);
handler.proxiedCallback(trustEvent(fakeFetchEvent));
if (!responded) {
console.log("sw", "no response");
port.postMessage({
scramjet$type: "fetch",
scramjet$token: token,
scramjet$response: false,
});
}
}
}
}
function trustEvent(event: Event): Event {
return new Proxy(event, {
get(target, prop, _reciever) {
if (prop === "isTrusted") return true;
return Reflect.get(target, prop);
},
});
}
export type TransferrableResponse = {
body: ReadableStream;
headers: [string, string][];
status: number;
statusText: string;
};
export type TransferrableRequest = {
body: ReadableStream;
headers: [string, string][];
destinitation: RequestDestination;
method: Request["method"];
mode: Request["mode"];
url: string;
};
type FetchResponseMessage = {
scramjet$type: "fetch";
scramjet$response: TransferrableResponse;
};
type FetchRequestMessage = {
scramjet$type: "fetch";
scramjet$request: TransferrableRequest;
};
// r2w = runtime to (service) worker
type MessageTypeR2W = FetchResponseMessage;
type MessageTypeW2R = FetchRequestMessage;
type MessageCommon = {
scramjet$type: string;
scramjet$token: number;
};
export type MessageR2W = MessageCommon & MessageTypeR2W;
export type MessageW2R = MessageCommon &
MessageTypeW2R & { scramjet$port?: MessagePort };
|