Lufs's Blog

Life feeds on negative entropy.

使用 Cloudflare 对 GitHub 文件加速

Lufs's Avatar 2020-05-09 日常分享

  1. 1. 部署
  2. 2. 「美化」
  3. 3. 应用
  4. 4. 碎碎念

今年四月初,在频道看见一个可以利用 Cloudflare 加速 GitHub 文件的项目。

我看见后,便按起了 command + Ccommand + V

复制粘贴保存解析,设置 worker 规则,一顿操作

啪,GitHub 加速就是我滴啦

GitHub 开源项目
我「美化」过的站点

部署

要自己部署很简单,把全部的 代码 粘贴到 Cloudflare 就行了

项目有中文文档,Cloudflare 现在也有中文,自己就可以轻松部署(

"use strict";

/**
* static files (404.html, sw.js, conf.js)
*/
const ASSET_URL = "https://hunshcn.github.io/gh-proxy";
// 前缀,如果自定义路由为example.com/gh/*,将PREFIX改为 '/gh/',注意,少一个杠都会错!
const PREFIX = "/";
// git使用cnpmjs镜像、分支文件使用jsDelivr镜像的开关,0为关闭,默认开启
const Config = {
jsdelivr: 1,
cnpmjs: 1,
};

/** @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 + PREFIX + path, 301);
}
// cfworker 会把路径中的 `//` 合并成 `/`
path = urlObj.href
.substr(urlObj.origin.length + PREFIX.length)
.replace(/^https?:\/+/, "https://");
const exp1 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:releases|archive)\/.*$/i;
const exp2 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:blob)\/.*$/i;
const exp3 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:info|git-upload-pack).*$/i;
const exp4 = /^(?:https?:\/\/)?raw\.githubusercontent\.com\/.+?\/.+?\/.+?\/.+$/i;
if (
path.search(exp1) === 0 ||
(!Config.jsdelivr && path.search(exp2) === 0) ||
(!Config.cnpmjs && (path.search(exp3) === 0 || path.search(exp4)))
) {
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 if (path.search(exp3) === 0) {
const newUrl = path.replace(
/^(?:https?:\/\/)?github\.com/,
"https://github.com.cnpmjs.org"
);
return Response.redirect(newUrl, 302);
} else if (path.search(exp4) === 0) {
const newUrl = path
.replace(/(?<=com\/.+?\/.+?)\/(.+?\/)/, "@$1")
.replace(
/^(?:https?:\/\/)?raw\.githubusercontent\.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);

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,
});
}

将这上面的代码片段复制粘贴到 Workers 里就可以啦(没错,就是那么简单)

「美化」

我觉得越来的网页有点复杂,就对它进行了一点点的精简/美化

顺便适配了下暗色主题~

按钮那边加了个花里胡哨的动画

white
dark

应用

-const ASSET_URL = 'https://hunshcn.github.io/gh-proxy'
+const ASSET_URL = 'https://isteed.cc/gh'

将上面的 ASSET_URL 后的网址替换成 https://isteed.cc/gh 就好了

碎碎念

GitHub 作为全球最大的代码托管平台之一,但由于网络环境的问题,国内下载/访问速度普遍偏慢(我所在地区还无法正常访问 raw.githubusercontent.com

好了,水完一篇了~

本文作者 : Lufs
本文采用 CC BY-NC-SA 4.0 许可协议。转载和引用时请注意遵守协议!
本文链接 : https://blog.isteed.cc/post/cloudflare-gh-proxy/

本文最后更新于 天前,文中所描述的信息可能已发生改变