基于 Cloudflare Workers 的 Github 反向代理【JavaScript】
本文最后更新于 464 天前,其中的信息可能已经有所发展或是发生改变。

引言

作为全球最大的代码托管平台之一,GitHub [1]在开发者社区中具有重要地位。稳定且快速的访问对开发过程至关重要。然而,在中国境内,GitHub的响应速度和访问稳定性问题一直困扰着开发者,可能导致访问代码仓库时出现多种连接问题。

通常,人们会采用网络代理服务(Network Proxy)[2]或虚拟私人网络(VPN )[3]来提高GitHub的连接速度。然而,由于多种原因,这些方法在某些情况下可能同样存在连接不稳定等问题。此外,国内的代码托管平台如Gitee [4]等也被视为GitHub的替代选择。然而,这些平台可能缺乏全球范围内的开发者社区和资源,或者因安全考虑实施了各种限制政策,导致相对于GitHub的吸引力稍显不足。

在这一背景下,边缘计算技术 [5]为解决这一问题提供了新的可能性。作为一种新兴的分布式计算范式,边缘计算旨在将计算和数据处理尽可能地靠近数据源、终端用户和物联网设备,以提高计算资源的响应速度、降低延迟,并减轻中心化数据中心的负担。通过在物理世界的“边缘”执行计算,边缘计算架构已在多个应用领域取得显著成就。[6]

Cloudflare作为全球领先的网络性能和安全公司,在全球范围内部署的边缘节点和强大的技术实力,为边缘计算的发展注入了新的动力。[7]

Cloudflare Workers

Cloudflare Workers充当边缘计算平台,为开发者提供了一个强大的工具,能够在遍布全球的边缘节点上执行代码,实现即时响应并降低延迟。这一过渡不仅填补了边缘计算在某些技术方面的不足,同时为开发者提供了更便捷和灵活的途径,以实现边缘计算的目标。此平台还提供了 Serverless 的执行环境,使您能够轻松创建新应用程序或扩展现有应用程序,而无需配置或维护基础设施。[8]

什么是无服务器(Serverless)计算?

无服务器计算是一种按需提供后端服务的方法。无服务器提供者允许用户编写和部署代码,而不必担心底层基础设施。从无服务器提供商获得后端服务的公司将根据计算量来付费,由于这种服务是自动扩展的,不必预留和付费购买固定数量的带宽或服务器。请注意,虽然名为“无服务器”,实际上仍然需要物理服务器,但开发人员不需要考虑服务器细节。

无服务器计算允许开发人员在灵活的“按需付费”的基础上购买后端服务,这意味着开发人员仅需为使用的服务付费。这类似于从每月固定限额的手机数据套餐切换到按实际使用的每个字节数据收费的套餐。

“无服务器”一词在某种程度上具有误导性,因为它仍然依赖服务器提供这些后端服务,但所有服务器空间和基础设施问题都由提供商处理。无服务器意味着开发人员可以完全不用担心服务器。[9]

为什么可以?

要实现以上目标,一个重要的前提是,我们连接到Cloudflare Workers的速度必须比连接到GitHub的速度更快,否则无论边缘计算技术的优势如何,其效果都将受到限制。中国的互联网架构与世界其他地区存在差异。在中国境内,用户访问位于境外的数据中心时可能会面临拥塞、数据丢失等问题,从而影响用户的使用体验。

为了优化在中国境内的内容交付,关键在于建立遍布各地、地理位置分散的数据中心,并与每个地区的主要互联网服务提供商(ISP)建立连接。这正是Cloudflare中国网络所提供的服务。Cloudflare与京东云展开合作,以扩展网络覆盖。截至目前,Cloudflare在中国大陆已建立了超过45个数据中心,遍布约30个城市。[9]

怎么开始

以下是Cloudflare Workers官方文档链接:

梦开始的地方

addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request: Request) {
  return new Response('Hello worker!', {});
}

熟悉JavaScript的同学应该一眼就能看出,这段代码注册了一个fetch事件处理程序,当有请求到达时,程序将调用handleRequest函数处理请求,并返回一个包含Hello worker!内容的Response对象。

addEventListener(type, listener): void

在Cloudflare中,addEventListener函数用于定义Workers处理请求的监听器。目前有三种监听器类型,我们只需要使用fetch类型。如果注册了多个fetch监听器,当一个监听器没有调用event.respondWith()时,事件将被传递给下一个注册的监听器。

  • fetch:当请求到达时触发
  • scheduled:定时触发
  • queue:队列触发

处理请求

要实现页面的反向代理,我们只需解析用户的请求,然后将请求发送到相应的目标服务器,最后将响应返回给客户端。

addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request: Request) {
  const url = new URL(request.url);
  url.host = 'github.com';
  const newRequest = new Request(url.toString(), request);
  return fetch(newRequest);
}

通过解析用户的请求,我们可以获得用户访问的URL。其中,request.url返回用户请求的URL字符串。我们可以构造一个URL对象来解析其中的信息。

URL 接口用于解析,构造,规范化和编码 URL。它通过提供允许您轻松阅读和修改 URL 组件的属性来工作。通常,通过在调用 URL 的构造函数时将 URL 指定为字符串或提供相对 URL 和基本 URL 来创建新的 URL 对象。然后,您可以轻松读取 URL 的已解析组成部分或对 URL 进行更改。[11]

我们可以通过修改url.host来修改用户请求的目标服务器,然后构造一个新的请求对象newRequest,将其发送到目标服务器。最后,将目标服务器返回的响应返回给客户端即可。

这样,我们就完成了一个简单的反向代理。

file

还有点问题

尽管我们已经实现了一个简单的反向代理,但仍然存在一些问题。例如,页面中存在许多链接,这些链接指向GitHub,而不是我们的反向代理。这会导致一个问题,即当用户点击这些链接时,他们会被重定向回GitHub,而不是我们的反向代理(例如左上角的GitHub Logo)。

file

为解决这个问题,我们需要修改页面中的链接,使其指向我们的反向代理。这涉及解析页面,然后修改其中的链接。

一种选择是使用HTMLRewriter,它允许开发人员在 Cloudflare Workers 应用程序内部构建全面且富有表现力的 HTML 解析器。它可以被视为直接位于 Workers 应用程序内部的类似于 jQuery 的体验。依靠强大的 JavaScript API 来解析和转换 HTML,HTMLRewriter 允许开发人员构建功能强大的应用程序。[12] 但是,此方法仅能修改 HTML 中的链接,无法修改 JavaScript 或返回 JSON 中的链接。因此,使用正则表达式解析页面并修改链接可能是更好的选择。

addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request: Request) {
  const url = new URL(request.url);
  url.host = 'github.com';
  const newRequest = new Request(url.toString(), request);
  let newResponse = await fetch(newRequest);
  if (newResponse.body &&
    newResponse.headers.get('content-type')?.includes('text') ||
    newResponse.headers.get('content-type')?.includes('application/json')) {
    let newBody = await newResponse.text();
    newBody = newBody.replace(/\/(www.)?github\\.com/g, '\/github.sakurapuare.com');
    newResponse = new Response(newBody, newResponse);
  }
  return newResponse;
}

相较于上述代码,我们首先通过fetch(newRequest)获取返回的响应。然后,我们检查响应的content-type,只有在类型为textapplication/json时,才对其进行解析。[13] 随后,我们使用正则表达式将页面中的链接修改为指向我们的反向代理链接,并构建一个新的Response对象返回给客户端。

file

谨防网络爬虫

在上述代码中,我们对所有请求都进行了处理,一切看起来很好。然而,我们还需要考虑一种情况,即网络爬虫。网络爬虫是一种用于浏览万维网内容的自动化程序,通常定期抓取网站内容以在搜索引擎中建立索引。[14]

但这并不是我们所期望的,因为我们的网站只是GitHub的反向代理,不希望被搜索引擎收录。为此,我们需要屏蔽网络爬虫。

这时,robots.txt文件就派上用场了。robots.txt是一个文本文件,网站管理员可以在其中指定哪些网页可以被网络爬虫访问,哪些网页不能被网络爬虫访问。[15]

为避免网络爬虫,我们可以模拟一个robots.txt文件,然后将其返回给网络爬虫。以下是一个robots.txt文件示例,指示网络爬虫不要访问任何页面。

User-agent: *
Disallow: /

我们可以读取用户访问的pathname,然后判断是否为robots.txt,如果是,则返回上面的内容。否则,我们就对用户的请求进行处理。

addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request: Request) {
  const url = new URL(request.url);
  const {pathname} = url;
  if (pathname === '/robots.txt') {
    return new Response('User-agent: *\nDisallow: /', {
      headers: {
        'content-type': 'text/plain;charset=UTF-8',
      },
    });
  }
  url.host = 'github.com';
  const newRequest = new Request(url.toString(), request);
  let newResponse = await fetch(newRequest);
  if (newResponse.body &&
    newResponse.headers.get('content-type')?.includes('text') ||
    newResponse.headers.get('content-type')?.includes('application/json')) {
    let newBody = await newResponse.text();
    newBody = newBody.replace(/\/(www.)?github\\.com/g, '\/github.sakurapuare.com');
    newResponse = new Response(newBody, newResponse);
  }
  return newResponse;
}

file

需要注意,尽管robots.txt中使用了“允许”和“禁止”术语,但该协议纯粹是建议性的,取决于网络爬虫是否遵守;它不能强制执行文件中的任何状态。恶意网络爬虫可能不会遵守robots.txt,有些人甚至可能使用robots.txt作为指南来查找不允许的链接并直接访问。

为进一步防范,还可以结合Cloudflare的各种服务进行屏蔽,这里不再赘述。

Show me the Code

所有部署Workers需要的源代码已在Github开源

TS;WR (Too Short; Want Read)

直接使用吧 -> 免费Github加速反向代理

参考资料

本文作者:SakuraPuare
本文链接:https://blog.sakurapuare.com/archives/2023/03/cloudflare_worker_based_github_reverse_proxy/
版权声明:本文采用 CC BY-NC-SA 4.0 CN 协议进行许可
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇