From 3391d1df8b0a7cb254ad492ff08f426d6708259b Mon Sep 17 00:00:00 2001 From: zjcqoo Date: Tue, 23 Jul 2019 20:37:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E9=AA=8C=E6=97=A0=E6=B3=95=E6=BB=A1?= =?UTF-8?q?=E8=B6=B3=20https://fetch.spec.whatwg.org/#cors-unsafe-request-?= =?UTF-8?q?header-byte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.conf | 9 +++-- cert/cert.conf | 3 ++ cf-worker/README.md | 17 ---------- cf-worker/index.js | 71 +++++++++++++++++----------------------- lua/http-dec-req-hdr.lua | 55 ++++++++++++++++--------------- lua/http-enc-res-hdr.lua | 24 ++++---------- 6 files changed, 74 insertions(+), 105 deletions(-) diff --git a/api.conf b/api.conf index 9e740fe..a4c40c6 100644 --- a/api.conf +++ b/api.conf @@ -27,15 +27,18 @@ 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 { + rewrite ^ /error?msg=API_OBSOLETED; +} # HTTP(S) Proxy -location = /http { +location /http/ { # see ./allowed-sites.conf if ($_origin_id = '') { rewrite ^ /error?msg=ORIGIN_NOT_ALLOWED; @@ -47,7 +50,7 @@ location = /http { proxy_set_header Connection $http_connection; - if ($http_access_control_request_headers) { + if ($http_access_control_request_methods) { rewrite ^ /preflight; } diff --git a/cert/cert.conf b/cert/cert.conf index e69de29..04c085f 100644 --- a/cert/cert.conf +++ b/cert/cert.conf @@ -0,0 +1,3 @@ +listen 8443 ssl http2; +ssl_certificate cert/etherdream.com/ecc.cer; +ssl_certificate_key cert/etherdream.com/ecc.key; diff --git a/cf-worker/README.md b/cf-worker/README.md index 9cf985f..46ff08c 100644 --- a/cf-worker/README.md +++ b/cf-worker/README.md @@ -23,29 +23,12 @@ 如果不够用,可注册多个 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) 处修改大于多少字节的静态资源走加速。 - -该功能目前还在实验中,有问题或者更好的思路可交流。 - -(推荐下行流量免费且不限速的服务器,可节省大量费用) - - # 存在问题 * WebSocket 代理尚未实现 diff --git a/cf-worker/index.js b/cf-worker/index.js index b5e97f0..b044b92 100644 --- a/cf-worker/index.js +++ b/cf-worker/index.js @@ -25,8 +25,6 @@ const PREFLIGHT_INIT = { * @param {Object} headers */ function makeRes(body, status = 200, headers = {}) { - headers['cache-control'] = 'no-cache' - headers['vary'] = '--url' headers['--ver'] = JS_VER headers['access-control-allow-origin'] = '*' return new Response(body, {status, headers}) @@ -47,6 +45,7 @@ async function fetchHandler(e) { const req = e.request const urlStr = req.url const urlObj = new URL(urlStr) + const {pathname} = urlObj if (urlObj.protocol === 'http:') { urlObj.protocol = 'https:' @@ -56,16 +55,18 @@ async function fetchHandler(e) { }) } - switch (urlObj.pathname) { - case '/http': + if (pathname.startsWith('/http')) { return httpHandler(req) + } + + switch (pathname) { 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 + pathname) } } @@ -86,8 +87,13 @@ function httpHandler(req) { return new Response(null, PREFLIGHT_INIT) } - let urlObj = null - let extHdrs = null + const urlStr = req.url.substr(6) + try { + var urlObj = new URL(urlStr) + } catch (err) { + return makeRes('invalid url: ' + urlStr, 403) + } + let acehOld = false let rawSvr = '' let rawLen = '' @@ -96,46 +102,34 @@ 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) + // 此处逻辑和 http-dec-req-hdr.lua 大致相同 + // https://github.com/EtherDream/jsproxy/blob/master/lua/http-dec-req-hdr.lua + const {sys, ext} = JSON.parse(reqHdrNew.get('accept')) - const k2 = k.substr(2) - switch (k2) { - case 'url': - urlObj = new URL(v) - break + // 系统信息 + for (const [k, v] of Object.entries(sys)) { + switch (k) { 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: - if (v) { - reqHdrNew.set(k2, v) - } else { - reqHdrNew.delete(k2) - } - break } } - if (extHdrs) { - for (const [k, v] of Object.entries(extHdrs)) { - reqHdrNew.set(k, v) + + // 原始 HTTP 字段 + let hasRawAccept = false + + for (const [k, v] of Object.entries(ext)) { + if (k === 'accept') { + hasRawAccept = true } + reqHdrNew.set(k, v) } - if (!urlObj) { - return makeRes('missing url param', 403) + + if (!hasRawAccept) { + reqHdrNew.delete('accept') } /** @type {RequestInit} */ @@ -163,7 +157,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' || @@ -178,9 +171,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' && @@ -225,7 +215,6 @@ 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) diff --git a/lua/http-dec-req-hdr.lua b/lua/http-dec-req-hdr.lua index 46c1f4e..0e2d7ff 100644 --- a/lua/http-dec-req-hdr.lua +++ b/lua/http-dec-req-hdr.lua @@ -1,20 +1,18 @@ --- 功能:还原 HTTP 请求头 --- 阶段:access_by_lua +-- 还原 HTTP 请求头 +-- 前端只保留简单字段(防止出现 preflight), +-- 其余字段及系统信息,存储在 Accept 字段里(JSON 格式)。 +local cjson = require('cjson') local hdrs, err = ngx.req.get_headers() -local extHdrs +ngx.log(ngx.ALERT, 'accept:' .. hdrs['accept']) +local info = ngx.unescape_uri(hdrs['accept']) +local json = cjson.decode(info) -for k, v in pairs(hdrs) do - if k:sub(1, 2) ~= '--' then - goto continue - end +-- 系统信息 +local sys = json['sys'] - ngx.req.clear_header(k) - k = k:sub(3) - - if k == 'url' then - ngx.var._url = v - elseif k == 'ver' then +for k, v in pairs(sys) do + if k == 'ver' then ngx.var._ver = v elseif k == 'type' then ngx.var._type = v @@ -25,20 +23,25 @@ for k, v in pairs(hdrs) do elseif k == 'level' then ngx.var._level = v ngx.ctx._level = tonumber(v) - elseif k == 'ext' then - extHdrs = require('cjson').decode(v) - else - if k == 'referer' then - ngx.var._ref = v - end - ngx.req.set_header(k, v) end - - ::continue:: end -if extHdrs then - for k, v in pairs(extHdrs) do - ngx.req.set_header(k, v) +-- 原始 HTTP 字段 +local ext = json['ext'] +local hasRawAccept = false + +for k, v in pairs(ext) do + if k == 'accept' then + hasRawAccept = true + elseif k == 'referer' then + ngx.var._ref = v end -end \ No newline at end of file + ngx.req.set_header(k, v) +end + +if not hasRawAccept then + ngx.req.clear_header('accept') +end + +-- 删除 URL 的 '/http/' 前缀 +ngx.var._url = ngx.var.request_uri:sub(7) \ No newline at end of file diff --git a/lua/http-enc-res-hdr.lua b/lua/http-enc-res-hdr.lua index f55c0c7..86927b4 100644 --- a/lua/http-enc-res-hdr.lua +++ b/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 @@ -32,7 +29,6 @@ local function flushHdr() ngx.header['access-control-expose-headers'] = expose ngx.header['access-control-allow-origin'] = '*' - ngx.header['vary'] = vary local status = ngx.status @@ -57,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 @@ -113,7 +100,6 @@ local function nodeSwitched() addHdr('--switched', '1') ngx.header['cache-control'] = 'no-cache' - ngx.header['vary'] = '--url' ngx.var._switched = resLenStr ngx.ctx._switched = true @@ -147,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 @@ -164,4 +147,9 @@ for k, v in pairs(h) do end end -flushHdr() \ No newline at end of file +-- 不缓存非 GET 请求 +if ngx.req.get_method() ~= 'GET' then + ngx.header['cache-control'] = 'no-cache' +end + +flushHdr()