diff --git a/esbuild.dev.js b/esbuild.dev.js index a305b38..e6a41dc 100644 --- a/esbuild.dev.js +++ b/esbuild.dev.js @@ -14,24 +14,24 @@ const devServer = createServer({ outdir: "./dist", bundle: true, sourcemap: true, - logLevel: 'info', + logLevel: "info", plugins: [ copy({ resolveFrom: "cwd", assets: [ { - from: ['./node_modules/@mercuryworkshop/bare-mux/dist/bare.cjs'], - to: ['./static/bare-mux.js'], + from: ["./node_modules/@mercuryworkshop/bare-mux/dist/bare.cjs"], + to: ["./static/bare-mux.js"], }, { - from: ['./node_modules/@mercuryworkshop/bare-as-module3/dist/bare.cjs'], - to: ['./static/bare-client.js'], + from: ["./node_modules/@mercuryworkshop/bare-as-module3/dist/bare.cjs"], + to: ["./static/bare-client.js"], }, { from: ["./dist/*"], to: ["./static"] }, - ], + ], }), time() ] diff --git a/package.json b/package.json index 39cfdf6..1c43cf3 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,9 @@ "type": "module", "dependencies": { "@fastify/static": "^7.0.3", - "@mercuryworkshop/bare-mux": "^1.1.0", "@mercuryworkshop/bare-as-module3": "^2.2.0-alpha", + "@mercuryworkshop/bare-mux": "^1.1.0", + "@tomphttp/bare-client": "2.2.0-alpha", "@tomphttp/bare-server-node": "^2.0.3", "concurrently": "^8.2.2", "dom-serializer": "^2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ca6a87..5d39f13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ dependencies: '@fastify/static': specifier: ^7.0.3 version: 7.0.3 + '@mercuryworkshop/bare-as-module3': + specifier: ^2.2.0-alpha + version: 2.2.0-alpha + '@mercuryworkshop/bare-mux': + specifier: ^1.1.0 + version: 1.1.0 '@tomphttp/bare-client': specifier: 2.2.0-alpha version: 2.2.0-alpha @@ -385,6 +391,17 @@ packages: engines: {node: '>=8'} dev: false + /@mercuryworkshop/bare-as-module3@2.2.0-alpha: + resolution: {integrity: sha512-Vbk07NDfwENaiadfZFdG+FkjZb6dQODhykYhMpY7k2TZJanuDjs+MDIucpiqufymCTemx+9uAnv91v/GTxqJsg==} + dev: false + + /@mercuryworkshop/bare-mux@1.1.0: + resolution: {integrity: sha512-E+d2GZEdbnUrGTx8AOrDVe0d7Cxz57rjDWBn9xAHH65G68PQkVY17j0EXAurFXF/sJzbjLTPcJZvHZQA2P/IVQ==} + dependencies: + '@types/uuid': 9.0.8 + uuid: 9.0.1 + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -457,6 +474,10 @@ packages: resolution: {integrity: sha512-jBhFui72jyO0Tpsq/8AvL7GRJKiuUxdHPD9rrSRhf2SSElCn61yTyU2G133IlBVvBumCH4T5FDjmjbAG7MU9tg==} dev: true + /@types/uuid@9.0.8: + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + dev: false + /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} engines: {node: ^16.0.0 || >=18.0.0} @@ -2026,6 +2047,11 @@ packages: dependencies: punycode: 2.3.1 + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} diff --git a/src/bundle/rewriters/headers.ts b/src/bundle/rewriters/headers.ts index b2f31e5..7899b75 100644 --- a/src/bundle/rewriters/headers.ts +++ b/src/bundle/rewriters/headers.ts @@ -1,4 +1,5 @@ import { encodeUrl } from "./url"; +import { BareHeaders } from "@tomphttp/bare-client"; const cspHeaders = [ "cross-origin-embedder-policy", @@ -25,26 +26,19 @@ const urlHeaders = [ "referer" ]; -export function rewriteHeaders(headers: Headers, origin?: string) { +export function rewriteHeaders(headers: BareHeaders, origin?: string) { cspHeaders.forEach((header) => { - if (headers.has(header)) { - headers.delete(header); - } + delete headers[header]; + delete headers[header.toLowerCase()]; }); urlHeaders.forEach((header) => { - if (headers.has(header)) { - headers.set(header, encodeUrl(headers.get(header), origin)); + if (headers[header]) { + headers[header] = encodeUrl(headers[header] as string, origin); + } else if (headers[header.toLowerCase()]) { + headers[header.toLowerCase()] = encodeUrl(headers[header.toLowerCase()] as string, origin); } - }); + }) - if (headers.has("link")) { - let link = headers.get("link"); - - link = link.replace(/<(.*?)>/g, (match, g1) => { - return `<${encodeUrl(g1, origin)}>`; - }); - - headers.set("link", link); - } + return headers; } \ No newline at end of file diff --git a/src/bundle/rewriters/html.ts b/src/bundle/rewriters/html.ts index e25a5ee..f1c3041 100644 --- a/src/bundle/rewriters/html.ts +++ b/src/bundle/rewriters/html.ts @@ -1,6 +1,6 @@ import { Parser } from "htmlparser2"; import { DomHandler, hasChildren } from "domhandler"; -import { hasAttrib, getAttributeValue } from "domutils"; +import { hasAttrib } from "domutils"; import render from "dom-serializer"; import { encodeUrl } from "./url"; import { rewriteCss } from "./css"; @@ -20,97 +20,27 @@ export function rewriteHtml(html: string, origin?: string) { } function traverseParsedHtml(node, origin?: string) { - // apparently nonce is a global attribute so i'll just delete it at the beginning of the file - if (hasAttrib(node, "nonce")) { - delete node.attribs.nonce; - } + /* csp attributes */ + if (hasAttrib(node, "nonce")) delete node.attribs.nonce; + if (hasAttrib(node, "integrity")) delete node.attribs.integrity; + if (hasAttrib(node, "csp")) delete node.attribs.csp; - if (node.name === "a" && hasAttrib(node, "href")) { - node.attribs.href = encodeUrl(node.attribs.href, origin); - } else if (node.name === "iframe") { - if (hasAttrib(node, "src")) { - node.attribs.src = encodeUrl(node.attribs.src, origin); - } + /* url attributes */ + if (hasAttrib(node, "src")) node.attribs.src = encodeUrl(node.attribs.src, origin); + if (hasAttrib(node, "href")) node.attribs.href = encodeUrl(node.attribs.href, origin); + if (hasAttrib(node, "data")) node.attribs.data = encodeUrl(node.attribs.data, origin); + if (hasAttrib(node, "formaction")) node.attribs.formaction = encodeUrl(node.attribs.formaction, origin); + if (hasAttrib(node, "form")) node.attribs.action = encodeUrl(node.attribs.action, origin); - if (hasAttrib(node, "srcdoc")) { - node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin); - } + /* other */ + if (hasAttrib(node, "srcdoc")) node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin); + if (hasAttrib(node, "srcset")) node.attribs.srcset = rewriteSrcset(node.attribs.srcset, origin); + if (hasAttrib(node, "imagesrcset")) node.attribs.imagesrcset = rewriteSrcset(node.attribs.imagesrcset, origin); - if (hasAttrib(node, "csp")) { - delete node.attribs.csp; - } - } else if (node.name === "link") { - if (hasAttrib(node, "integrity")) { - delete node.attribs.integrity; - } - - node.attribs.href = encodeUrl(node.attribs.href, origin); - - if (hasAttrib(node, "imagesrcset")) { - node.attribs.imagesrcset = rewriteSrcset(node.attribs.imagesrcset); - } - - console.log(node.attribs.href) - } else if (node.name === "style") { - node.children[0].data = rewriteCss(node.children[0].data, origin); - } else if (node.name === "script") { - if (hasAttrib(node, "integrity")) { - delete node.attribs.integrity; - } - - if (hasAttrib(node, "type") && /(application|text)\/javascript|importmap/.test(getAttributeValue(node, "type"))) { - if (hasAttrib(node, "src")) { - node.attribs.src = encodeUrl(node.attribs.src, origin); - console.log(node.attribs.src); - } - } - - // implement js rewriting when done - } else if (node.name === "img" && hasAttrib(node, "src")) { - if (hasAttrib(node, "src")) { - node.attribs.src = encodeUrl(node.attribs.src, origin); - } - - if (hasAttrib(node, "srcset")) { - node.attribs.srcset = rewriteSrcset(node.attribs.srcset); - } - } else if (node.name === "object") { - node.attribs.data = encodeUrl(node.attribs.data, origin); - } else if (node.name === "embed") { - node.attribs.src = encodeUrl(node.attribs.src, origin); - } else if (node.name === "source") { - if (hasAttrib(node, "src")) { - node.attribs.src = encodeUrl(node.attribs.src, origin); - } - - if (hasAttrib(node, "srcset")) { - node.attribs.srcset = rewriteSrcset(node.attribs.srcset); - } - } else if (node.name === "form") { - node.attribs.action = encodeUrl(node.attribs.action, origin); - } else if (node.name === "area") { - node.attribs.href = encodeUrl(node.attribs.href, origin); - } else if (node.name === "base" && hasAttrib(node, "href")) { - node.attribs.href = encodeUrl(node.attribs.href, origin); - } else if (node.name === "input") { - if (hasAttrib(node, "formaction")) { - node.attribs.formaction = encodeUrl(node.attribs.formaction, origin); - } - } else if (node.name === "audio") { - node.attribs.src = encodeUrl(node.attribs.src, origin); - } else if (node.name === "button") { - if (hasAttrib(node, "formaction")) { - node.attribs.formaction = encodeUrl(node.attribs.formaction, origin); - } - } else if (node.name === "track") { - node.attribs.src = encodeUrl(node.attribs.src, origin); - } else if (node.name === "video") { - node.attribs.src = encodeUrl(node.attribs.src, origin); - } else if (node.name === "meta") { - if (hasAttrib(node, "http-equiv") && node.attribs["http-equiv"] === "content-security-policy") { - delete node.attribs["http-equiv"]; - } - } + if (node.name === "style" && hasChildren(node)) node.children[0].data = rewriteCss(node.children[0].data, origin); + // if (node.name === "script" && /((application|text)\/javascript)|importmap|undefined/.test(node.attribs.type) && !!node.children[0].data) { + // node.children[0].data = rewriteJs(node.children[0].data); + // } if (node.childNodes) { for (const childNode in node.childNodes) { diff --git a/src/worker/index.ts b/src/worker/index.ts index 50718ec..71a0808 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -2,6 +2,7 @@ importScripts("/scramjet.codecs.js"); importScripts("/scramjet.config.js"); importScripts("/scramjet.bundle.js"); import { BareClient } from "@mercuryworkshop/bare-mux"; +import { BareResponseFetch } from "@tomphttp/bare-client" declare global { interface Window { @@ -19,19 +20,16 @@ self.ScramjetServiceWorker = class ScramjetServiceWorker { async fetch(event: FetchEvent) { const url = new URL(self.__scramjet$bundle.rewriters.url.decodeUrl(event.request.url)); - let headers = new Headers(event.request.headers); - self.__scramjet$bundle.rewriters.rewriteHeaders(headers); - // implement header rewriting later - const response = await this.client.fetch(url, { + const response: BareResponseFetch = await this.client.fetch(url, { method: event.request.method, body: event.request.body, - headers + headers: event.request.headers }); - self.__scramjet$bundle.rewriters.rewriteHeaders(response.headers); - + let responseBody; + const responseHeaders = self.__scramjet$bundle.rewriters.rewriteHeaders(response.rawHeaders, origin); if (event.request.destination === "document") { responseBody = self.__scramjet$bundle.rewriters.rewriteHtml(await response.text(), url.origin); @@ -43,11 +41,12 @@ self.ScramjetServiceWorker = class ScramjetServiceWorker { responseBody = response.body; } - if (crossOriginIsolated) { - response.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'; - } + // if (crossOriginIsolated) { + // response.headers["Cross-Origin-Embedder-Policy"] = "require-cors"; + // } + return new Response(responseBody, { - headers: response.headers, + headers: responseHeaders as HeadersInit, status: response.status, statusText: response.statusText }) diff --git a/static/index.html b/static/index.html index 1c689b8..5ab468a 100644 --- a/static/index.html +++ b/static/index.html @@ -4,8 +4,8 @@