Skip to content

Instantly share code, notes, and snippets.

@DoingDog
Forked from abersheeran/proxy.worker.js
Last active May 22, 2023 13:24
Show Gist options
  • Save DoingDog/79fcd195189e436e2af4af825cdaee7a to your computer and use it in GitHub Desktop.
Save DoingDog/79fcd195189e436e2af4af825cdaee7a to your computer and use it in GitHub Desktop.
A proxy download cloudflare worker
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request).catch(({ stack }) => new Response(stack, { status: 500 })));
});
function getQueryParam(url, paramName) {
const queryStartIdx = url.indexOf("?");
if (queryStartIdx < 0) {
return null;
}
const query = url.slice(queryStartIdx + 1);
const params = new URLSearchParams(query);
const paramValue = params.get(paramName);
if (!paramValue || paramValue === "") {
return null;
}
return paramValue;
}
async function handleRequest(request) {
const url = getUrl(request);
if (!url) {
return new Response(Page, {
headers: { "Content-Type": "text/html" },
status: 404,
});
}
let response = await fetch(
new Request(url, {
body: request.body,
headers: request.headers,
method: request.method,
redirect: "follow",
})
);
const modType = getQueryParam(request.url, "type");
const headers = new Headers(response.headers);
if (modType === "reverse") {
const fileType = response.headers.get("Content-Type");
if (fileType) {
function getHost(str) {
return str.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, "$1");
}
const cloneBody = await response.clone().text();
const proxyHost = getHost(request.url);
function processUrl(str, proxy, proxyUrl) {
const addProxy = `${proxy}/?type=reverse&url=`;
if (str.startsWith("http")) {
return addProxy + encodeURIComponent(str);
} else if (str.startsWith("//")) {
return addProxy + encodeURIComponent(`https:${str}`);
} else if (str.startsWith("./")) {
return addProxy + encodeURIComponent(proxyUrl.replace(/^(https?:\/\/.+)\/[^\/]*$/, "$1") + str.substring(1));
} else if (str.startsWith("/")) {
return addProxy + encodeURIComponent(getHost(proxyUrl) + str);
} else {
return addProxy + encodeURIComponent(proxyUrl.replace(/^(https?:\/\/.+)\/[^\/]*$/, "$1/") + str);
}
}
if (fileType.startsWith("text/html")) {
const regex = /((?:href|src|action)=["'])(.*?)["']/gi;
const replacedBody = cloneBody.replace(regex, (match, p1, p2) => `${p1}${processUrl(p2, proxyHost, url)}"`);
if (headers.get("Content-Security-Policy")) {
headers.set("Content-Security-Policy", `default-src *;`);
}
return new Response(replacedBody, {
headers,
status: response.status,
});
} else if (fileType.startsWith("text/css")) {
const cssRegex = /url\(["'](.*?)["']\)/gi;
const replacedCss = cloneBody.replace(cssRegex, (match, p1) => `url("${processUrl(p1, proxyHost, url)}")`);
return new Response(replacedCss, {
headers,
status: response.status,
});
}
}
return new Response(response.body, {
headers,
status: response.status,
});
}
if (modType) {
headers.set("Content-Type", `${modType}`);
}
if (getQueryParam(request.url, "download")) {
const contentDisposition = headers.get("Content-Disposition");
if (!contentDisposition) {
const filenameinurl = url.split("/").pop();
headers.set("Content-Disposition", `attachment; filename="${filenameinurl}"`);
}
} else {
headers.delete("Content-Disposition");
}
if (getQueryParam(request.url, "fetch")) {
return new Response(response.body, {
headers,
status: response.status,
});
} else {
let { readable, writable } = new TransformStream();
response.body.pipeTo(writable);
return new Response(readable, {
headers,
status: response.status,
});
}
}
const getUrl = ({ url }) => {
const { pathname, searchParams } = new URL(url);
if (pathname.startsWith("/http")) {
return pathname
.slice(1)
.replace(/http:\/(?!\/)/, "http://")
.replace(/https:\/(?!\/)/, "https://");
}
const searchParamsUrl = searchParams.get("url");
if (searchParamsUrl?.startsWith("http")) {
return searchParamsUrl;
}
};
const Page = `
<!doctype html>
<html>
<head>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; fill=&quot;currentColor&quot; class=&quot;bi bi-flower1&quot; viewBox=&quot;0 0 16 16&quot;><path d=&quot;M6.174 1.184a2 2 0 0 1 3.652 0A2 2 0 0 1 12.99 3.01a2 2 0 0 1 1.826 3.164 2 2 0 0 1 0 3.652 2 2 0 0 1-1.826 3.164 2 2 0 0 1-3.164 1.826 2 2 0 0 1-3.652 0A2 2 0 0 1 3.01 12.99a2 2 0 0 1-1.826-3.164 2 2 0 0 1 0-3.652A2 2 0 0 1 3.01 3.01a2 2 0 0 1 3.164-1.826zM8 1a1 1 0 0 0-.998 1.03l.01.091c.012.077.029.176.054.296.049.241.122.542.213.887.182.688.428 1.513.676 2.314L8 5.762l.045-.144c.248-.8.494-1.626.676-2.314.091-.345.164-.646.213-.887a4.997 4.997 0 0 0 .064-.386L9 2a1 1 0 0 0-1-1zM2 9l.03-.002.091-.01a4.99 4.99 0 0 0 .296-.054c.241-.049.542-.122.887-.213a60.59 60.59 0 0 0 2.314-.676L5.762 8l-.144-.045a60.59 60.59 0 0 0-2.314-.676 16.705 16.705 0 0 0-.887-.213 4.99 4.99 0 0 0-.386-.064L2 7a1 1 0 1 0 0 2zm7 5-.002-.03a5.005 5.005 0 0 0-.064-.386 16.398 16.398 0 0 0-.213-.888 60.582 60.582 0 0 0-.676-2.314L8 10.238l-.045.144c-.248.8-.494 1.626-.676 2.314-.091.345-.164.646-.213.887a4.996 4.996 0 0 0-.064.386L7 14a1 1 0 1 0 2 0zm-5.696-2.134.025-.017a5.001 5.001 0 0 0 .303-.248c.184-.164.408-.377.661-.629A60.614 60.614 0 0 0 5.96 9.23l.103-.111-.147.033a60.88 60.88 0 0 0-2.343.572c-.344.093-.64.18-.874.258a5.063 5.063 0 0 0-.367.138l-.027.014a1 1 0 1 0 1 1.732zM4.5 14.062a1 1 0 0 0 1.366-.366l.014-.027c.01-.02.021-.048.036-.084a5.09 5.09 0 0 0 .102-.283c.078-.233.165-.53.258-.874a60.6 60.6 0 0 0 .572-2.343l.033-.147-.11.102a60.848 60.848 0 0 0-1.743 1.667 17.07 17.07 0 0 0-.629.66 5.06 5.06 0 0 0-.248.304l-.017.025a1 1 0 0 0 .366 1.366zm9.196-8.196a1 1 0 0 0-1-1.732l-.025.017a4.951 4.951 0 0 0-.303.248 16.69 16.69 0 0 0-.661.629A60.72 60.72 0 0 0 10.04 6.77l-.102.111.147-.033a60.6 60.6 0 0 0 2.342-.572c.345-.093.642-.18.875-.258a4.993 4.993 0 0 0 .367-.138.53.53 0 0 0 .027-.014zM11.5 1.938a1 1 0 0 0-1.366.366l-.014.027c-.01.02-.021.048-.036.084a5.09 5.09 0 0 0-.102.283c-.078.233-.165.53-.258.875a60.62 60.62 0 0 0-.572 2.342l-.033.147.11-.102a60.848 60.848 0 0 0 1.743-1.667c.252-.253.465-.477.629-.66a5.001 5.001 0 0 0 .248-.304l.017-.025a1 1 0 0 0-.366-1.366zM14 9a1 1 0 0 0 0-2l-.03.002a4.996 4.996 0 0 0-.386.064c-.242.049-.543.122-.888.213-.688.182-1.513.428-2.314.676L10.238 8l.144.045c.8.248 1.626.494 2.314.676.345.091.646.164.887.213a4.996 4.996 0 0 0 .386.064L14 9zM1.938 4.5a1 1 0 0 0 .393 1.38l.084.035c.072.03.166.064.283.103.233.078.53.165.874.258a60.88 60.88 0 0 0 2.343.572l.147.033-.103-.111a60.584 60.584 0 0 0-1.666-1.742 16.705 16.705 0 0 0-.66-.629 4.996 4.996 0 0 0-.304-.248l-.025-.017a1 1 0 0 0-1.366.366zm2.196-1.196.017.025a4.996 4.996 0 0 0 .248.303c.164.184.377.408.629.661A60.597 60.597 0 0 0 6.77 5.96l.111.102-.033-.147a60.602 60.602 0 0 0-.572-2.342c-.093-.345-.18-.642-.258-.875a5.006 5.006 0 0 0-.138-.367l-.014-.027a1 1 0 1 0-1.732 1zm9.928 8.196a1 1 0 0 0-.366-1.366l-.027-.014a5 5 0 0 0-.367-.138c-.233-.078-.53-.165-.875-.258a60.619 60.619 0 0 0-2.342-.572l-.147-.033.102.111a60.73 60.73 0 0 0 1.667 1.742c.253.252.477.465.66.629a4.946 4.946 0 0 0 .304.248l.025.017a1 1 0 0 0 1.366-.366zm-3.928 2.196a1 1 0 0 0 1.732-1l-.017-.025a5.065 5.065 0 0 0-.248-.303 16.705 16.705 0 0 0-.629-.661A60.462 60.462 0 0 0 9.23 10.04l-.111-.102.033.147a60.6 60.6 0 0 0 .572 2.342c.093.345.18.642.258.875a4.985 4.985 0 0 0 .138.367.575.575 0 0 0 .014.027zM8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z&quot;/></svg>">
<meta charset=UTF-8>
<meta http-equiv=X-UA-Compatible content="IE=edge">
<meta name=viewport content="width=device-width,initial-scale=1">
<title>Proxy</title>
<style>body,div,form{display:flex}body,select[name=type]{color:#fff;background-color:#000}body,html{height:100%;overflow:hidden}body{justify-content:center;align-items:center;height:100vh;margin:0;font-family:Arial,'Helvetica Neue',Helvetica,sans-serif}form{flex-direction:column;align-items:center}input[name=url]{margin-right:10px;flex-grow:1;height:30px;font-size:16px;border-radius:5px;border:2px solid #fff;padding:5px;color:#fff;background-color:transparent}button#download,button#fetch{background-color:red;color:#fff;border:none;border-radius:5px;cursor:pointer;height:30px;padding:5px;margin:10px}input#download:checked+button#download,input#fetch:checked+button#fetch{background-color:green;color:#fff}div{align-items:center}button[type=button],button[type=submit]{height:30px;font-size:16px;border-radius:5px;border:none;padding:5px 10px;cursor:pointer}button[type=submit]{background-color:#007bff;color:#fff;margin-right:10px}button[type=button]{background-color:#333;color:#eee}button[type=button]:hover,button[type=submit]:hover{opacity:.8}select[name=type]{margin-top:10px;height:30px;font-size:16px;border-radius:5px;border:2px solid #fff;padding:5px}</style>
</head>
<body>
<form>
<div>
<input name=url type=url required>
<button type=submit>→</button>
<button type=button onclick=clearInput()>Clear</button>
</div>
<br>
<select name=type id=type>
<option value="" selected>As Is</option>
<option value=application/json>application/json</option>
<option value=application/msword>application/msword</option>
<option value=application/octet-stream>application/octet-stream</option>
<option value=application/pdf>application/pdf</option>
<option value=application/xml>application/xml</option>
<option value=application/zip>application/zip</option>
<option value=audio/mp3>audio/mp3</option>
<option value=audio/ogg>audio/ogg</option>
<option value=audio/wav>audio/wav</option>
<option value=audio/webm>audio/webm</option>
<option value=image/avif>image/avif</option>
<option value=image/bmp>image/bmp</option>
<option value=image/gif>image/gif</option>
<option value=image/jpeg>image/jpeg</option>
<option value=image/png>image/png</option>
<option value=image/svg+xml>image/svg+xml</option>
<option value=image/webp>image/webp</option>
<option value=image/webp>image/webp</option>
<option value="text/css;charset=utf8">text/css</option>
<option value="text/html;charset=utf8">text/html</option>
<option value="text/javascript;charset=utf8">text/javascript</option>
<option value="text/plain;charset=utf8">text/plain</option>
<option value=video/mp2t>video/mp2t</option>
<option value=video/mp4>video/mp4</option>
<option value=video/ogg>video/ogg</option>
<option value=video/quicktime>video/quicktime</option>
<option value=video/webm>video/webm</option>
<option value=video/x-msvideo>video/x-msvideo</option>
<option value=reverse>Reverse</option>
</select><br>
<div>
<input type=checkbox id=download name=download value=true style=display:none>
<button type=button id=download onclick=toggleDownload()>As File</button>
<input type=checkbox id=fetch name=fetch value=true style=display:none>
<button type=button id=fetch onclick=toggleFetch()>Use Fetch</button>
</div>
</form>
<script>function clearInput(){document.querySelector("input[name='url']").value=""}function toggleDownload(){var e=document.getElementById("download"),c=document.querySelector("input[name='download']");c.checked?(e.style.backgroundColor="red",c.checked=!1):(e.style.backgroundColor="green",c.checked=!0)}function toggleFetch(){var e=document.getElementById("fetch"),c=document.querySelector("input[name='fetch']");c.checked?(e.style.backgroundColor="red",c.checked=!1):(e.style.backgroundColor="green",c.checked=!0)}</script>
</body>
</html>
`;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment