2026-04-21 13:16:36 +08:00

198 lines
6.4 KiB
HTML

<!-- saved from url=(0055)https://jimmywarting.github.io/StreamSaver.js/mitm.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script>
// This will prevent the sw from restarting
let keepAlive = () => {
keepAlive = () => {};
var ping =
location.href.substr(0, location.href.lastIndexOf("/")) + "/ping";
var interval = setInterval(() => {
if (sw) {
sw.postMessage("ping");
} else {
fetch(ping).then((res) =>
res.text(!res.ok && clearInterval(interval))
);
}
}, 10000);
};
// message event is the first thing we need to setup a listner for
// don't want the opener to do a random timeout - instead they can listen for
// the ready event
// but since we need to wait for the Service Worker registration, we store the
// message for later
let messages = [];
window.onmessage = (evt) => messages.push(evt);
let sw = null;
let scope = "";
function registerWorker() {
return navigator.serviceWorker
.getRegistration("./")
.then((swReg) => {
return (
swReg ||
navigator.serviceWorker.register("sw.js", {
scope: "./"
})
);
})
.then((swReg) => {
const swRegTmp = swReg.installing || swReg.waiting;
scope = swReg.scope;
return (
(sw = swReg.active) ||
new Promise((resolve) => {
swRegTmp.addEventListener(
"statechange",
(fn = () => {
if (swRegTmp.state === "activated") {
swRegTmp.removeEventListener("statechange", fn);
sw = swReg.active;
resolve();
}
})
);
})
);
});
}
// Now that we have the Service Worker registered we can process messages
function onMessage(event) {
let {
data,
ports,
origin
} = event;
// It's important to have a messageChannel, don't want to interfere
// with other simultaneous downloads
if (!ports || !ports.length) {
throw new TypeError("[StreamSaver] You didn't send a messageChannel");
}
if (typeof data !== "object") {
throw new TypeError("[StreamSaver] You didn't send a object");
}
// the default public service worker for StreamSaver is shared among others.
// so all download links needs to be prefixed to avoid any other conflict
data.origin = origin;
// if we ever (in some feature versoin of streamsaver) would like to
// redirect back to the page of who initiated a http request
data.referrer = data.referrer || document.referrer || origin;
// pass along version for possible backwards compatibility in sw.js
data.streamSaverVersion = new URLSearchParams(location.search).get(
"version"
);
if (data.streamSaverVersion === "1.2.0") {
console.warn("[StreamSaver] please update streamsaver");
}
/** @since v2.0.0 */
if (!data.headers) {
console.warn(
"[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts"
);
} else {
// test if it's correct
// should thorw a typeError if not
new Headers(data.headers);
}
/** @since v2.0.0 */
if (typeof data.filename === "string") {
console.warn(
"[StreamSaver] You shouldn't send `data.filename` anymore. It should be included in the Content-Disposition header option"
);
// Do what File constructor do with fileNames
data.filename = data.filename.replace(/\//g, ":");
}
/** @since v2.0.0 */
if (data.size) {
console.warn(
"[StreamSaver] You shouldn't send `data.size` anymore. It should be included in the content-length header option"
);
}
/** @since v2.0.0 */
if (data.readableStream) {
console.warn(
"[StreamSaver] You should send the readableStream in the messageChannel, not throught mitm"
);
}
/** @since v2.0.0 */
if (!data.pathname) {
console.warn(
"[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)"
);
data.pathname =
Math.random().toString().slice(-6) + "/" + data.filename;
}
// remove all leading slashes
data.pathname = data.pathname.replace(/^\/+/g, "");
// remove protocol
let org = origin.replace(/(^\w+:|^)\/\//, "");
// set the absolute pathname to the download url.
data.url = new URL(`${scope + org}/${data.pathname}`).toString();
if (!data.url.startsWith(`${scope + org}/`)) {
throw new TypeError("[StreamSaver] bad `data.pathname`");
}
// This sends the message data as well as transferring
// messageChannel.port2 to the service worker. The service worker can
// then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
const transferable = data.readableStream ?
[ports[0], data.readableStream] :
[ports[0]];
if (!(data.readableStream || data.transferringReadable)) {
keepAlive();
}
return sw.postMessage(data, transferable);
}
if (window.opener) {
// The opener can't listen to onload event, so we need to help em out!
// (telling them that we are ready to accept postMessage's)
window.opener.postMessage("StreamSaver::loadedPopup", "*");
}
if (navigator.serviceWorker) {
registerWorker().then(() => {
window.onmessage = onMessage;
messages.forEach(window.onmessage);
});
} else {
// FF can ping sw with fetch from a secure hidden iframe
// shouldn't really be possible?
keepAlive();
}
</script>
</head>
<body></body>
</html>