diff --git a/.eslintrc.json b/.eslintrc.json index cb531b2..40a781b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -25,6 +25,6 @@ "no-unreachable": "warn", "no-undef": "off", "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/ban-ts-comment": "off" } } diff --git a/README.md b/README.md index e0ad2e8..a39ee14 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ --- +npm version Scramjet is an experimental web proxy that aims to be the successor to Ultraviolet. diff --git a/rewriter/test.js b/rewriter/test.js index 9326d2c..ad44f54 100644 --- a/rewriter/test.js +++ b/rewriter/test.js @@ -1,10 +1,8 @@ +window.location.href = "http://example.com"; -window.location.href = "http://example.com" +console.log(top.window.aaa); +consle.log(globalThis["win" + "dow"]); +globalThis.eval(".."); -console.log(top.window.aaa) -consle.log(globalThis["win" + "dow"]) - -globalThis.eval("..") - -let ref = { b: this.top.window, c: globalThis["win" + "dow"] } +let ref = { b: this.top.window, c: globalThis["win" + "dow"] }; diff --git a/rspack.config.js b/rspack.config.js index 56418e7..ba6d164 100644 --- a/rspack.config.js +++ b/rspack.config.js @@ -51,5 +51,5 @@ export default defineConfig({ // }) ], watch: true, - target: "webworker" + target: "webworker", }); diff --git a/src/client/element.ts b/src/client/element.ts index e2fd16b..cc7162f 100644 --- a/src/client/element.ts +++ b/src/client/element.ts @@ -34,23 +34,25 @@ for (const attr of attrs) { const descriptor = Object.getOwnPropertyDescriptor(element.prototype, attr); Object.defineProperty(element.prototype, attr, { get() { - if (/src|href|data|action|formaction/.test(attr)) { + if (["src", "data", "href", "action", "formaction"].includes(attr)) { return decodeUrl(descriptor.get.call(this)); } - if (this.__origattrs[attr]) { - return this.__origattrs[attr]; + if (this.$origattrs[attr]) { + return this.$origattrs[attr]; } return descriptor.get.call(this); }, set(value) { - this.__origattrs[attr] = value; + this.$origattrs[attr] = value; if (["nonce", "integrity", "csp"].includes(attr)) { return; - } else if (["src", "data", "href", "action", "formaction"].includes(attr)) { + } else if ( + ["src", "data", "href", "action", "formaction"].includes(attr) + ) { value = encodeUrl(value); } else if (attr === "srcdoc") { value = rewriteHtml(value); @@ -66,16 +68,16 @@ for (const attr of attrs) { declare global { interface Element { - __origattrs: Record; + $origattrs: Record; } } -Element.prototype.__origattrs = {}; +Element.prototype.$origattrs = {}; Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, { apply(target, thisArg, argArray) { - if (attrs.includes(argArray[0]) && thisArg.__origattrs[argArray[0]]) { - return thisArg.__origattrs[argArray[0]]; + if (attrs.includes(argArray[0]) && thisArg.$origattrs[argArray[0]]) { + return thisArg.$origattrs[argArray[0]]; } return Reflect.apply(target, thisArg, argArray); @@ -85,10 +87,12 @@ Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, { Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, { apply(target, thisArg, argArray) { if (attrs.includes(argArray[0])) { - thisArg.__origattrs[argArray[0]] = argArray[1]; + thisArg.$origattrs[argArray[0]] = argArray[1]; if (["nonce", "integrity", "csp"].includes(argArray[0])) { return; - } else if (["src", "data", "href", "action", "formaction"].includes(argArray[0])) { + } else if ( + ["src", "data", "href", "action", "formaction"].includes(argArray[0]) + ) { argArray[1] = encodeUrl(argArray[1]); } else if (argArray[0] === "srcdoc") { argArray[1] = rewriteHtml(argArray[1]); diff --git a/src/client/import.ts b/src/client/import.ts index 91dfd9b..631a67b 100644 --- a/src/client/import.ts +++ b/src/client/import.ts @@ -1,9 +1,8 @@ -import { decodeUrl, encodeUrl } from "../shared/rewriters/url" +import { encodeUrl } from "../shared/rewriters/url"; window.$sImport = function(base) { return function(url) { let resolved = new URL(url, base).href - console.log(resolved) return (function() { }.constructor(`return import("${encodeUrl(resolved)}")`))(); } } diff --git a/src/client/index.ts b/src/client/index.ts index c713068..b75701b 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -12,7 +12,7 @@ import "./storage.ts"; import "./css.ts"; import "./history.ts"; import "./worker.ts"; -import "./beacon.ts" +import "./beacon.ts"; import "./origin.ts"; import "./import.ts"; diff --git a/src/client/location.ts b/src/client/location.ts index c4154b8..509c847 100644 --- a/src/client/location.ts +++ b/src/client/location.ts @@ -11,22 +11,27 @@ function createLocation() { return loc; } -export const locationProxy = new Proxy({}, { - get(target, prop) { - const loc = createLocation(); - - return loc[prop]; +export const locationProxy = new Proxy( + { + host: "", }, + { + get(target, prop) { + const loc = createLocation(); - set(obj, prop, value) { - const loc = createLocation(); + return loc[prop]; + }, - if (prop === "href") { - location.href = encodeUrl(value); - } else { - loc[prop] = value; - } + set(obj, prop, value) { + const loc = createLocation(); - return true; - }, -}); + if (prop === "href") { + location.href = encodeUrl(value); + } else { + loc[prop] = value; + } + + return true; + }, + } +); diff --git a/src/client/native/eval.ts b/src/client/native/eval.ts index b228615..03112fc 100644 --- a/src/client/native/eval.ts +++ b/src/client/native/eval.ts @@ -33,4 +33,4 @@ window.eval = new Proxy(window.eval, { return Reflect.apply(target, thisArg, [rewriteJs(argArray[0])]); }, }); -*/ \ No newline at end of file +*/ diff --git a/src/client/origin.ts b/src/client/origin.ts index 35b4e76..9b452c0 100644 --- a/src/client/origin.ts +++ b/src/client/origin.ts @@ -1,42 +1,40 @@ import { decodeUrl } from "../shared/rewriters/url"; - // const descriptor = Object.getOwnPropertyDescriptor(window, "origin"); delete window.origin; Object.defineProperty(window, "origin", { - get() { - return new URL(decodeUrl(location.href)).origin; - }, - set() { - return false; - }, + get() { + return new URL(decodeUrl(location.href)).origin; + }, + set() { + return false; + }, }); - Object.defineProperty(document, "URL", { - get() { - return decodeUrl(location.href); - }, - set() { - return false; - } -}) + get() { + return decodeUrl(location.href); + }, + set() { + return false; + }, +}); Object.defineProperty(document, "baseURI", { - get() { - return decodeUrl(location.href); - }, - set() { - return false; - } -}) + get() { + return decodeUrl(location.href); + }, + set() { + return false; + }, +}); Object.defineProperty(document, "domain", { - get() { - return new URL(decodeUrl(location.href)).hostname; - }, - set() { - return false; - } -}) + get() { + return new URL(decodeUrl(location.href)).hostname; + }, + set() { + return false; + }, +}); diff --git a/src/client/requests/websocket.ts b/src/client/requests/websocket.ts index 990e961..43fa158 100644 --- a/src/client/requests/websocket.ts +++ b/src/client/requests/websocket.ts @@ -10,7 +10,7 @@ WebSocket = new Proxy(WebSocket, { target, { "User-Agent": navigator.userAgent, - "Origin": new URL(decodeUrl(location.href)).origin, + Origin: new URL(decodeUrl(location.href)).origin, }, ArrayBuffer.prototype ); diff --git a/src/client/scope.ts b/src/client/scope.ts index f924e8e..921e5a3 100644 --- a/src/client/scope.ts +++ b/src/client/scope.ts @@ -3,7 +3,11 @@ import { documentProxy, windowProxy } from "./window"; function scope(identifier: any) { // this will break iframe postmessage! - if (identifier instanceof Window || identifier instanceof top.window.Window || identifier instanceof parent.window.Window) { + if ( + identifier instanceof Window || + identifier instanceof top.window.Window || + identifier instanceof parent.window.Window + ) { return windowProxy; } else if (identifier instanceof Location) { return locationProxy; diff --git a/src/client/window.ts b/src/client/window.ts index e29dd68..2234a1e 100644 --- a/src/client/window.ts +++ b/src/client/window.ts @@ -12,7 +12,7 @@ export const windowProxy = new Proxy(window, { ) { return windowProxy; } else if (propIsString && prop == "parent") { - return window.parent + return window.parent; } else if (propIsString && prop === "$scramjet") { return; } else if (propIsString && prop === "addEventListener") { @@ -27,7 +27,6 @@ export const windowProxy = new Proxy(window, { const value = Reflect.get(target, prop); - // this is bad! i don't know what the right thing to do is if (typeof value === "function") { return new Proxy(value, { @@ -55,7 +54,6 @@ export const windowProxy = new Proxy(window, { }, }); - export const documentProxy = new Proxy(document, { get(target, prop) { const propIsString = typeof prop === "string"; @@ -78,7 +76,6 @@ export const documentProxy = new Proxy(document, { }, set(target, prop, newValue) { if (typeof prop === "string" && prop === "location") { - //@ts-ignore location = new URL(encodeUrl(newValue)); @@ -86,5 +83,5 @@ export const documentProxy = new Proxy(document, { } return Reflect.set(target, prop, newValue); - } + }, }); diff --git a/src/client/worker.ts b/src/client/worker.ts index 1578089..7a8da4c 100644 --- a/src/client/worker.ts +++ b/src/client/worker.ts @@ -20,6 +20,10 @@ Worklet.prototype.addModule = new Proxy(Worklet.prototype.addModule, { }, }); +if ("serviceWorker" in window.navigator) { + //@ts-expect-error temporary until nested sw support + delete window.Navigator.prototype.serviceWorker; +} // broken // window.importScripts = new Proxy(window.importScripts, { diff --git a/src/shared/rewriters/js.ts b/src/shared/rewriters/js.ts index 9fe0f86..3babf29 100644 --- a/src/shared/rewriters/js.ts +++ b/src/shared/rewriters/js.ts @@ -19,9 +19,11 @@ import * as ESTree from "estree"; import { initSync, rewrite_js } from "../../../rewriter/out/rewriter.js"; import "../../../static/wasm.js"; -initSync(new WebAssembly.Module( - Uint8Array.from(atob(self.WASM), c => c.charCodeAt(0)) -)) +initSync( + new WebAssembly.Module( + Uint8Array.from(atob(self.WASM), (c) => c.charCodeAt(0)) + ) +); global.rws = rewriteJs; export function rewriteJs(js: string | ArrayBuffer, origin?: URL) { @@ -155,4 +157,3 @@ export function rewriteJs(js: string | ArrayBuffer, origin?: URL) { // return js; // } } - diff --git a/src/shared/rewriters/worker.ts b/src/shared/rewriters/worker.ts index 5e1d8dc..d25dbfa 100644 --- a/src/shared/rewriters/worker.ts +++ b/src/shared/rewriters/worker.ts @@ -1,13 +1,11 @@ import { rewriteJs } from "./js"; export function rewriteWorkers(js: string, origin?: URL) { - let str = new String().toString()[ - //@ts-expect-error - ("codecs", "config", "shared", "client") - ].forEach((script) => { + let str = new String().toString(); + + ["codecs", "config", "shared", "client"].forEach((script) => { str += `import "${self.$scramjet.config[script]}"\n`; }); str += rewriteJs(js, origin); return str; } - diff --git a/src/thread/thread.ts b/src/thread/thread.ts index 0010d5e..5cb44f8 100644 --- a/src/thread/thread.ts +++ b/src/thread/thread.ts @@ -1,46 +1,40 @@ import { rewriteJs } from "../shared/rewriters/js"; - - // @ts-ignore onconnect = (e) => { - const port = e.ports[0]; + const port = e.ports[0]; + console.log("thread: connected to port", port); + port.postMessage("ready"); - console.log("thread: connected to port", port) - port.postMessage("ready"); + let syncToken = 0; + port.onmessage = ({ data }) => { + console.log("thread: received message", data); + const [task, ...args] = data; + let token = syncToken++; - let syncToken = 0; - port.onmessage = ({ data }) => { - console.log("thread: received message", data) - const [task, ...args] = data; - let token = syncToken++; - - try { - let res = tasks[task](...args); - console.log("thread: task", task, "completed with token", token) - port.postMessage({ - token, - result: res - }) - } catch (e) { - port.postMessage({ - token, - error: e.message - }) - } - - - port.postMessage("idle"); - } -} + try { + let res = tasks[task](...args); + console.log("thread: task", task, "completed with token", token); + port.postMessage({ + token, + result: res, + }); + } catch (e) { + port.postMessage({ + token, + error: e.message, + }); + } + port.postMessage("idle"); + }; +}; const tasks = { - "rewriteJs": taskRewriteJs, -} - + rewriteJs: taskRewriteJs, +}; function taskRewriteJs(js: ArrayBuffer, origin: string): string { - return rewriteJs(js, new URL(origin)); + return rewriteJs(js, new URL(origin)); } diff --git a/src/worker/error.ts b/src/worker/error.ts index a7dcb57..30c2726 100644 --- a/src/worker/error.ts +++ b/src/worker/error.ts @@ -1,17 +1,16 @@ - export function errorTemplate(trace: string, fetchedURL: string) { - // turn script into a data URI so we don"t have to escape any HTML values - const script = ` + // turn script into a data URI so we don"t have to escape any HTML values + const script = ` errorTrace.value = ${JSON.stringify(trace)}; fetchedURL.textContent = ${JSON.stringify(fetchedURL)}; for (const node of document.querySelectorAll("#hostname")) node.textContent = ${JSON.stringify( - location.hostname - )}; + location.hostname + )}; reload.addEventListener("click", () => location.reload()); version.textContent = "0.0.1"; `; - return ` + return ` @@ -43,24 +42,24 @@ export function errorTemplate(trace: string, fetchedURL: string) {

Scramjet v

- + `; } export function renderError(err: unknown, fetchedURL: string) { - const headers = { - "content-type": "text/html", - }; - if (crossOriginIsolated) { - headers["Cross-Origin-Embedder-Policy"] = "require-corp"; - } + const headers = { + "content-type": "text/html", + }; + if (crossOriginIsolated) { + headers["Cross-Origin-Embedder-Policy"] = "require-corp"; + } - return new Response(errorTemplate(String(err), fetchedURL), { - status: 500, - headers: headers, - }); + return new Response(errorTemplate(String(err), fetchedURL), { + status: 500, + headers: headers, + }); } - diff --git a/src/worker/fetch.ts b/src/worker/fetch.ts index b943eab..1bd801c 100644 --- a/src/worker/fetch.ts +++ b/src/worker/fetch.ts @@ -4,169 +4,164 @@ import { ParseResultType } from "parse-domain"; import { ScramjetServiceWorker } from "."; import { renderError } from "./error"; -export async function swfetch(this: ScramjetServiceWorker, { request }: FetchEvent) { - const urlParam = new URLSearchParams(new URL(request.url).search); - const { encodeUrl, decodeUrl } = self.$scramjet.shared.url; - const { - rewriteHeaders, - rewriteHtml, - rewriteJs, - rewriteCss, - rewriteWorkers, - } = self.$scramjet.shared.rewrite; - const { parseDomain } = self.$scramjet.shared.util; +export async function swfetch( + this: ScramjetServiceWorker, + { request }: FetchEvent +) { + const urlParam = new URLSearchParams(new URL(request.url).search); + const { encodeUrl, decodeUrl } = self.$scramjet.shared.url; + const { rewriteHeaders, rewriteHtml, rewriteJs, rewriteCss, rewriteWorkers } = + self.$scramjet.shared.rewrite; + const { parseDomain } = self.$scramjet.shared.util; - if (urlParam.has("url")) { - return Response.redirect( - encodeUrl(urlParam.get("url"), new URL(urlParam.get("url"))) - ); - } + if (urlParam.has("url")) { + return Response.redirect( + encodeUrl(urlParam.get("url"), new URL(urlParam.get("url"))) + ); + } - try { - const url = new URL(decodeUrl(request.url)); + try { + const url = new URL(decodeUrl(request.url)); - const cookieStore = new IDBMap(url.host, { - durability: "relaxed", - prefix: "Cookies", - }); + const cookieStore = new IDBMap(url.host, { + durability: "relaxed", + prefix: "Cookies", + }); - const response: BareResponseFetch = await this.client.fetch(url, { - method: request.method, - body: request.body, - headers: request.headers, - credentials: "omit", - mode: request.mode === "cors" ? request.mode : "same-origin", - cache: request.cache, - redirect: request.redirect, - //@ts-ignore why the fuck is this not typed mircosoft - duplex: "half", - }); + const response: BareResponseFetch = await this.client.fetch(url, { + method: request.method, + body: request.body, + headers: request.headers, + credentials: "omit", + mode: request.mode === "cors" ? request.mode : "same-origin", + cache: request.cache, + redirect: request.redirect, + //@ts-ignore why the fuck is this not typed mircosoft + duplex: "half", + }); - let responseBody; - const responseHeaders = rewriteHeaders(response.rawHeaders, url); + let responseBody; + const responseHeaders = rewriteHeaders(response.rawHeaders, url); - for (const cookie of (responseHeaders["set-cookie"] || []) as string[]) { - let cookieParsed = cookie.split(";").map((x) => x.trim().split("=")); + for (const cookie of (responseHeaders["set-cookie"] || []) as string[]) { + let cookieParsed = cookie.split(";").map((x) => x.trim().split("=")); - let [key, value] = cookieParsed.shift(); - if (!value) continue; - value = value.replace("\"", ""); + let [key, value] = cookieParsed.shift(); + if (!value) continue; + value = value.replace('"', ""); - const hostArg = cookieParsed.find((x) => x[0] === "Domain"); - cookieParsed = cookieParsed.filter((x) => x[0] !== "Domain"); - let host = hostArg ? hostArg[1] : undefined; + const hostArg = cookieParsed.find((x) => x[0] === "Domain"); + cookieParsed = cookieParsed.filter((x) => x[0] !== "Domain"); + let host = hostArg ? hostArg[1] : undefined; - if (url.protocol === "http" && cookieParsed.includes(["Secure"])) - continue; - if ( - cookieParsed.includes(["SameSite", "None"]) && - !cookieParsed.includes(["Secure"]) - ) - continue; + if (url.protocol === "http" && cookieParsed.includes(["Secure"])) + continue; + if ( + cookieParsed.includes(["SameSite", "None"]) && + !cookieParsed.includes(["Secure"]) + ) + continue; - if (host && host !== url.host) { - if (host.startsWith(".")) host = host.slice(1); - const urlDomain = parseDomain(url.hostname); + if (host && host !== url.host) { + if (host.startsWith(".")) host = host.slice(1); + const urlDomain = parseDomain(url.hostname); - if (urlDomain.type === ParseResultType.Listed) { - const { subDomains: domain, topLevelDomains } = urlDomain; - if (!host.endsWith([domain, ...topLevelDomains].join("."))) - continue; - } else { - continue; - } + if (urlDomain.type === ParseResultType.Listed) { + const { subDomains: domain, topLevelDomains } = urlDomain; + if (!host.endsWith([domain, ...topLevelDomains].join("."))) continue; + } else { + continue; + } - const realCookieStore = new IDBMap(host, { - durability: "relaxed", - prefix: "Cookies", - }); - realCookieStore.set(key, { - value: value, - args: cookieParsed, - subdomain: true, - }); - } else { - cookieStore.set(key, { - value: value, - args: cookieParsed, - subdomain: false, - }); - } - } + const realCookieStore = new IDBMap(host, { + durability: "relaxed", + prefix: "Cookies", + }); + realCookieStore.set(key, { + value: value, + args: cookieParsed, + subdomain: true, + }); + } else { + cookieStore.set(key, { + value: value, + args: cookieParsed, + subdomain: false, + }); + } + } - for (const header in responseHeaders) { - // flatten everything past here - if (Array.isArray(responseHeaders[header])) - responseHeaders[header] = responseHeaders[header][0]; - } + for (const header in responseHeaders) { + // flatten everything past here + if (Array.isArray(responseHeaders[header])) + responseHeaders[header] = responseHeaders[header][0]; + } - if (response.body) { - switch (request.destination) { - case "iframe": - case "document": - if ( - responseHeaders["content-type"] - ?.toString() - ?.startsWith("text/html") - ) { - responseBody = rewriteHtml(await response.text(), url); - } else { - responseBody = response.body; - } - break; - case "script": - responseBody = await this.threadpool.rewriteJs(await response.arrayBuffer(), url.toString()); - break; - case "style": - responseBody = rewriteCss(await response.text(), url); - break; - case "sharedworker": - case "worker": - responseBody = rewriteWorkers(await response.text(), url); - break; - default: - responseBody = response.body; - break; - } - } - // downloads - if (["document", "iframe"].includes(request.destination)) { - const header = responseHeaders["content-disposition"]; + if (response.body) { + switch (request.destination) { + case "iframe": + case "document": + if ( + responseHeaders["content-type"]?.toString()?.startsWith("text/html") + ) { + responseBody = rewriteHtml(await response.text(), url); + } else { + responseBody = response.body; + } + break; + case "script": + responseBody = rewriteJs(await response.arrayBuffer(), url); + // Disable threading for now, it's causing issues. + // responseBody = await this.threadpool.rewriteJs(responseBody, url.toString()); + break; + case "style": + responseBody = rewriteCss(await response.text(), url); + break; + case "sharedworker": + case "worker": + responseBody = rewriteWorkers(await response.text(), url); + break; + default: + responseBody = response.body; + break; + } + } + // downloads + if (["document", "iframe"].includes(request.destination)) { + const header = responseHeaders["content-disposition"]; - // validate header and test for filename - if (!/\s*?((inline|attachment);\s*?)filename=/i.test(header)) { - // if filename= wasn"t specified then maybe the remote specified to download this as an attachment? - // if it"s invalid then we can still possibly test for the attachment/inline type - const type = /^\s*?attachment/i.test(header) - ? "attachment" - : "inline"; + // validate header and test for filename + if (!/\s*?((inline|attachment);\s*?)filename=/i.test(header)) { + // if filename= wasn"t specified then maybe the remote specified to download this as an attachment? + // if it"s invalid then we can still possibly test for the attachment/inline type + const type = /^\s*?attachment/i.test(header) ? "attachment" : "inline"; - // set the filename - const [filename] = new URL(response.finalURL).pathname - .split("/") - .slice(-1); + // set the filename + const [filename] = new URL(response.finalURL).pathname + .split("/") + .slice(-1); - responseHeaders["content-disposition"] = - `${type}; filename=${JSON.stringify(filename)}`; - } - } - if (responseHeaders["accept"] === "text/event-stream") { - responseHeaders["content-type"] = "text/event-stream"; - } - if (crossOriginIsolated) { - responseHeaders["Cross-Origin-Embedder-Policy"] = "require-corp"; - } + responseHeaders["content-disposition"] = + `${type}; filename=${JSON.stringify(filename)}`; + } + } + if (responseHeaders["accept"] === "text/event-stream") { + responseHeaders["content-type"] = "text/event-stream"; + } + if (crossOriginIsolated) { + responseHeaders["Cross-Origin-Embedder-Policy"] = "require-corp"; + } - return new Response(responseBody, { - headers: responseHeaders as HeadersInit, - status: response.status, - statusText: response.statusText, - }); - } catch (err) { - console.error("ERROR FROM SERVICE WORKER FETCH", err); - if (!["document", "iframe"].includes(request.destination)) - return new Response(undefined, { status: 500 }); + return new Response(responseBody, { + headers: responseHeaders as HeadersInit, + status: response.status, + statusText: response.statusText, + }); + } catch (err) { + console.error("ERROR FROM SERVICE WORKER FETCH", err); + if (!["document", "iframe"].includes(request.destination)) + return new Response(undefined, { status: 500 }); - return renderError(err, decodeUrl(request.url)); - } + return renderError(err, decodeUrl(request.url)); + } } diff --git a/src/worker/index.ts b/src/worker/index.ts index daa1422..4c6b92a 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -18,7 +18,6 @@ export class ScramjetServiceWorker { this.config = config; this.threadpool = new ScramjetThreadpool(); - } route({ request }: FetchEvent) { @@ -28,7 +27,6 @@ export class ScramjetServiceWorker { } public fetch = swfetch; -}; - +} self.ScramjetServiceWorker = ScramjetServiceWorker; diff --git a/src/worker/threadpool.ts b/src/worker/threadpool.ts index b71cb63..7980b8c 100644 --- a/src/worker/threadpool.ts +++ b/src/worker/threadpool.ts @@ -1,92 +1,86 @@ - type Thread = { - handle: MessagePort; - ready: boolean; - busy: boolean; - syncToken: number; - promises: Map; -} + handle: MessagePort; + ready: boolean; + busy: boolean; + syncToken: number; + promises: Map; +}; export class ScramjetThreadpool { - threads: Thread[] = []; - constructor() { - self.addEventListener("message", ({ data }) => { - if (data.scramjet$type == "add") { - this.spawn(data.handle); - } - }); - } + threads: Thread[] = []; + constructor() { + self.addEventListener("message", ({ data }) => { + if (data.scramjet$type == "add") { + this.spawn(data.handle); + } + }); + } - spawn(handle) { - const thread = { - handle, - ready: false, - busy: false, - syncToken: 0, - promises: new Map() - } + spawn(handle) { + const thread = { + handle, + ready: false, + busy: false, + syncToken: 0, + promises: new Map(), + }; - this.threads.push(thread); + this.threads.push(thread); - thread.handle.onmessage = (e) => { - if (e.data === "ready") { - thread.ready = true; - return; - } - if (e.data === "idle") { - thread.busy = false; - return; - } + thread.handle.onmessage = (e) => { + if (e.data === "ready") { + thread.ready = true; + return; + } + if (e.data === "idle") { + thread.busy = false; + return; + } - const { token, result, error } = e.data; - const { resolve, reject } = thread.promises.get(token); - thread.promises.delete(token); + const { token, result, error } = e.data; + const { resolve, reject } = thread.promises.get(token); + thread.promises.delete(token); - if (error) { - reject(error); - } else { - resolve(result); - } - } + if (error) { + reject(error); + } else { + resolve(result); + } + }; - thread.handle.start(); - } + thread.handle.start(); + } - pick(): Thread | undefined { - const alive = this.threads.filter(t => t.ready); - const idle = alive.filter(t => !t.busy); + pick(): Thread | undefined { + const alive = this.threads.filter((t) => t.ready); + const idle = alive.filter((t) => !t.busy); - // no threads - if (!alive.length) return; + // no threads + if (!alive.length) return; - // there is a thread, but it's busy - if (!idle.length) return alive[Math.floor(Math.random() * alive.length)]; + // there is a thread, but it's busy + if (!idle.length) return alive[Math.floor(Math.random() * alive.length)]; - // there's an open thread - return idle[Math.floor(Math.random() * idle.length)]; - } + // there's an open thread + return idle[Math.floor(Math.random() * idle.length)]; + } - run(task: string, args: any[], transferrable: any[]): Promise { - const thread = this.pick(); - if (!thread) throw new Error("No threads available"); - thread.busy = true; + run(task: string, args: any[], transferrable: any[]): Promise { + const thread = this.pick(); + if (!thread) throw new Error("No threads available"); + thread.busy = true; + let token = thread.syncToken++; - let token = thread.syncToken++; + // console.log("runthread: dispatching task", task, "to thread", thread, "of token", token) + return new Promise((resolve, reject) => { + thread.promises.set(token, { resolve, reject }); - // console.log("runthread: dispatching task", task, "to thread", thread, "of token", token) - return new Promise((resolve, reject) => { - thread.promises.set(token, { resolve, reject }); + thread.handle.postMessage([task, ...args], transferrable); + }); + } - thread.handle.postMessage([ - task, - ...args - ], transferrable); - }); - } - - - async rewriteJs(js: ArrayBuffer, origin: string): Promise { - return await this.run("rewriteJs", [js, origin], [js]); - } + async rewriteJs(js: ArrayBuffer, origin: string): Promise { + return await this.run("rewriteJs", [js, origin], [js]); + } } diff --git a/static/ui.js b/static/ui.js index 07bce98..8255979 100644 --- a/static/ui.js +++ b/static/ui.js @@ -1,19 +1,21 @@ -navigator.serviceWorker - .register("./sw.js") - .then((reg) => { - reg.update(); - }); +navigator.serviceWorker.register("./sw.js").then((reg) => { + reg.update(); +}); navigator.serviceWorker.ready.then((reg) => { - for (let i = 0; i < 20; i++) { - const thread = new SharedWorker($scramjet.config.thread, { name: "thread" + i }); + for (let i = 0; i < 20; i++) { + const thread = new SharedWorker($scramjet.config.thread, { + name: "thread" + i, + }); - reg.active.postMessage({ - scramjet$type: "add", - handle: thread.port - }, [thread.port]); - - } + reg.active.postMessage( + { + scramjet$type: "add", + handle: thread.port, + }, + [thread.port] + ); + } }); const connection = new BareMux.BareMuxConnection("/baremux/worker.js"); @@ -24,21 +26,21 @@ const col = css` flex-direction: column; `; const store = $store( - { - url: "https://google.com", - wispurl: "wss://wisp.mercurywork.shop/", - bareurl: - (location.protocol === "https:" ? "https" : "http") + - "://" + - location.host + - "/bare/", - }, - { ident: "settings", backing: "localstorage", autosave: "auto" } + { + url: "https://google.com", + wispurl: "wss://wisp.mercurywork.shop/", + bareurl: + (location.protocol === "https:" ? "https" : "http") + + "://" + + location.host + + "/bare/", + }, + { ident: "settings", backing: "localstorage", autosave: "auto" } ); connection.setTransport("/baremod/index.mjs", [store.bareurl]); function App() { - this.urlencoded = ""; - this.css = ` + this.urlencoded = ""; + this.css = ` width: 100%; height: 100%; color: #e0def4; @@ -97,7 +99,7 @@ function App() { } `; - return html` + return html`

Percury Unblocker

surf the unblocked and mostly buggy web

@@ -121,5 +123,5 @@ function App() { } window.addEventListener("load", () => { - document.body.appendChild(h(App)); + document.body.appendChild(h(App)); });