Fix header rewriting

Fix header rewriting, revamp html rewriting, add types to bare-mux from bare-client, prefetch scramjet scripts for faster load
This commit is contained in:
Avad3 2024-05-07 00:29:30 -04:00
parent 8a68a17e7a
commit 23fd5a7b83
7 changed files with 75 additions and 125 deletions

View file

@ -14,18 +14,18 @@ const devServer = createServer({
outdir: "./dist", outdir: "./dist",
bundle: true, bundle: true,
sourcemap: true, sourcemap: true,
logLevel: 'info', logLevel: "info",
plugins: [ plugins: [
copy({ copy({
resolveFrom: "cwd", resolveFrom: "cwd",
assets: [ assets: [
{ {
from: ['./node_modules/@mercuryworkshop/bare-mux/dist/bare.cjs'], from: ["./node_modules/@mercuryworkshop/bare-mux/dist/bare.cjs"],
to: ['./static/bare-mux.js'], to: ["./static/bare-mux.js"],
}, },
{ {
from: ['./node_modules/@mercuryworkshop/bare-as-module3/dist/bare.cjs'], from: ["./node_modules/@mercuryworkshop/bare-as-module3/dist/bare.cjs"],
to: ['./static/bare-client.js'], to: ["./static/bare-client.js"],
}, },
{ {
from: ["./dist/*"], from: ["./dist/*"],

View file

@ -25,8 +25,9 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@fastify/static": "^7.0.3", "@fastify/static": "^7.0.3",
"@mercuryworkshop/bare-mux": "^1.1.0",
"@mercuryworkshop/bare-as-module3": "^2.2.0-alpha", "@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", "@tomphttp/bare-server-node": "^2.0.3",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"dom-serializer": "^2.0.0", "dom-serializer": "^2.0.0",

26
pnpm-lock.yaml generated
View file

@ -8,6 +8,12 @@ dependencies:
'@fastify/static': '@fastify/static':
specifier: ^7.0.3 specifier: ^7.0.3
version: 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': '@tomphttp/bare-client':
specifier: 2.2.0-alpha specifier: 2.2.0-alpha
version: 2.2.0-alpha version: 2.2.0-alpha
@ -385,6 +391,17 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: false 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: /@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -457,6 +474,10 @@ packages:
resolution: {integrity: sha512-jBhFui72jyO0Tpsq/8AvL7GRJKiuUxdHPD9rrSRhf2SSElCn61yTyU2G133IlBVvBumCH4T5FDjmjbAG7MU9tg==} resolution: {integrity: sha512-jBhFui72jyO0Tpsq/8AvL7GRJKiuUxdHPD9rrSRhf2SSElCn61yTyU2G133IlBVvBumCH4T5FDjmjbAG7MU9tg==}
dev: true 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): /@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==} resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==}
engines: {node: ^16.0.0 || >=18.0.0} engines: {node: ^16.0.0 || >=18.0.0}
@ -2026,6 +2047,11 @@ packages:
dependencies: dependencies:
punycode: 2.3.1 punycode: 2.3.1
/uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
dev: false
/which@2.0.2: /which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}

View file

@ -1,4 +1,5 @@
import { encodeUrl } from "./url"; import { encodeUrl } from "./url";
import { BareHeaders } from "@tomphttp/bare-client";
const cspHeaders = [ const cspHeaders = [
"cross-origin-embedder-policy", "cross-origin-embedder-policy",
@ -25,26 +26,19 @@ const urlHeaders = [
"referer" "referer"
]; ];
export function rewriteHeaders(headers: Headers, origin?: string) { export function rewriteHeaders(headers: BareHeaders, origin?: string) {
cspHeaders.forEach((header) => { cspHeaders.forEach((header) => {
if (headers.has(header)) { delete headers[header];
headers.delete(header); delete headers[header.toLowerCase()];
}
}); });
urlHeaders.forEach((header) => { urlHeaders.forEach((header) => {
if (headers.has(header)) { if (headers[header]) {
headers.set(header, encodeUrl(headers.get(header), origin)); 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")) { return headers;
let link = headers.get("link");
link = link.replace(/<(.*?)>/g, (match, g1) => {
return `<${encodeUrl(g1, origin)}>`;
});
headers.set("link", link);
}
} }

View file

@ -1,6 +1,6 @@
import { Parser } from "htmlparser2"; import { Parser } from "htmlparser2";
import { DomHandler, hasChildren } from "domhandler"; import { DomHandler, hasChildren } from "domhandler";
import { hasAttrib, getAttributeValue } from "domutils"; import { hasAttrib } from "domutils";
import render from "dom-serializer"; import render from "dom-serializer";
import { encodeUrl } from "./url"; import { encodeUrl } from "./url";
import { rewriteCss } from "./css"; import { rewriteCss } from "./css";
@ -20,97 +20,27 @@ export function rewriteHtml(html: string, origin?: string) {
} }
function traverseParsedHtml(node, 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 /* csp attributes */
if (hasAttrib(node, "nonce")) { if (hasAttrib(node, "nonce")) delete node.attribs.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")) { /* url attributes */
node.attribs.href = encodeUrl(node.attribs.href, origin); if (hasAttrib(node, "src")) node.attribs.src = encodeUrl(node.attribs.src, origin);
} else if (node.name === "iframe") { if (hasAttrib(node, "href")) node.attribs.href = encodeUrl(node.attribs.href, origin);
if (hasAttrib(node, "src")) { if (hasAttrib(node, "data")) node.attribs.data = encodeUrl(node.attribs.data, origin);
node.attribs.src = encodeUrl(node.attribs.src, 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")) { /* other */
node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin); 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")) { if (node.name === "style" && hasChildren(node)) node.children[0].data = rewriteCss(node.children[0].data, origin);
delete node.attribs.csp; // 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);
} 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.childNodes) { if (node.childNodes) {
for (const childNode in node.childNodes) { for (const childNode in node.childNodes) {

View file

@ -2,6 +2,7 @@ importScripts("/scramjet.codecs.js");
importScripts("/scramjet.config.js"); importScripts("/scramjet.config.js");
importScripts("/scramjet.bundle.js"); importScripts("/scramjet.bundle.js");
import { BareClient } from "@mercuryworkshop/bare-mux"; import { BareClient } from "@mercuryworkshop/bare-mux";
import { BareResponseFetch } from "@tomphttp/bare-client"
declare global { declare global {
interface Window { interface Window {
@ -19,19 +20,16 @@ self.ScramjetServiceWorker = class ScramjetServiceWorker {
async fetch(event: FetchEvent) { async fetch(event: FetchEvent) {
const url = new URL(self.__scramjet$bundle.rewriters.url.decodeUrl(event.request.url)); 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 // implement header rewriting later
const response = await this.client.fetch(url, { const response: BareResponseFetch = await this.client.fetch(url, {
method: event.request.method, method: event.request.method,
body: event.request.body, body: event.request.body,
headers headers: event.request.headers
}); });
self.__scramjet$bundle.rewriters.rewriteHeaders(response.headers);
let responseBody; let responseBody;
const responseHeaders = self.__scramjet$bundle.rewriters.rewriteHeaders(response.rawHeaders, origin);
if (event.request.destination === "document") { if (event.request.destination === "document") {
responseBody = self.__scramjet$bundle.rewriters.rewriteHtml(await response.text(), url.origin); responseBody = self.__scramjet$bundle.rewriters.rewriteHtml(await response.text(), url.origin);
@ -43,11 +41,12 @@ self.ScramjetServiceWorker = class ScramjetServiceWorker {
responseBody = response.body; responseBody = response.body;
} }
if (crossOriginIsolated) { // if (crossOriginIsolated) {
response.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'; // response.headers["Cross-Origin-Embedder-Policy"] = "require-cors";
} // }
return new Response(responseBody, { return new Response(responseBody, {
headers: response.headers, headers: responseHeaders as HeadersInit,
status: response.status, status: response.status,
statusText: response.statusText statusText: response.statusText
}) })

View file

@ -4,8 +4,8 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title> <title>Document</title>
<link rel="preload" as="script" href="./scramjet.worker.js"> <link rel="prefetch" href="./scramjet.worker.js">
<link rel="preload" as="script" href="./scramjet.bundle.js"> <link rel="prefetch" href="./scramjet.bundle.js">
<script src="./bare-mux.js" defer></script> <script src="./bare-mux.js" defer></script>
<script src="./bare-client.js" defer></script> <script src="./bare-client.js" defer></script>
</head> </head>