http-enc-res-hdr.lua 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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. -- 由于接口路径固定,为避免被缓存,以请求头的 --url 值区分缓存
  10. local vary = '--url'
  11. local function addHdr(k, v)
  12. ngx.header[k] = v
  13. if detail then
  14. expose = expose .. ',' .. k
  15. end
  16. end
  17. local function flushHdr()
  18. if detail then
  19. expose = expose .. ',--s'
  20. -- 该字段不在 aceh 中,如果浏览器能读取到,说明支持 * 通配
  21. ngx.header['--t'] = '1'
  22. end
  23. local status = ngx.status
  24. ngx.header['access-control-expose-headers'] = expose
  25. ngx.header['access-control-allow-origin'] = '*'
  26. ngx.header['vary'] = vary
  27. ngx.header['--s'] = status
  28. if
  29. status == 301 or
  30. status == 302 or
  31. status == 303 or
  32. status == 307 or
  33. status == 308
  34. then
  35. status = status + 10
  36. end
  37. ngx.status = status
  38. end
  39. local function addVary(v)
  40. if type(v) == 'table' then
  41. vary = vary .. ',' .. table.concat(v, ',')
  42. else
  43. vary = vary .. ',' .. v
  44. end
  45. end
  46. local function nodeSwitched()
  47. local status = ngx.status
  48. if status ~= 200 and status ~= 206 then
  49. return false
  50. end
  51. local level = ngx.ctx._level
  52. if level == nil or level == 0 then
  53. return false
  54. end
  55. if ngx.req.get_method() ~= 'GET' then
  56. return false
  57. end
  58. if ngx.header['set-cookie'] ~= nil then
  59. return false
  60. end
  61. local resLenStr = ngx.header['content-length']
  62. if resLenStr == nil then
  63. return false
  64. end
  65. -- 小于 400KB 的资源不走加速
  66. local resLenNum = tonumber(resLenStr)
  67. if resLenNum == nil or resLenNum < 1000 * 400 then
  68. return false
  69. end
  70. local addr = ngx.var.upstream_addr or ''
  71. local etag = ngx.header['etag'] or ''
  72. local last = ngx.header['last-modified'] or ''
  73. local info = addr .. '|' .. resLenStr .. '|' .. etag .. '|' .. last
  74. -- clear all res headers
  75. local h, err = ngx.resp.get_headers()
  76. for k, v in pairs(h) do
  77. ngx.header[k] = nil
  78. end
  79. addHdr('--raw-info', info)
  80. addHdr('--switched', '1')
  81. ngx.header['cache-control'] = 'no-cache'
  82. ngx.var._switched = resLenStr
  83. ngx.ctx._switched = true
  84. flushHdr()
  85. return true
  86. end
  87. -- 节点切换功能,目前还在测试中(demo 中已开启)
  88. -- if nodeSwitched() then
  89. -- return
  90. -- end
  91. local h, err = ngx.resp.get_headers()
  92. for k, v in pairs(h) do
  93. if
  94. -- 这些头有特殊意义,需要转义 --
  95. k == 'access-control-allow-origin' or
  96. k == 'access-control-expose-headers' or
  97. k == 'location' or
  98. k == 'set-cookie'
  99. then
  100. if type(v) == 'table' then
  101. -- 重复的字段,例如 Set-Cookie
  102. -- 转换成 1-Set-Cookie, 2-Set-Cookie, ...
  103. for i = 1, #v do
  104. addHdr(i .. '-' .. k, v[i])
  105. end
  106. else
  107. addHdr('--' .. k, v)
  108. end
  109. ngx.header[k] = nil
  110. elseif k == 'vary' then
  111. addVary(v)
  112. elseif detail and
  113. -- 非简单头无法被 fetch 读取,需添加到 aceh 列表 --
  114. -- https://developer.mozilla.org/en-US/docs/Glossary/Simple_response_header
  115. k ~= 'cache-control' and
  116. k ~= 'content-language' and
  117. k ~= 'content-type' and
  118. k ~= 'expires' and
  119. k ~= 'last-modified' and
  120. k ~= 'pragma'
  121. then
  122. expose = expose .. ',' .. k
  123. end
  124. end
  125. flushHdr()