http-enc-res-hdr.lua 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. -- 功能:编码 HTTP 返回头
  2. -- 阶段:header_filter_by_lua
  3. -- 备注:
  4. -- aceh = HTTP 返回头的 access-control-expose-headers 字段
  5. -- 无论浏览器是否支持,aceh 始终包含 *
  6. local expose = '*'
  7. -- 该值为 true 表示浏览器不支持 aceh: *,需返回详细的头部列表
  8. local detail = ngx.ctx._acehOld
  9. local function addHdr(k, v)
  10. ngx.header[k] = v
  11. if detail then
  12. expose = expose .. ',' .. k
  13. end
  14. end
  15. local function flushHdr()
  16. if detail then
  17. if status ~= 200 then
  18. expose = expose .. ',--s'
  19. end
  20. -- 该字段不在 aceh 中,如果浏览器能读取到,说明支持 * 通配
  21. ngx.header['--t'] = '1'
  22. end
  23. ngx.header['access-control-expose-headers'] = expose
  24. ngx.header['access-control-allow-origin'] = '*'
  25. local status = ngx.status
  26. -- 前端优先使用该字段作为状态码
  27. if status ~= 200 then
  28. ngx.header['--s'] = status
  29. end
  30. -- 保留原始状态码,便于控制台调试
  31. -- 例如 404 显示红色,如果统一设置成 200 则没有颜色区分
  32. -- 需要转义 30X 重定向,否则不符合 cors 标准
  33. if
  34. status == 301 or
  35. status == 302 or
  36. status == 303 or
  37. status == 307 or
  38. status == 308
  39. then
  40. status = status + 10
  41. end
  42. ngx.status = status
  43. end
  44. local function nodeSwitched()
  45. local status = ngx.status
  46. if status ~= 200 and status ~= 206 then
  47. return false
  48. end
  49. local level = ngx.ctx._level
  50. if level == nil or level == 0 then
  51. return false
  52. end
  53. if ngx.req.get_method() ~= 'GET' then
  54. return false
  55. end
  56. if ngx.header['set-cookie'] ~= nil then
  57. return false
  58. end
  59. local resLenStr = ngx.header['content-length']
  60. if resLenStr == nil then
  61. return false
  62. end
  63. -- 小于 400KB 的资源不走加速
  64. local resLenNum = tonumber(resLenStr)
  65. if resLenNum == nil or resLenNum < 1000 * 400 then
  66. return false
  67. end
  68. local addr = ngx.var.upstream_addr or ''
  69. local etag = ngx.header['etag'] or ''
  70. local last = ngx.header['last-modified'] or ''
  71. local info = addr .. '|' .. resLenStr .. '|' .. etag .. '|' .. last
  72. -- clear all res headers
  73. local h, err = ngx.resp.get_headers()
  74. for k, v in pairs(h) do
  75. ngx.header[k] = nil
  76. end
  77. addHdr('--raw-info', info)
  78. addHdr('--switched', '1')
  79. ngx.header['cache-control'] = 'no-cache'
  80. ngx.var._switched = resLenStr
  81. ngx.ctx._switched = true
  82. flushHdr()
  83. return true
  84. end
  85. -- 节点切换功能,目前还在测试中(demo 中已开启)
  86. -- if nodeSwitched() then
  87. -- return
  88. -- end
  89. local h, err = ngx.resp.get_headers()
  90. for k, v in pairs(h) do
  91. if
  92. -- 这些头有特殊意义,需要转义 --
  93. k == 'access-control-allow-origin' or
  94. k == 'access-control-expose-headers' or
  95. k == 'location' or
  96. k == 'set-cookie'
  97. then
  98. if type(v) == 'table' then
  99. -- 重复的字段,例如 Set-Cookie
  100. -- 转换成 1-Set-Cookie, 2-Set-Cookie, ...
  101. for i = 1, #v do
  102. addHdr(i .. '-' .. k, v[i])
  103. end
  104. else
  105. addHdr('--' .. k, v)
  106. end
  107. ngx.header[k] = nil
  108. elseif detail and
  109. -- 非简单头无法被 fetch 读取,需添加到 aceh 列表 --
  110. -- https://developer.mozilla.org/en-US/docs/Glossary/Simple_response_header
  111. k ~= 'cache-control' and
  112. k ~= 'content-language' and
  113. k ~= 'content-type' and
  114. k ~= 'expires' and
  115. k ~= 'last-modified' and
  116. k ~= 'pragma'
  117. then
  118. expose = expose .. ',' .. k
  119. end
  120. end
  121. -- 不缓存非 GET 请求
  122. if ngx.req.get_method() ~= 'GET' then
  123. ngx.header['cache-control'] = 'no-cache'
  124. end
  125. flushHdr()