使用 Cloudflare 对 GitHub 文件加速

使用 Cloudflare 对 GitHub 文件加速

发布日期:2020-05-09文章字数:1K

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

我看见后,便按起了 command + C,command + 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

好了,水完一篇了~