背景
测试环境已有静态资源服务器,但是需要设置hosts172.19.80.14 s.thsi.cn进行访问,这必须连接抓包才能完成,一些支付相关的业务由于第三方支付服务的限制,在设置抓包的情况下将无法走完流程。为了满足这样的场景,需要提供一种公网访问测试环境静态资源的方法。
测试环境nginx设置了CD静态资源的转发,链路为https://testm.10jqka.com.cn/cd/→https://s.thsi.cn/cd/,这类资源只需替换域名即可。
但是采用这样的方式,必须让前端在编译测试环境页面时就将CD静态资源目录设置为 testm 域名,构建产物和线上将会不一致,十分不优雅。
所以,有什么样的方法可以即可以维持测试环境构建产物和线上一致,又能使得CD静态资源请求转到测试环境呢?
同样地,前端注入 eruda 工具,是否也能有不改变构建产物而实现的优雅方式呢?
解决方案
在 testm 的 nginx 层面处理,前端只管按照正式环境的方式进行构建,nginx层将页面所有的//s.thsi.cn/cd/替换为//testm.10jqka.com.cn/cd/,然后在 html 的 header 结束之前注入 eruda 的 script 标签即可;
使用方法
直接在 nginx.conf 中新增如下语句进行配置引用即可
include thsi_inject.conf;
按照上述配置之后,前端页面将看到 eruda 工具,并且CD资源请求全部替换为testm域名,html请求的响应头也会有Thsi-Inject-Status: true ;
如果某个前端页面不希望有 eruda 工具注入,则在转发规则中新增如下语句即可关闭
set $eruda "false";
副作用
使用注入之后,nginx返回给浏览器的相应中响应头中将不会有Content-Length,而是走Transfer-Encoding: chunked的模式,目前还没有发现由此导致的问题;
HTTP请求经由 lua 做了一层过滤,性能会有所影响;
原理
使用 openresty 的header_filter和body_filter功能即可实现,源码如下
header_filter
-- 文件位置:/data/waf/lua/inject/thsi_header.lua
local pos = string.find(ngx.var.request_uri, ".html")
if pos ~= nil then
-- 清空响应头中的内容长度,因为body的内容替换会导致实际发送内容长度变化
-- 标称长度和实际长度若不一致会导致前端下载html页面资源时间过长
ngx.header.content_length = nil
-- 响应头中添加注入的标记
ngx.header["Thsi-Inject-Status"] = "true"
end
body_filter
-- 文件位置:/data/waf/lua/inject/thsi_body.lua
-- body_filter_by_lua, body filter模块,ngx.arg[1]代表输入的chunk,ngx.arg[2]代表当前chunk是否为last
local pos = string.find(ngx.var.request_uri, ".html")
if pos ~= nil then
local chunk, eof = ngx.arg[1], ngx.arg[2]
local buffered = ngx.ctx.buffered
if not buffered then
buffered = {} -- XXX we can use table.new here
ngx.ctx.buffered = buffered
end
if chunk ~= "" then
buffered[#buffered + 1] = chunk
ngx.arg[1] = nil
end
if eof then
local whole = table.concat(buffered)
ngx.ctx.buffered = nil
-- try to unzip
-- local status, debody = pcall(com.decode, whole)
-- if status then whole = debody end
-- try to add or replace response body
-- local js_code = ...
-- whole = whole .. js_code
-- cd路径的静态资源请求域名替换
whole = string.gsub(whole, "//s.thsi.cn/cd/", "//testm.10jqka.com.cn/cd/")
-- 注入前端调试工具
if ngx.var.eruda ~= 'false' then
whole = string.gsub(whole, "</head>", '<script type="text/javascript" src="//s.thsi.cn/js/m/eruda.js"></script></head>')
end
ngx.arg[1] = whole
end
end
nginx conf
# 文件位置:/usr/local/nginx/conf/thsi_inject.conf
header_filter_by_lua_file /data/waf/lua/inject/thsi_header.lua;
body_filter_by_lua_file /data/waf/lua/inject/thsi_body.lua;
参考资料
Lua + OpenResty修改response body (https://jkzhao.github.io/2018/05/03/Lua-OpenResty%E4%BF%AE%E6%94%B9response-body/)