mirror of https://github.com/hunshcn/gh-proxy
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
3.6 KiB
147 lines
3.6 KiB
'use strict' |
|
|
|
/** |
|
* static files (404.html, sw.js, conf.js) |
|
*/ |
|
const ASSET_URL = 'https://hunshcn.github.io/gh-proxy' |
|
|
|
/** @type {RequestInit} */ |
|
const PREFLIGHT_INIT = { |
|
status: 204, |
|
headers: new Headers({ |
|
'access-control-allow-origin': '*', |
|
'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS', |
|
'access-control-max-age': '1728000', |
|
}), |
|
} |
|
|
|
/** |
|
* @param {any} body |
|
* @param {number} status |
|
* @param {Object<string, string>} headers |
|
*/ |
|
function makeRes(body, status = 200, headers = {}) { |
|
headers['access-control-allow-origin'] = '*' |
|
return new Response(body, {status, headers}) |
|
} |
|
|
|
|
|
/** |
|
* @param {string} urlStr |
|
*/ |
|
function newUrl(urlStr) { |
|
try { |
|
return new URL(urlStr) |
|
} catch (err) { |
|
return null |
|
} |
|
} |
|
|
|
|
|
addEventListener('fetch', e => { |
|
const ret = fetchHandler(e) |
|
.catch(err => makeRes('cfworker error:\n' + err.stack, 502)) |
|
e.respondWith(ret) |
|
}) |
|
|
|
|
|
/** |
|
* @param {FetchEvent} e |
|
*/ |
|
async function fetchHandler(e) { |
|
const req = e.request |
|
const urlStr = req.url |
|
const urlObj = new URL(urlStr) |
|
let path = urlObj.searchParams.get('q') |
|
if(path) |
|
{ |
|
return Response.redirect('https://' + urlObj.host + '/' + path, 301) |
|
} |
|
// cfworker 会把路径中的 `//` 合并成 `/` |
|
path = urlObj.href.substr(urlObj.origin.length + 1).replace(/^https?:\/+/, 'https://') |
|
const exp = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:releases|archive|info|git-upload-pack).*$/ |
|
const exp2 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:blob)\/.*$/ |
|
if (path.search(exp)===0) { |
|
return httpHandler(req, path) |
|
}else if(path.search(exp2)===0){ |
|
const newUrl = path.replace('/blob/', '@').replace(/^(?:https?:\/\/)?github\.com/, 'https://cdn.jsdelivr.net/gh') |
|
return Response.redirect(newUrl, 302) |
|
} else { |
|
return fetch(ASSET_URL + path) |
|
} |
|
} |
|
|
|
|
|
/** |
|
* @param {Request} req |
|
* @param {string} pathname |
|
*/ |
|
function httpHandler(req, pathname) { |
|
const reqHdrRaw = req.headers |
|
|
|
// preflight |
|
if (req.method === 'OPTIONS' && |
|
reqHdrRaw.has('access-control-request-headers') |
|
) { |
|
return new Response(null, PREFLIGHT_INIT) |
|
} |
|
|
|
let rawLen = '' |
|
|
|
const reqHdrNew = new Headers(reqHdrRaw) |
|
|
|
const refer = reqHdrNew.get('referer') |
|
|
|
let urlStr = pathname |
|
if (urlStr.startsWith('github')) { |
|
urlStr = 'https://' + urlStr |
|
} |
|
const urlObj = newUrl(urlStr) |
|
|
|
/** @type {RequestInit} */ |
|
const reqInit = { |
|
method: req.method, |
|
headers: reqHdrNew, |
|
redirect: 'follow', |
|
body: req.body |
|
} |
|
return proxy(urlObj, reqInit, rawLen, 0) |
|
} |
|
|
|
|
|
/** |
|
* |
|
* @param {URL} urlObj |
|
* @param {RequestInit} reqInit |
|
*/ |
|
async function proxy(urlObj, reqInit, rawLen) { |
|
const res = await fetch(urlObj.href, reqInit) |
|
const resHdrOld = res.headers |
|
const resHdrNew = new Headers(resHdrOld) |
|
|
|
// verify |
|
if (rawLen) { |
|
const newLen = resHdrOld.get('content-length') || '' |
|
const badLen = (rawLen !== newLen) |
|
|
|
if (badLen) { |
|
return makeRes(res.body, 400, { |
|
'--error': `bad len: ${newLen}, except: ${rawLen}`, |
|
'access-control-expose-headers': '--error', |
|
}) |
|
} |
|
} |
|
const status = res.status |
|
resHdrNew.set('access-control-expose-headers', '*') |
|
resHdrNew.set('access-control-allow-origin', '*') |
|
|
|
resHdrNew.delete('content-security-policy') |
|
resHdrNew.delete('content-security-policy-report-only') |
|
resHdrNew.delete('clear-site-data') |
|
|
|
return new Response(res.body, { |
|
status, |
|
headers: resHdrNew, |
|
}) |
|
} |
|
|
|
|