Guest User

proxy code

a guest
Sep 9th, 2025
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 10.19 KB | Source Code | 0 0
  1. addEventListener("fetch", e => e.respondWith(handle(e.request)))
  2.  
  3. async function handle(request) {
  4.   if (request.method === "OPTIONS") {
  5.     return new Response(null, { status: 204, headers: corsHeaders() })
  6.   }
  7.  
  8.   const reqUrl    = new URL(request.url)
  9.   const proxyBase = `${reqUrl.origin}${reqUrl.pathname}`
  10.   let target      = reqUrl.searchParams.get("url")
  11.  
  12.   if (!target || typeof target !== "string") {
  13.     return new Response("Missing or invalid `url` param", {
  14.       status: 400,
  15.       headers: corsHeaders()
  16.     })
  17.   }
  18.  
  19.   // unwrap nested proxies
  20.   while (true) {
  21.     let tmp
  22.     try {
  23.       tmp = new URL(target)
  24.     } catch {
  25.       break
  26.     }
  27.     if (tmp.origin === reqUrl.origin && tmp.pathname === reqUrl.pathname) {
  28.       const inner = tmp.searchParams.get("url")
  29.       if (!inner) break
  30.       target = inner
  31.       continue
  32.     }
  33.     break
  34.   }
  35.  
  36.   // validate upstream URL
  37.   let upstream
  38.   try {
  39.     upstream = new URL(target)
  40.   } catch {
  41.     return new Response("Invalid `url` param", {
  42.       status: 400,
  43.       headers: corsHeaders()
  44.     })
  45.   }
  46.  
  47.   // forward request
  48.   const init = {
  49.     method:   request.method,
  50.     headers:  overrideHost(request.headers, upstream.host),
  51.     redirect: "follow"
  52.   }
  53.   if (!["GET","HEAD"].includes(request.method)) {
  54.     init.body = await request.clone().arrayBuffer()
  55.   }
  56.   const res         = await fetch(upstream.toString(), init)
  57.   const contentType = res.headers.get("Content-Type") || ""
  58.   const runtimePatcher = buildRuntimePatcher(proxyBase, upstream.origin)
  59.  
  60.   // HTML
  61.   if (contentType.includes("text/html")) {
  62.     const dir      = upstream.pathname.replace(/\/[^/]*$/, "/")
  63.     const baseHref = proxyBase + "?url=" + encodeURIComponent(upstream.origin + dir)
  64.  
  65.     const transformed = new HTMLRewriter()
  66.       .on("head", {
  67.         element(el) {
  68.           el.prepend(
  69.             `<base href="${baseHref}">` +
  70.             `<script>${runtimePatcher}</script>`,
  71.             { html: true }
  72.           )
  73.         }
  74.       })
  75.       .on("meta[http-equiv='Content-Security-Policy']", { element(el){ el.remove() } })
  76.       .on("a[href]",      new AttrRewriter("href",   proxyBase, upstream.origin))
  77.       .on("script[src]",  new AttrRewriter("src",    proxyBase, upstream.origin))
  78.       .on("link[href]",   new AttrRewriter("href",   proxyBase, upstream.origin))
  79.       .on("img[src]",     new AttrRewriter("src",    proxyBase, upstream.origin))
  80.       .on("img[srcset]",  new SrcsetRewriter(proxyBase, upstream.origin))
  81.       .on("source[src]",  new AttrRewriter("src",    proxyBase, upstream.origin))
  82.       .on("video[poster]",new AttrRewriter("poster", proxyBase, upstream.origin))
  83.       .on("iframe[src]",  new AttrRewriter("src",    proxyBase, upstream.origin))
  84.       .on("form[action]", new AttrRewriter("action", proxyBase, upstream.origin))
  85.       .transform(res)
  86.  
  87.     return new Response(transformed.body, {
  88.       status: transformed.status,
  89.       headers: mergeHeaders(
  90.         stripCSP(new Headers(transformed.headers)),
  91.         corsHeaders(),
  92.         { "Content-Type": "text/html; charset=UTF-8" }
  93.       )
  94.     })
  95.   }
  96.  
  97.   // CSS
  98.   if (contentType.includes("text/css")) {
  99.     const css  = await res.text()
  100.     const body = css
  101.       .replace(/url\((['"]?)(https?:\/\/[^'")]+)\1\)/g,
  102.                (_,q,u)=>`url(${q}${makeProxy(u,proxyBase)}${q})`)
  103.       .replace(/url\((['"]?)(\/[^'")]+)\1\)/g,
  104.                (_,q,p)=>`url(${q}${makeProxy(upstream.origin+p,proxyBase)}${q})`)
  105.     return new Response(body, {
  106.       status:  res.status,
  107.       headers: mergeHeaders(
  108.         stripCSP(new Headers({ "Content-Type":"text/css; charset=UTF-8" })),
  109.         corsHeaders()
  110.       )
  111.     })
  112.   }
  113.  
  114.   // JavaScript
  115.   if (contentType.includes("javascript")) {
  116.     const raw      = await res.text()
  117.     const isModule = /^\s*import\s+[\w{]/.test(raw)
  118.     let js = isModule ? raw : runtimePatcher + "\n" + raw
  119.  
  120.     js = js
  121.       .replace(/(['"`])\/([^'"`]+)\1/g,
  122.                (_,q,p)=>q+makeProxy(upstream.origin+"/"+p,proxyBase)+q)
  123.       .replace(/(['"`])\/\/([^'"`]+)\1/g,
  124.                (_,q,h)=>q+makeProxy("https://"+h,proxyBase)+q)
  125.       .replace(/(['"`])(https?:\/\/[^'"`]+)\1/g,
  126.                (_,q,u)=>q+makeProxy(u,proxyBase)+q)
  127.  
  128.     return new Response(js, {
  129.       status:  res.status,
  130.       headers: mergeHeaders(
  131.         stripCSP(new Headers({ "Content-Type":"application/javascript; charset=UTF-8" })),
  132.         corsHeaders()
  133.       )
  134.     })
  135.   }
  136.  
  137.   // Others
  138.   if (!contentType.includes("text/html") &&
  139.     !contentType.includes("text/css") &&
  140.     !contentType.includes("javascript")) {
  141.     return res
  142.   }
  143. }
  144.  
  145.  
  146. /**–– Helpers ––**/
  147.  
  148. function makeProxy(raw, base) {
  149.   return `${base}?url=${encodeURIComponent(raw)}`
  150. }
  151.  
  152. function overrideHost(orig, host) {
  153.   const h = new Headers(orig)
  154.   h.set("host", host)
  155.   return h
  156. }
  157.  
  158. function corsHeaders() {
  159.   return {
  160.     "Access-Control-Allow-Origin":  "*",
  161.     "Access-Control-Allow-Methods": "GET,POST,PUT,PATCH,DELETE,OPTIONS",
  162.     "Access-Control-Allow-Headers": "*"
  163.   }
  164. }
  165.  
  166. function stripCSP(h) {
  167.   const out = new Headers(h)
  168.   out.delete("content-security-policy")
  169.   out.delete("content-security-policy-report-only")
  170.   return out
  171. }
  172.  
  173. function mergeHeaders(...sources) {
  174.   const h = new Headers()
  175.   for (const src of sources) {
  176.     if (src instanceof Headers) {
  177.       for (const [k,v] of src.entries()) h.set(k,v)
  178.     } else {
  179.       for (const [k,v] of Object.entries(src)) h.set(k,v)
  180.     }
  181.   }
  182.   return h
  183. }
  184.  
  185. /** rewrite HTML attributes */
  186. class AttrRewriter {
  187.   constructor(attr, proxyBase, origin) {
  188.     this.attr      = attr
  189.     this.proxyBase = proxyBase
  190.     this.origin    = origin
  191.   }
  192.   element(el) {
  193.     const raw = el.getAttribute(this.attr)
  194.     if (!raw) return
  195.     let abs
  196.     try { abs = new URL(raw, this.origin).toString() }
  197.     catch { return }
  198.     if (abs.startsWith(this.proxyBase)) return
  199.     el.setAttribute(this.attr, makeProxy(abs, this.proxyBase))
  200.   }
  201. }
  202.  
  203. /** rewrite srcset lists */
  204. class SrcsetRewriter {
  205.   constructor(proxyBase, origin) {
  206.     this.proxyBase = proxyBase
  207.     this.origin    = origin
  208.   }
  209.   element(el) {
  210.     const srcset = el.getAttribute("srcset")
  211.     if (!srcset) return
  212.     const parts = srcset.split(/\s*,\s*/)
  213.     const out   = parts.map(p=>{
  214.       const [url, desc] = p.split(/\s+/,2)
  215.       let abs
  216.       try { abs = new URL(url, this.origin).toString() }
  217.       catch { return p }
  218.       const prox = makeProxy(abs, this.proxyBase)
  219.       return desc ? `${prox} ${desc}` : prox
  220.     })
  221.     el.setAttribute("srcset", out.join(", "))
  222.   }
  223. }
  224.  
  225. /** build a script that forces all in-page network calls through your proxy */
  226. function buildRuntimePatcher(proxyBase, ORG) {
  227.   const PB = JSON.stringify(proxyBase)
  228.   const O  = JSON.stringify(ORG)
  229.   return `
  230. ;(function(){
  231.   const PB=${PB}, ORG=${O}, enc=encodeURIComponent;
  232.   function shouldProxy(u){
  233.     return typeof u==="string" && /^https?:/.test(u) && !u.startsWith(PB)
  234.   }
  235.  
  236.   // patch fetch
  237.   const _fetch = window.fetch
  238.   window.fetch = function(input, init){
  239.     try {
  240.       let u = input instanceof Request ? input.url : input
  241.       if (typeof u !== "string") return _fetch.call(this, input, init)
  242.       if (u.startsWith("/")) u = ORG + u
  243.       if (shouldProxy(u)) u = PB + "?url=" + enc(u)
  244.       const req = input instanceof Request
  245.                 ? new Request(u, input)
  246.                 : u
  247.       return _fetch.call(this, req, init)
  248.     } catch (err) {
  249.       console.error("Proxy fetch error:", err)
  250.       return _fetch.call(this, input, init)
  251.     }
  252.   }
  253.  
  254.   // patch XHR.open
  255.   const _xo = XMLHttpRequest.prototype.open
  256.   XMLHttpRequest.prototype.open = function(m, u, ...a){
  257.     try {
  258.       if (typeof u==="string" && u.startsWith("/")) u = ORG + u
  259.       if (shouldProxy(u)) u = PB + "?url=" + enc(u)
  260.     } catch (_) {}
  261.     return _xo.call(this, m, u, ...a)
  262.   }
  263.  
  264.   // patch WebSocket
  265.   const _ws = window.WebSocket
  266.   window.WebSocket = function(u, ...a){
  267.     let url = u
  268.     try {
  269.       if (typeof url==="string" && url.startsWith("/")) url = ORG + url
  270.       if (shouldProxy(url)) url = PB + "?url=" + enc(url)
  271.     } catch (_) {}
  272.     return new _ws(url, ...a)
  273.   }
  274.  
  275.   // patch EventSource
  276.   const _es = window.EventSource
  277.   window.EventSource = function(u, opts){
  278.     let url = u
  279.     try {
  280.       if (typeof url==="string" && url.startsWith("/")) url = ORG + url
  281.       if (shouldProxy(url)) url = PB + "?url=" + enc(url)
  282.     } catch (_) {}
  283.     return new _es(url, opts)
  284.   }
  285.  
  286.   // patch Worker & SharedWorker
  287.   const _Worker = window.Worker
  288.   window.Worker = function(u, opts){
  289.     let url = u
  290.     try {
  291.       if (typeof url==="string" && url.startsWith("/")) url = ORG + url
  292.       if (shouldProxy(url)) url = PB + "?url=" + enc(url)
  293.     } catch (_) {}
  294.     return new _Worker(url, opts)
  295.   }
  296.   if (window.SharedWorker) {
  297.     const _SW = window.SharedWorker
  298.     window.SharedWorker = function(u, name){
  299.       let url = u
  300.       try {
  301.         if (typeof url==="string" && url.startsWith("/")) url = ORG + url
  302.         if (shouldProxy(url)) url = PB + "?url=" + enc(url)
  303.       } catch (_) {}
  304.       return new _SW(url, name)
  305.     }
  306.   }
  307.  
  308.   // patch importScripts
  309.   if (typeof importScripts==="function") {
  310.     const _is = importScripts
  311.     importScripts = function(...urls){
  312.       urls = urls.map(u=>{
  313.         try {
  314.           if (u.startsWith("/")) u = ORG + u
  315.           if (shouldProxy(u)) u = PB + "?url=" + enc(u)
  316.         } catch(_) {}
  317.         return u
  318.       })
  319.       return _is.apply(this, urls)
  320.     }
  321.   }
  322.  
  323.   // patch element.src & href
  324.   [
  325.     ["src", [HTMLScriptElement, HTMLImageElement, HTMLIFrameElement,
  326.              HTMLVideoElement, HTMLAudioElement]],
  327.     ["href",[HTMLAnchorElement, HTMLLinkElement]]
  328.   ].forEach(([prop, ctors])=>{
  329.     ctors.forEach(C=>{
  330.       const desc = Object.getOwnPropertyDescriptor(C.prototype, prop)
  331.       Object.defineProperty(C.prototype, prop, {
  332.         get: desc.get,
  333.         set(v){
  334.           let url = v
  335.           try {
  336.             if (typeof url==="string" && url.startsWith("/")) url = ORG + url
  337.             if (shouldProxy(url)) url = PB + "?url=" + enc(url)
  338.           } catch(_) {}
  339.           desc.set.call(this, url)
  340.         }
  341.       })
  342.     })
  343.   })
  344. })();`.trim()
  345. }
  346.  
Advertisement
Add Comment
Please, Sign In to add comment