diff --git a/kong/plugins/proxy-cache/handler.lua b/kong/plugins/proxy-cache/handler.lua index 4b2c04421951..1992bd37612c 100644 --- a/kong/plugins/proxy-cache/handler.lua +++ b/kong/plugins/proxy-cache/handler.lua @@ -10,22 +10,17 @@ local ngx = ngx local kong = kong local type = type local pairs = pairs -local tostring = tostring -local tonumber = tonumber -local max = math.max local floor = math.floor local lower = string.lower -local concat = table.concat local time = ngx.time local resp_get_headers = ngx.resp and ngx.resp.get_headers -local ngx_re_gmatch = ngx.re.gmatch local ngx_re_sub = ngx.re.gsub local ngx_re_match = ngx.re.match -local parse_http_time = ngx.parse_http_time local parse_mime_type = mime_type.parse_mime_type +local parse_directive_header = require("kong.tools.http").parse_directive_header +local calculate_resource_ttl = require("kong.tools.http").calculate_resource_ttl -local tab_new = require("table.new") local STRATEGY_PATH = "kong.plugins.proxy-cache.strategies" @@ -74,39 +69,6 @@ local function set_res_header(res, header, value, conf) end end -local function parse_directive_header(h) - if not h then - return EMPTY - end - - if type(h) == "table" then - h = concat(h, ", ") - end - - local t = {} - local res = tab_new(3, 0) - local iter = ngx_re_gmatch(h, "([^,]+)", "oj") - - local m = iter() - while m do - local _, err = ngx_re_match(m[0], [[^\s*([^=]+)(?:=(.+))?]], - "oj", nil, res) - if err then - kong.log.err(err) - end - - -- store the directive token as a numeric value if it looks like a number; - -- otherwise, store the string value. for directives without token, we just - -- set the key to true - t[lower(res[1])] = tonumber(res[2]) or res[2] or true - - m = iter() - end - - return t -end - - local function req_cc() return parse_directive_header(ngx.var.http_cache_control) end @@ -117,27 +79,6 @@ local function res_cc() end -local function resource_ttl(res_cc) - local max_age = res_cc["s-maxage"] or res_cc["max-age"] - - if not max_age then - local expires = ngx.var.sent_http_expires - - -- if multiple Expires headers are present, last one wins - if type(expires) == "table" then - expires = expires[#expires] - end - - local exp_time = parse_http_time(tostring(expires)) - if exp_time then - max_age = exp_time - time() - end - end - - return max_age and max(max_age, 0) or 0 -end - - local function cacheable_request(conf, cc) -- TODO refactor these searches to O(1) do @@ -227,7 +168,7 @@ local function cacheable_response(conf, cc) return false end - if conf.cache_control and resource_ttl(cc) <= 0 then + if conf.cache_control and calculate_resource_ttl(cc) <= 0 then return false end @@ -425,7 +366,7 @@ function ProxyCacheHandler:header_filter(conf) if cacheable_response(conf, cc) then -- TODO: should this use the kong.conf configured limit? proxy_cache.res_headers = resp_get_headers(0, true) - proxy_cache.res_ttl = conf.cache_control and resource_ttl(cc) or conf.cache_ttl + proxy_cache.res_ttl = conf.cache_control and calculate_resource_ttl(cc) or conf.cache_ttl else set_header(conf, "X-Cache-Status", "Bypass") diff --git a/kong/tools/http.lua b/kong/tools/http.lua index d95648ae9138..7101470e0e5d 100644 --- a/kong/tools/http.lua +++ b/kong/tools/http.lua @@ -15,7 +15,15 @@ local re_match = ngx.re.match local join = require("kong.tools.string").join local split = require("kong.tools.string").split local strip = require("kong.tools.string").strip +local parse_http_time = ngx.parse_http_time +local time = ngx.time +local ngx_re_gmatch = ngx.re.gmatch +local ngx_re_match = ngx.re.match +local lower = string.lower +local max = math.max +local tab_new = require("table.new") +local EMPTY = {} local _M = {} @@ -555,4 +563,63 @@ do end end +-- Parses a HTTP header value into a table of directives +-- eg: Cache-Control: public, max-age=3600 +-- => { public = true, ["max-age"] = 3600 } +-- @param h (string) the header value to parse +-- @return table a table of directives +function _M.parse_directive_header(h) + if not h then + return EMPTY + end + + if type(h) == "table" then + h = concat(h, ", ") + end + + local t = {} + local res = tab_new(3, 0) + local iter = ngx_re_gmatch(h, "([^,]+)", "oj") + + local m = iter() + while m do + local _, err = ngx_re_match(m[0], [[^\s*([^=]+)(?:=(.+))?]], "oj", nil, res) + if err then + kong.log.err(err) + end + + -- store the directive token as a numeric value if it looks like a number; + -- otherwise, store the string value. for directives without token, we just + -- set the key to true + t[lower(res[1])] = tonumber(res[2]) or res[2] or true + + m = iter() + end + + return t +end + +-- Calculates resource Time-To-Live (TTL) based on Cache-Control headers +-- @param res_cc (table) the Cache-Control headers, as parsed by `parse_directive_header` +-- @return number the TTL in seconds +function _M.calculate_resource_ttl(res_cc) + local max_age = res_cc and (res_cc["s-maxage"] or res_cc["max-age"]) + + if not max_age then + local expires = ngx.var.sent_http_expires + + if type(expires) == "table" then + expires = expires[#expires] + end + + local exp_time = parse_http_time(tostring(expires)) + if exp_time then + max_age = exp_time - time() + end + end + + return max_age and max(max_age, 0) or 0 +end + + return _M