summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormayx2025-08-01 12:07:43 +0000
committermayx2025-08-01 12:07:43 +0000
commit2c629c95b9f2e93e20202f5f6be48bf70d59cc42 (patch)
tree7102de962a4eb371cb86f5f52053cd62ee956a6e
parentd1e734ede7de4fa662deda877cfaa608b5591f6d (diff)
Update 2 files
- /assets/Mabbs.zip - /_posts/2025-08-01-sw-proxy.md
-rw-r--r--_posts/2025-08-01-sw-proxy.md110
-rw-r--r--assets/Mabbs.zipbin44664 -> 44667 bytes
2 files changed, 110 insertions, 0 deletions
diff --git a/_posts/2025-08-01-sw-proxy.md b/_posts/2025-08-01-sw-proxy.md
new file mode 100644
index 0000000..6636efd
--- /dev/null
+++ b/_posts/2025-08-01-sw-proxy.md
@@ -0,0 +1,110 @@
+---
+layout: post
+title: 用Service Worker实现一个反向代理
+tags: [浏览器, Service Worker, Worker, 反向代理]
+---
+
+ 现代浏览器真是强大,可以替代一些服务器的功能了!<!--more-->
+
+# 起因
+ 前段时间在和群友聊天的时候,提到了我博客的[分发方案](/2022/02/14/move.html),这么多年过去之后我已经在很多平台上[分发](/proxylist.html)了我的博客,不过这只是多重冗余,并不算去中心化(虽然我也有向IPFS同步,不过IPFS还得pin,也不太可靠)……所以这么看来,我的博客似乎还不算极其可靠😂?但其实不完全是这样。因为除了向不同平台的分发,我的博客还有一个全文搜索的功能。更重要的是,之前做[文章推荐功能](/2024/10/01/suggest.html)时,会把整个博客所有文章的文字存到访客浏览器的localStorage中。这么说来,只要有人访问了我博客的文章,他们的浏览器中就会保存一份我博客文章的完整文本副本。从这个角度看,可靠性应该算是相当高了吧?
+ 不过我之前的分发方案里还记录了一点,在GitHub Pages以外的平台我还打包了一份全站生成后的代码,之所以要全站打包,也是希望我的博客能尽可能的分发,考虑到几乎所有的Linux发行版一定有tar,而不一定有zip,所以我最终打包成了tgz格式。如果能让访客下载这个全站打包好的副本,相比于浏览器里只存储了文章文字的全文数据,这应该是一个更好的备份方式吧?毕竟我的博客本身也是我的作品……所以这个压缩包到底有什么地方可以用到呢?
+ 这时候我想起来,现代的浏览器功能已经非常强大了,甚至在浏览器里直接运行一个Web服务器也完全没问题。如果能让访客在浏览器里下载那个压缩包并运行一个Web服务器,那就相当于在他们本地设备上部署了一份我的博客副本。这样一来,除了我自己搭建的网站之外,这些访客的本地也运行着一个我的博客实例😆(当然,这份副本只有访客自己能看到)。
+
+# 研究实现方案
+ 想要在浏览器上运行Web服务器其实很简单,那就是使用Service Worker,它可以完全离线在浏览器上工作。格式的话和以前写过的Cloudflare Worker非常相似,毕竟Cloudflare Worker就是模仿Service Worker的方式运行啊😂,所以我要是想写Service Worker应该很简单。
+ 有了执行的东西之后就是存储,在Service Worker上存储可以用Cache Storage,用它的话不仅可以保存文件的内容,还可以保存响应头之类的东西,用来和Service Worker配合使用非常的方便,不过既然是Cache,它的可靠性就不能保证了,浏览器很可能在需要的时候清除缓存内容,所以相比之下用IndexedDB应该会更可靠一些。
+ 那么接下来就该处理我的tgz文件了,tgz的本质是tar文件被gzip压缩之后的东西。浏览器解压gzip倒是简单,可以用Compression Stream API,但它也只能处理gzip了……对于tar的处理似乎就必须用第三方库。而tar的库在网上搜了搜似乎很少,网上找了个[tarjs](https://github.com/gera2ld/tarjs)库,文档写的也看不懂,⭐️也很少,看来是有这个需求的人很少啊,而且还要用现代JS那种开发方式,要用什么npm之类的。在[上一篇文章](/2025/07/24/screenshot.html)我就说过我不是专门写前端的,对在自己电脑上安装Node.js之类的东西很反感。后来问AI也完全写不出能用的代码,估计这个功能还是太小众了……另外又想到除了这个问题之外还要处理网站更新的时候该怎么通知Service Worker之类乱七八糟的事情……所以只好作罢😅。
+
+# 使用Service Worker进行反向代理
+ 这么看来离线运行我的博客似乎有点麻烦,不过既然都研究了一下Service Worker,不如想想其他能做的事情……比如当作反向代理?虽然在浏览器上搞反向代理好像意义不是很大……但值得一试。我之前见过一个项目叫做[jsproxy](https://github.com/EtherDream/jsproxy),它是用Service Worker实现的正向代理,这给了我一些启发。我在之前研究分发方案的时候发现了一些模仿GeoCities的复古静态网站托管平台,比如[Neocities](https://neocities.org)和[Nekoweb](https://nekoweb.org)。它们需要通过网页或API才能上传网站,不太方便使用CI/CD的方式部署。但是我又觉得它们的社区很有意思,所以想用Service Worker的方式反代到我的网站,显得我的网站是部署在它们上面一样。
+ 这个做起来非常简单,其实就和我以前用[Cloudflare Worker搭建反代](/2021/03/02/workers.html#%E9%A6%96%E5%85%88%E7%BB%99%E8%87%AA%E5%B7%B1%E6%90%AD%E4%B8%AA%E5%8F%8D%E4%BB%A3)几乎完全一样,遇到请求之后直接通过Fetch获取内容然后再返回就行,唯一不同的就是浏览器存在跨域策略,在跨域时只有对应网站存在合适的响应头才可以成功请求,还好我用的Pages服务大多都允许跨域。但是在我实际测试的时候发现这个允许跨域的等级不太一样,比如GitHub Pages的响应头里包含`Access-Control-Allow-Origin: *`,但是不允许OPTIONS方式请求,另外如果要修改请求头,在响应头里还要一一允许相应的请求头才行……当然对于这种问题解决起来很简单,就和我之前写的[订阅源预览](/2025/04/08/feed.html)一样,用[cloudflare-cors-anywhere](https://github.com/Zibri/cloudflare-cors-anywhere)搭建的CORS代理就可以,有了这个就可以轻松使用Service Worker反代其他网站了。
+ 当然对我来说其实有`Access-Control-Allow-Origin: *`就够了,我也不需要花里胡哨的请求方式,也不需要在请求头和请求体里加什么莫名其妙的东西,所以对我来说直接请求我的某一个镜像站就可以,于是代码如下:
+ **index.html**
+```html
+<!DOCTYPE html>
+<html>
+
+<head>
+ <meta charset="UTF-8" />
+ <title>Mayx的博客</title>
+</head>
+
+<body>
+ <script>
+ // 注册 Service Worker
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.register('/sw.js')
+ .then(registration => {
+ console.log('Service Worker 注册成功:', registration.scope);
+ // 刷新网页
+ location.reload();
+ })
+ .catch(error => {
+ console.error('Service Worker 注册失败:', error);
+ location="https://mabbs.github.io";
+ });
+ } else {
+ location="https://mabbs.github.io";
+ }
+ </script>
+ <h1>Redirecting&hellip;</h1>
+ <a href="https://mabbs.github.io">Click here if you are not redirected.</a>
+</body>
+
+</html>
+```
+ **sw.js**
+```javascript
+const TARGET_SITE = '被反代的网站'; //也可以用CORS代理
+
+self.addEventListener('install', event => {
+ // 强制立即激活新 Service Worker
+ event.waitUntil(self.skipWaiting());
+});
+
+self.addEventListener('activate', event => {
+ // 立即控制所有客户端
+ event.waitUntil(self.clients.claim());
+});
+
+self.addEventListener('fetch', event => {
+ if (new URL(event.request.url).origin == self.location.origin) {
+ event.respondWith(handleProxyRequest(event.request));
+ }
+});
+
+async function handleProxyRequest(request) {
+ try {
+ // 构建目标 URL
+ const targetUrl = new URL(request.url);
+ const proxyUrl = TARGET_SITE + targetUrl.pathname + targetUrl.search;
+
+ // 创建新请求(复制原请求属性)
+ const proxyRequest = new Request(proxyUrl, {
+ method: request.method,
+ // headers: request.headers,
+ // body: request.body
+ });
+
+ // 发送代理请求
+ const response = await fetch(proxyRequest);
+
+ // 返回修改后的响应
+ return new Response(response.body, {
+ status: response.status,
+ statusText: response.statusText,
+ headers: response.headers
+ });
+
+ } catch (error) {
+ console.error('Proxy error:', error);
+ return new Response('Proxy failed', { status: 500 });
+ }
+}
+```
+ 最终的实际效果: <https://mayx.nekoweb.org>
+
+# 感想
+ 虽然折腾了半天没能增强我博客的可靠性……但是体会到了现代浏览器的强大之处,难怪前几年会提出ChromeOS和PWA之类的东西,原来浏览器功能还是相当强大的,用了Service Worker以后即使是纯前端也可以有和使用服务器一样的体验,在过去的浏览器中要是想实现这样的功能……好像也不是不可能😂,用AJAX加服务器使用伪静态策略其实是可以做到的……其实Service Worker的功能更多还是在离线时使用的,我这个例子好像没体现它的优势😆。
+ 但总的来说相比以前想要实现这种反代的功能代码还是更清晰,也更简单了,也许以后如果有机会我又有心思让博客在访客浏览器上离线运行,那就可以体现Service Worker真正的优势了🤣。 \ No newline at end of file
diff --git a/assets/Mabbs.zip b/assets/Mabbs.zip
index 0986539..77b6a6c 100644
--- a/assets/Mabbs.zip
+++ b/assets/Mabbs.zip
Binary files differ