mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-13 14:30:02 -04:00
sync xhr
This commit is contained in:
parent
f59766b79c
commit
7fa94bd9d3
5 changed files with 153 additions and 1 deletions
|
@ -18,6 +18,7 @@ export default defineConfig({
|
|||
client: join(__dirname, "src/client/index.ts"),
|
||||
codecs: join(__dirname, "src/codecs/index.ts"),
|
||||
controller: join(__dirname, "src/controller/index.ts"),
|
||||
sync: join(__dirname, "src/sync.ts"),
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"],
|
||||
|
|
|
@ -1,10 +1,105 @@
|
|||
import { decodeUrl, encodeUrl, rewriteHeaders } from "../../../shared";
|
||||
import { config, decodeUrl, encodeUrl, rewriteHeaders } from "../../../shared";
|
||||
import { ScramjetClient } from "../../client";
|
||||
|
||||
export default function (client: ScramjetClient, self: Self) {
|
||||
const worker = new Worker(config.sync);
|
||||
const ARGS = Symbol("xhr original args");
|
||||
const HEADERS = Symbol("xhr headers");
|
||||
|
||||
client.Proxy("XMLHttpRequest.prototype.open", {
|
||||
apply(ctx) {
|
||||
if (ctx.args[1]) ctx.args[1] = encodeUrl(ctx.args[1], client.meta);
|
||||
ctx.this[ARGS] = ctx.args;
|
||||
},
|
||||
});
|
||||
|
||||
client.Proxy("XMLHttpRequest.prototype.setRequestHeader", {
|
||||
apply(ctx) {
|
||||
const headers = ctx.this[HEADERS] || (ctx.this[HEADERS] = {});
|
||||
headers[ctx.args[0]] = ctx.args[1];
|
||||
},
|
||||
});
|
||||
|
||||
client.Proxy("XMLHttpRequest.prototype.send", {
|
||||
apply(ctx) {
|
||||
const args = ctx.this[ARGS];
|
||||
if (!args || args[2]) return;
|
||||
|
||||
// it's a sync request
|
||||
// sync xhr to service worker is not supported
|
||||
// there's a nice way of polyfilling this though, we can spin on an atomic using sharedarraybuffer. this will maintain the sync behavior
|
||||
|
||||
// @ts-expect-error maxbytelength not in types yet i guess
|
||||
const sab = new SharedArrayBuffer(1024, { maxByteLength: 2147483647 });
|
||||
const view = new DataView(sab);
|
||||
|
||||
worker.postMessage({
|
||||
sab,
|
||||
args,
|
||||
headers: ctx.this[HEADERS],
|
||||
body: ctx.args[0],
|
||||
});
|
||||
|
||||
while (view.getUint8(0) === 0) {
|
||||
/* spin */
|
||||
}
|
||||
|
||||
const status = view.getUint16(1);
|
||||
const headersLength = view.getUint32(3);
|
||||
|
||||
const headersab = new Uint8Array(headersLength);
|
||||
headersab.set(new Uint8Array(sab.slice(7, 7 + headersLength)));
|
||||
const headers = new TextDecoder().decode(headersab);
|
||||
|
||||
const bodyLength = view.getUint32(7 + headersLength);
|
||||
const bodyab = new Uint8Array(bodyLength);
|
||||
bodyab.set(
|
||||
new Uint8Array(
|
||||
sab.slice(11 + headersLength, 11 + headersLength + bodyLength)
|
||||
)
|
||||
);
|
||||
const body = new TextDecoder().decode(bodyab);
|
||||
|
||||
// these should be using proxies to not leak scram strings but who cares
|
||||
client.RawTrap(ctx.this, "status", {
|
||||
get() {
|
||||
return status;
|
||||
},
|
||||
});
|
||||
client.RawTrap(ctx.this, "responseText", {
|
||||
get() {
|
||||
return body;
|
||||
},
|
||||
});
|
||||
client.RawTrap(ctx.this, "response", {
|
||||
get() {
|
||||
if (ctx.this.responseType === "arraybuffer") return bodyab.buffer;
|
||||
return body;
|
||||
},
|
||||
});
|
||||
client.RawTrap(ctx.this, "responseXML", {
|
||||
get() {
|
||||
const parser = new DOMParser();
|
||||
return parser.parseFromString(body, "text/xml");
|
||||
},
|
||||
});
|
||||
client.RawTrap(ctx.this, "getAllResponseHeaders", {
|
||||
get() {
|
||||
return () => headers;
|
||||
},
|
||||
});
|
||||
client.RawTrap(ctx.this, "getResponseHeader", {
|
||||
get() {
|
||||
return (header: string) => {
|
||||
const re = new RegExp(`^${header}: (.*)$`, "m");
|
||||
const match = re.exec(headers);
|
||||
return match ? match[1] : null;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// send has no return value right
|
||||
ctx.return(undefined);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ export class ScramjetController {
|
|||
thread: "/scramjet.thread.js",
|
||||
client: "/scramjet.client.js",
|
||||
codecs: "/scramjet.codecs.js",
|
||||
sync: "/scramjet.sync.js",
|
||||
flags: {
|
||||
serviceworkers: false,
|
||||
naiiveRewriter: false,
|
||||
|
|
54
src/sync.ts
Normal file
54
src/sync.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
addEventListener(
|
||||
"message",
|
||||
({
|
||||
data: {
|
||||
sab,
|
||||
args: [method, url, _, username, password],
|
||||
body,
|
||||
headers,
|
||||
},
|
||||
}) => {
|
||||
const view = new DataView(sab);
|
||||
const u8view = new Uint8Array(sab);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "arraybuffer";
|
||||
|
||||
// force async since we need it to resolve to the sw
|
||||
xhr.open(method, url, true, username, password);
|
||||
|
||||
if (headers)
|
||||
for (const [k, v] of Object.entries(headers)) {
|
||||
xhr.setRequestHeader(k, v as string);
|
||||
}
|
||||
|
||||
xhr.send(body);
|
||||
|
||||
xhr.onload = () => {
|
||||
let cursor = 1; // first byte is the lock
|
||||
|
||||
view.setUint16(cursor, xhr.status);
|
||||
cursor += 2;
|
||||
|
||||
// next write the header string
|
||||
const headers = xhr.getAllResponseHeaders();
|
||||
view.setUint32(cursor, headers.length);
|
||||
cursor += 4;
|
||||
|
||||
if (sab.byteLength < cursor + headers.length)
|
||||
sab.grow(cursor + headers.length);
|
||||
u8view.set(new TextEncoder().encode(headers), cursor);
|
||||
cursor += headers.length;
|
||||
|
||||
view.setUint32(cursor, xhr.response.byteLength);
|
||||
cursor += 4;
|
||||
|
||||
if (sab.byteLength < cursor + xhr.response.byteLength)
|
||||
sab.grow(cursor + xhr.response.byteLength);
|
||||
u8view.set(new Uint8Array(xhr.response), cursor);
|
||||
|
||||
// release the lock, main thread will stop spinning now
|
||||
view.setUint8(0, 1);
|
||||
};
|
||||
}
|
||||
);
|
1
src/types.d.ts
vendored
1
src/types.d.ts
vendored
|
@ -42,6 +42,7 @@ interface ScramjetConfig {
|
|||
thread: string;
|
||||
client: string;
|
||||
codecs: string;
|
||||
sync: string;
|
||||
flags: ScramjetFlags;
|
||||
siteflags?: Record<string, ScramjetFlags>;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue