Selaa lähdekoodia

更新 v0.1.0

zjcqoo 6 vuotta sitten
vanhempi
sitoutus
8bb7735e4b
12 muutettua tiedostoa jossa 229 lisäystä ja 187 poistoa
  1. 6 10
      README.md
  2. 9 4
      api.conf
  3. 2 3
      cf-worker/.eslintrc.json
  4. 1 18
      cf-worker/README.md
  5. 71 59
      cf-worker/index.js
  6. 6 25
      changelogs/README.md
  7. 70 0
      changelogs/v0.1.0.md
  8. 3 3
      i.sh
  9. 6 8
      log-svc/backup.sh
  10. 29 32
      lua/http-dec-req-hdr.lua
  11. 23 24
      lua/http-enc-res-hdr.lua
  12. 3 1
      nginx.conf

+ 6 - 10
README.md

@@ -1,17 +1,9 @@
 
 # 更新
 
-* 2019-06-22 [cfworker 无服务器版](cf-worker) 发布,长期使用演示服务的请使用该版本。
-
-* 2019-06-11 前端脚本调整,首页可离线访问(如果长时间加载中,尝试多刷新几次或者隐身模式访问)
-
-* 2019-05-30 更新 cfworker,对 ytb 视频进行了优化(推荐选 1080p+,不会增加服务器压力)
+* 2019-07-24 [v0.1.0](tree/0.1.0) 发布,主要修复了缓存失效的问题。网络接口和之前版本不兼容,请及时更新服务端和 cfworker。
 
-* 2019-05-29 nginx 增加静态资源服务,可同时支持代理接口和首页访问
-
-* 2019-05-27 增加 nio.io、sslip.io 后备域名,减少申请失败的几率
-
-* 2019-05-26 安装时自动申请证书(使用 xip.io 域名),安装后即可预览
+* 2019-06-22 [cfworker 无服务器版](cf-worker) 发布,长期使用演示服务的请使用该版本。
 
 [查看更多](changelogs)
 
@@ -19,7 +11,11 @@
 # 安装
 
 ```bash
+<<<<<<< HEAD
 curl https://raw.githubusercontent.com/EtherDream/jsproxy/master/i.sh | bash
+=======
+curl https://raw.githubusercontent.com/EtherDream/jsproxy/0.1.0/i.sh | bash
+>>>>>>> dev
 ```
 
 * 自动安装目前只支持 Linux x64,并且需要 root 权限

+ 9 - 4
api.conf

@@ -15,7 +15,7 @@ location = /error {
   more_set_headers
     'access-control-allow-origin: *'
     'access-control-expose-headers: gateway-err--'
-    'gateway-err--: {"msg": "$arg_msg", "addr": "$upstream_addr"}'
+    'gateway-err--: {"msg": "$arg_msg", "addr": "$upstream_addr", "url": "$arg_url"}'
   ;
   return              204;
 }
@@ -27,15 +27,20 @@ location = /preflight {
   more_set_headers
     'access-control-allow-origin: *'
     'access-control-allow-methods: GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS'
-    'access-control-allow-headers: --raw-info,--level,--url,--referer,--cookie,--origin,--ext,--aceh,--ver,--type,--mode,accept,accept-charset,accept-encoding,accept-language,accept-datetime,authorization,cache-control,content-length,content-type,date,if-match,if-modified-since,if-none-match,if-range,if-unmodified-since,max-forwards,pragma,range,te,upgrade,upgrade-insecure-requests,x-requested-with,chrome-proxy,purpose'
     'access-control-max-age: 1728000'
   ;
   return              204;
 }
 
+# 该接口已作废
+location = /http {
+  access_log          off;
+  more_set_headers    'access-control-allow-origin: *';
+  return              200  "该接口已作废,请更新前端脚本";
+}
 
 # HTTP(S) Proxy
-location = /http {
+location /http/ {
   # see ./allowed-sites.conf
   if ($_origin_id = '') {
     rewrite             ^   /error?msg=ORIGIN_NOT_ALLOWED;
@@ -47,7 +52,7 @@ location = /http {
   proxy_set_header      Connection  $http_connection;
   
 
-  if ($http_access_control_request_headers) {
+  if ($http_access_control_request_methods) {
     rewrite             ^   /preflight;
   }
 

+ 2 - 3
cf-worker/.eslintrc.json

@@ -1,7 +1,8 @@
 {
   "env": {
     "browser": true,
-    "es6": true
+    "es6": true,
+    "serviceworker": true
   },
   "extends": "eslint:recommended",
   "globals": {
@@ -13,10 +14,8 @@
     "sourceType": "module"
   },
   "rules": {
-    "no-console": "warn",
     "no-empty": "warn",
     "no-unused-vars": "warn",
-    "no-debugger": "warn",
     "no-constant-condition": "warn"
   }
 }

+ 1 - 18
cf-worker/README.md

@@ -23,27 +23,10 @@
 
 如果不够用,可注册多个 Worker,在 `conf.js` 中配置多线路负载均衡。或者升级到 $5 的高级版本,每月可用 1000 万次请求(超出部分 $0.5/百万次请求)。
 
-如果远不够用,建议和服务器组合使用。因为 cfworker 是按请求次数计费的,所以小文件更适合通过服务器代理,大文件走 cfworker 才合算。可参考下面的 `加速功能`。
-
 
 # 修改配置
 
-默认情况下,静态资源从 `https://zjcqoo.github.io` 反向代理,可通过代码中 `ASSET_URL` 配置,从而可使用自定义的 `conf.js` 配置。
-
-
-# 加速功能
-
-如果你已有服务器,也可通过 CloudFlare Worker 分担大文件的代理。
-
-前端修改:`conf.js` 的 `cfworker` 节点 `lines` 配置。
-
-后端修改:`lua/http-enc-res-hdr.lua` 的 [114-116 行](https://github.com/EtherDream/jsproxy/blob/master/lua/http-enc-res-hdr.lua#L114-L116) 注释打开,重启服务生效。
-
-可在 [84行](https://github.com/EtherDream/jsproxy/blob/master/lua/http-enc-res-hdr.lua#L84) 处修改大于多少字节的静态资源走加速。
-
-该功能目前还在实验中,有问题或者更好的思路可交流。
-
-(推荐下行流量免费且不限速的服务器,可节省大量费用)
+默认情况下,静态资源从 `https://etherdream.github.io/jsproxy` 反向代理,可通过代码中 `ASSET_URL` 配置,从而可使用自定义的 `conf.js` 配置。
 
 
 # 存在问题

+ 71 - 59
cf-worker/index.js

@@ -3,46 +3,60 @@
 /**
  * static files (404.html, sw.js, conf.js)
  */
-const ASSET_URL = 'https://zjcqoo.github.io'
+const ASSET_URL = 'https://etherdream.github.io/jsproxy'
 
-const JS_VER = 8
+const JS_VER = 10
 const MAX_RETRY = 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-allow-headers': '--raw-info,--level,--url,--referer,--cookie,--origin,--ext,--aceh,--ver,--type,--mode,accept,accept-charset,accept-encoding,accept-language,accept-datetime,authorization,cache-control,content-length,content-type,date,if-match,if-modified-since,if-none-match,if-range,if-unmodified-since,max-forwards,pragma,range,te,upgrade,upgrade-insecure-requests,x-requested-with,chrome-proxy,purpose',
     'access-control-max-age': '1728000',
   }),
 }
 
 /**
- * @param {string} message
+ * @param {any} body
  * @param {number} status
- * @param {any} headers
+ * @param {Object<string, string>} headers
  */
-function makeRes(message, status = 200, headers = {}) {
-  headers['cache-control'] = 'no-cache'
-  headers['vary'] = '--url'
+function makeRes(body, status = 200, headers = {}) {
+  headers['--ver'] = JS_VER
   headers['access-control-allow-origin'] = '*'
-  return new Response(message, {status, headers})
+  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:' + err, 502))
+    .catch(err => makeRes('cfworker error:\n' + err.stack, 502))
   e.respondWith(ret)
 })
 
 
-function fetchHandler(e) {
+/**
+ * @param {FetchEvent} e 
+ */
+async function fetchHandler(e) {
   const req = e.request
   const urlStr = req.url
   const urlObj = new URL(urlStr)
+  const path = urlObj.href.substr(urlObj.origin.length)
 
   if (urlObj.protocol === 'http:') {
     urlObj.protocol = 'https:'
@@ -52,25 +66,29 @@ function fetchHandler(e) {
     })
   }
 
-  switch (urlObj.pathname) {
+  if (path.startsWith('/http/')) {
+    return httpHandler(req, path.substr(6))
+  }
+
+  switch (path) {
   case '/http':
-    return httpHandler(req)
+    return makeRes('请更新 cfworker 到最新版本!')
   case '/ws':
     return makeRes('not support', 400)
   case '/works':
     return makeRes('it works')
   default:
     // static files
-    return fetch(ASSET_URL + urlObj.pathname)
+    return fetch(ASSET_URL + path)
   }
 }
 
 
-
 /**
  * @param {Request} req
+ * @param {string} pathname
  */
-async function httpHandler(req) {
+function httpHandler(req, pathname) {
   const reqHdrRaw = req.headers
   if (reqHdrRaw.has('x-jsproxy')) {
     return Response.error()
@@ -83,8 +101,6 @@ async function httpHandler(req) {
     return new Response(null, PREFLIGHT_INIT)
   }
 
-  let urlObj = null
-  let extHdrs = null
   let acehOld = false
   let rawSvr = ''
   let rawLen = ''
@@ -93,52 +109,51 @@ async function httpHandler(req) {
   const reqHdrNew = new Headers(reqHdrRaw)
   reqHdrNew.set('x-jsproxy', '1')
 
-  for (const [k, v] of reqHdrRaw.entries()) {
-    if (!k.startsWith('--')) {
-      continue
-    }
-    reqHdrNew.delete(k)
-
-    const k2 = k.substr(2)
-    switch (k2) {
-    case 'url':
-      urlObj = new URL(v)
-      break
-    case 'aceh':
-      acehOld = true
-      break
-    case 'raw-info':
-      [rawSvr, rawLen, rawEtag] = v.split('|')
-      break
-    case 'level':
-    case 'mode':
-    case 'type':
-      break
-    case 'ext':
-      extHdrs = JSON.parse(v)
-      break
-    default:
+  // 此处逻辑和 http-dec-req-hdr.lua 大致相同
+  // https://github.com/EtherDream/jsproxy/blob/master/lua/http-dec-req-hdr.lua
+  const refer = reqHdrNew.get('referer')
+  const query = refer.substr(refer.indexOf('?') + 1)
+  if (!query) {
+    return makeRes('missing params', 403)
+  }
+  const param = new URLSearchParams(query)
+
+  for (const [k, v] of Object.entries(param)) {
+    if (k.substr(0, 2) === '--') {
+      // 系统信息
+      switch (k.substr(2)) {
+      case 'aceh':
+        acehOld = true
+        break
+      case 'raw-info':
+        [rawSvr, rawLen, rawEtag] = v.split('|')
+        break
+      }
+    } else {
+      // 还原 HTTP 请求头
       if (v) {
-        reqHdrNew.set(k2, v)
+        reqHdrNew.set(k, v)
       } else {
-        reqHdrNew.delete(k2)
+        reqHdrNew.delete(k)
       }
-      break
     }
   }
-  if (extHdrs) {
-    for (const [k, v] of Object.entries(extHdrs)) {
-      reqHdrNew.set(k, v)
-    }
+  if (!param.has('referer')) {
+    reqHdrNew.delete('referer')
   }
+
+  // cfworker 会把路径中的 `//` 合并成 `/`
+  const urlStr = pathname.replace(/^(https?):\/+/, '$1://')
+  const urlObj = newUrl(urlStr)
   if (!urlObj) {
-    return makeRes('missing url param', 403)
+    return makeRes('invalid proxy url: ' + urlStr, 403)
   }
 
   /** @type {RequestInit} */
   const reqInit = {
     method: req.method,
     headers: reqHdrNew,
+    redirect: 'manual',
   }
   if (req.method === 'POST') {
     reqInit.body = req.body
@@ -159,7 +174,6 @@ async function proxy(urlObj, reqInit, acehOld, rawLen, retryTimes) {
   const resHdrNew = new Headers(resHdrOld)
 
   let expose = '*'
-  let vary = '--url'
   
   for (const [k, v] of resHdrOld.entries()) {
     if (k === 'access-control-allow-origin' ||
@@ -174,9 +188,6 @@ async function proxy(urlObj, reqInit, acehOld, rawLen, retryTimes) {
       }
       resHdrNew.delete(k)
     }
-    else if (k === 'vary') {
-      vary = vary + ',' + v
-    }
     else if (acehOld &&
       k !== 'cache-control' &&
       k !== 'content-language' &&
@@ -206,8 +217,9 @@ async function proxy(urlObj, reqInit, acehOld, rawLen, retryTimes) {
           return proxy(urlObj, reqInit, acehOld, rawLen, retryTimes + 1)
         }
       }
-      return makeRes('error', 400, {
-        '--error': 'bad len:' + newLen
+      return makeRes(res.body, 400, {
+        '--error': `bad len: ${newLen}, except: ${rawLen}`,
+        'access-control-expose-headers': '--error',
       })
     }
 
@@ -220,12 +232,12 @@ async function proxy(urlObj, reqInit, acehOld, rawLen, retryTimes) {
 
   resHdrNew.set('access-control-expose-headers', expose)
   resHdrNew.set('access-control-allow-origin', '*')
-  resHdrNew.set('vary', vary)
   resHdrNew.set('--s', status)
   resHdrNew.set('--ver', JS_VER)
 
   resHdrNew.delete('content-security-policy')
   resHdrNew.delete('content-security-policy-report-only')
+  resHdrNew.delete('clear-site-data')
 
   if (status === 301 ||
       status === 302 ||

+ 6 - 25
changelogs/README.md

@@ -1,32 +1,13 @@
 # 完整更新日志
 
-## v0.0.1
+* 2019-06-11 前端脚本调整,首页可离线访问(如果长时间加载中,尝试多刷新几次或者隐身模式访问)
 
-全选的 URL 模型,取代 [之前版本](https://github.com/EtherDream/jsproxy/tree/first-ver)。[查看详细](v0.0.1.md)
+* 2019-05-30 更新 cfworker,对 ytb 视频进行了优化(推荐选 1080p+,不会增加服务器压力)
 
+* 2019-05-29 nginx 增加静态资源服务,可同时支持代理接口和首页访问
 
-## 开发中...
+* 2019-05-27 增加 nio.io、sslip.io 后备域名,减少申请失败的几率
 
-最新临时的测试,没分版本号
+* 2019-05-26 安装时自动申请证书(使用 xip.io 域名),安装后即可预览
 
-### 节点切换功能(服务端)
-
-服务端对于体积较大的资源不返回内容,只返回长度、修改时间、首块 hash 等信息。浏览器从廉价带宽获取完整内容,如果返回信息不一致,再从原服务器获取。
-
-目前体积阈值为 400KB。廉价带宽使用 Cloudflare Worker,源码参考 `cf-worker` 目录。虽然 Cloudflare Worker 延时较高,但带宽和流量无限制,且费用较低(1000 万次请求 5 美元),非常适合大文件代理。
-
-
-### 静态资源加速(前端)
-
-通过自动化工具分析 TOP 10 网站的资源,将缓存时间久的静态资源,预先下载到 CDN 上(包括一个 URL Hash 列表)。前端遇到这些资源时直接从 CDN 获取,大幅减少下载时间,以及代理服务器的带宽占用和流量消耗。
-
-目前 CDN 暂使用 jsdelivr。未来尝试将资源上传到贴吧等支持 CORS 的免费图床~
-
-另外演示首页的静态资源也使用 CDN。
-
-
-### 安装工具更新
-
-增加一键安装脚本,Linux x64 系统可自动安装。
-
-(没仔细测。有问题 issure 反馈。以后有时间补充测试案例。。。)
+* 全新的 URL 模型,取代 [之前版本](https://github.com/EtherDream/jsproxy/tree/first-ver)。[查看详细](v0.0.1.md)

+ 70 - 0
changelogs/v0.1.0.md

@@ -0,0 +1,70 @@
+# v0.1.0
+
+## 更新内容
+
+* 后端代理以及 cfworker 接口调整,修复缓存失效的问题
+
+* 前端增加缓存记录,提高浏览器缓存命中率
+
+* 前端增加 Cookie 持久化
+
+* 前端增加 CORS 站点直连功能
+
+* 配置调整,支持线路权重
+
+* 更详细的服务器错误信息显示
+
+* 增加更多的 Storage API Hook
+
+
+## 代理接口调整
+
+之前代理接口使用固定的路径 `/http`,目标 URL 设置在请求头 `--url` 字段,同时返回头配置了 `vary: --url` 字段,希望能根据不同的 `--url` 请求返回不同的缓存内容。但事实上该方案并未生效,和预想的不同,浪费了不少流量。(对 `vary` 了解不够透彻~)
+
+为修复这个问题,目前将代理接口改成 `/http/目标 URL`,去掉了 `vary` 字段。同时将绝大部分的请求字段打包到 `Referer` 字段里,使请求头保持简单,不产生 CORS preflight。
+
+> 如果不打包,则会频繁出现 preflight,即使配置了 `Access-Control-Max-Age` 也没用,因为 max-age 只对特定 URL 记忆,而现在的 URL 几乎每次都不同,所以必须保持请求头足够简单。至于为什么选择 `Referer` 字段,因为只有这个字段可以灵活存储数据,[其他几个字段都有些限制](https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte),容易出现 preflight。
+
+当然这个功能目前仍在研究中,未来也许会有更好的方案。
+
+
+## 节点缓存
+
+由于切换 节点/线路 会使得最终的 URL 发生变化,从而导致无法利用已有的缓存。
+
+目前增加了静态资源记忆功能,记住当前使用的域名。下次加载时直接使用上次的域名,从而命中浏览器缓存。
+
+存储查看:`indexedDB` -> `.sys` -> `url-cache`
+
+
+## Cookie 持久化
+
+目前 Cookie 信息定期同步到本地存储,浏览器重启后可保持之前的会话。
+
+存储查看:`indexedDB` -> `.sys` -> `cookie`
+
+
+## CORS 站点直连
+
+不少网站(通常是 CDN)在返回头中配置了 `access-control-allow-origin: *`,并且不校验 `origin` 和 `referer`(或者允许为空)。
+
+对于这样的站点,前端可直接连接而无需通过代理,从而能加快访问速度,并且节省代理服务器流量。
+
+目前收集了部分站点,只在纯前端实现。未来将尝试和服务端配合,覆盖所有这样的场合。
+
+
+## 节点权重支持
+
+不同于之前均匀分配负载,目前可配置每个线路的权重,从而能对部分线路增加或降低负载。
+
+例如演示案例中的 cfworker 节点,使用 1 个收费版 + 多个免费版的方案。由于免费版有访问频率限制,因此使用更低的权重以减少负载。(命中比例 = 当前值 / 总值)
+
+
+## 详细错误信息
+
+目前可显示代理服务器的 DNS 解析错误、HTTP 连接错误、白名单错误等,取代之前过于简陋的报错信息。
+
+
+## Storage API Hook
+
+增加 `indexedDB` 和 `Cache Storage` 的 key 枚举、删除的 API Hook。

+ 3 - 3
i.sh

@@ -2,7 +2,7 @@
 
 { # this ensures the entire script is downloaded #
 
-JSPROXY_VER=master
+JSPROXY_VER=0.1.0
 OPENRESTY_VER=1.15.8.1
 
 SRC_URL=https://raw.githubusercontent.com/EtherDream/jsproxy/$JSPROXY_VER
@@ -57,7 +57,7 @@ gen_cert() {
     ip=$(curl -s $i)
 
     if [[ ! $ip ]]; then
-      warn "获取失败"
+      warn "获取失败"
       continue
     fi
 
@@ -179,7 +179,7 @@ install() {
   fi
 
   if [ -d server ]; then
-    backup="$INSTALL_DIR/bak/$(date +%Y_%m_%d_%H_%M_%S)"
+    local backup="$INSTALL_DIR/bak/$(date +%Y_%m_%d_%H_%M_%S)"
     warn "当前 server 目录备份到 $backup"
     mkdir -p $backup
     mv server $backup

+ 6 - 8
log-svc/backup.sh

@@ -3,12 +3,13 @@
 
 SVC_DIR=/home/jsproxy/server
 LOG_DIR=$SVC_DIR/nginx/logs
+DST_DIR=$SVC_DIR/log-svc/backup
 
 LOG_FILE=$LOG_DIR/proxy.log
-LOG_SIZE=$(( 32 * 1024 * 1024 ))
+LOG_SIZE=$(( 256 * 1024 * 1024 ))
 
 ERR_FILE=$LOG_DIR/error.log
-ERR_SIZE=$(( 1 * 1024 * 1024 * 1024 ))
+ERR_SIZE=$(( 256 * 1024 * 1024 ))
 
 
 # error.log 达到 ERR_SIZE,开始备份(目前只清理)
@@ -24,12 +25,11 @@ if (( $logsize < $LOG_SIZE )); then
 fi
 
 logtime=$(date "+%Y-%m-%d-%H-%M-%S")
-logfile=$SVC_DIR/log-svc/backup/$logtime.log
 
 #
 # 先移走日志文件,然后创建新的日志文件,通知 nginx 重新打开
 #
-mv $LOG_FILE $logfile
+mv $LOG_FILE $DST_DIR/$logtime.log
 touch $LOG_FILE
 $SVC_DIR/run.sh reopen
 sleep 1
@@ -37,11 +37,9 @@ sleep 1
 #
 # 日志压缩
 # 根据实际情况调整策略,在不影响系统的前提下,充分利用剩余 CPU
-# 可尝试其他工具(例如 7z),在开销和效果之间找一个平衡点
 #
-echo "compress $logtime ($logsize bytes)"
+echo "compress ..."
 
-nice -n 19 \
-  gzip $logfile
+nice -n 19 xz $DST_DIR/*.log
 
 echo "done"

+ 29 - 32
lua/http-dec-req-hdr.lua

@@ -1,44 +1,41 @@
--- 功能:还原 HTTP 请求头
--- 阶段:access_by_lua
+-- 还原 HTTP 请求头
+local hasRawRefer = false
 
-local hdrs, err = ngx.req.get_headers()
-local extHdrs
+local hdrs = ngx.req.get_headers()
+local refer = hdrs['referer']
+local query = refer:sub(refer:find('?', 10, true) + 1)
+local param = ngx.decode_args(query)
 
-for k, v in pairs(hdrs) do
-  if k:sub(1, 2) ~= '--' then
-    goto continue
-  end
 
-  ngx.req.clear_header(k)
-  k = k:sub(3)
+for k, v in pairs(param) do
+  if k:sub(1, 2) == '--' then
+    k = k:sub(3)
 
-  if k == 'url' then
-    ngx.var._url = v
-  elseif k == 'ver' then
-    ngx.var._ver = v
-  elseif k == 'type' then
-    ngx.var._type = v
-  elseif k == 'mode' then
-    ngx.var._mode = v
-  elseif k == 'aceh' then
-    ngx.ctx._acehOld = true
-  elseif k == 'level' then
-    ngx.var._level = v
-    ngx.ctx._level = tonumber(v)
-  elseif k == 'ext' then
-    extHdrs = require('cjson').decode(v)
+    if k == 'ver' then
+      ngx.var._ver = v
+    elseif k == 'type' then
+      ngx.var._type = v
+    elseif k == 'mode' then
+      ngx.var._mode = v
+    elseif k == 'aceh' then
+      ngx.ctx._acehOld = true
+    elseif k == 'level' then
+      ngx.var._level = v
+      ngx.ctx._level = tonumber(v)
+    end
   else
+    ngx.req.set_header(k, v)
+
     if k == 'referer' then
+      hasRawRefer = true
       ngx.var._ref = v
     end
-    ngx.req.set_header(k, v)
   end
+end
 
-  ::continue::
+if not hasRawRefer then
+  ngx.req.clear_header('referer')
 end
 
-if extHdrs then
-  for k, v in pairs(extHdrs) do
-    ngx.req.set_header(k, v)
-  end
-end
+-- 删除 URL 的 '/http/' 前缀
+ngx.var._url = ngx.var.request_uri:sub(7)

+ 23 - 24
lua/http-enc-res-hdr.lua

@@ -9,9 +9,6 @@ local expose = '*'
 -- 该值为 true 表示浏览器不支持 aceh: *,需返回详细的头部列表
 local detail = ngx.ctx._acehOld
 
--- 由于接口路径固定,为避免被缓存,以请求头的 --url 值区分缓存
-local vary = '--url'
-
 
 local function addHdr(k, v)
   ngx.header[k] = v
@@ -23,17 +20,26 @@ end
 
 local function flushHdr()
   if detail then
-    expose = expose .. ',--s'
+    if status ~= 200 then
+      expose = expose .. ',--s'
+    end
     -- 该字段不在 aceh 中,如果浏览器能读取到,说明支持 * 通配
     ngx.header['--t'] = '1'
   end
 
-  local status = ngx.status
-
   ngx.header['access-control-expose-headers'] = expose
   ngx.header['access-control-allow-origin'] = '*'
-  ngx.header['vary'] = vary
-  ngx.header['--s'] = status
+
+  local status = ngx.status
+
+  -- 前端优先使用该字段作为状态码
+  if status ~= 200 then
+    ngx.header['--s'] = status
+  end
+
+  -- 保留原始状态码,便于控制台调试
+  -- 例如 404 显示红色,如果统一设置成 200 则没有颜色区分
+  -- 需要转义 30X 重定向,否则不符合 cors 标准
   if
     status == 301 or
     status == 302 or
@@ -47,15 +53,6 @@ local function flushHdr()
 end
 
 
-local function addVary(v)
-  if type(v) == 'table' then
-    vary = vary .. ',' .. table.concat(v, ',')
-  else
-    vary = vary .. ',' .. v
-  end
-end
-
-
 local function nodeSwitched()
   local status = ngx.status
   if status ~= 200 and status ~= 206 then
@@ -111,9 +108,9 @@ local function nodeSwitched()
 end
 
 -- 节点切换功能,目前还在测试中(demo 中已开启)
--- if nodeSwitched() then
---   return
--- end
+if nodeSwitched() then
+  return
+end
 
 
 local h, err = ngx.resp.get_headers()
@@ -136,9 +133,6 @@ for k, v in pairs(h) do
     end
     ngx.header[k] = nil
 
-  elseif k == 'vary' then
-    addVary(v)
-
   elseif detail and
     -- 非简单头无法被 fetch 读取,需添加到 aceh 列表 --
     -- https://developer.mozilla.org/en-US/docs/Glossary/Simple_response_header
@@ -153,4 +147,9 @@ for k, v in pairs(h) do
   end
 end
 
-flushHdr()
+-- 不缓存非 GET 请求
+if ngx.req.get_method() ~= 'GET' then
+  ngx.header['cache-control'] = 'no-cache'
+end
+
+flushHdr()

+ 3 - 1
nginx.conf

@@ -45,7 +45,9 @@ http {
   proxy_buffer_size       16k;
   proxy_buffers           4 32k;
   proxy_busy_buffers_size 64k;
-  proxy_send_timeout      10s;
+  proxy_send_timeout      30s;
+  proxy_read_timeout      30s;
+  proxy_connect_timeout   10s;
 
   lua_load_resty_core     off;