Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft]Feat/temp pdk test #13988

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kong-3.9.0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ build = {
["kong.concurrency"] = "kong/concurrency.lua",
["kong.deprecation"] = "kong/deprecation.lua",
["kong.globalpatches"] = "kong/globalpatches.lua",
["kong.header_cache_utils"] = "kong/header_cache_utils.lua",
["kong.error_handlers"] = "kong/error_handlers.lua",
["kong.hooks"] = "kong/hooks.lua",

Expand Down
15 changes: 15 additions & 0 deletions kong/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,21 @@ local constants = {
STREAM_TLS_PASSTHROUGH = "sp",
STREAM_RPC = "rp",
},

CACHE_HEADERS = {
{
KEY = "req_headers_cache",
FLAG = "req_headers_cache_flag",
-- nginx buildin req headers which only support single value
SINGLE_VALUES = {"Host", "Connection", "If-Modified-Since","If-Unmodified-Since","If-Match","If-None-Match","User-Agent","Referer", "Content-Length", "Content-Type", "Range", "If-Range", "Transfer-Encoding", "Expect", "Upgrade", "Accept-Encoding", "Via", "Authorization", "Keep-Alive", "X-Real-IP", "Depth", "Destination", "Overwrite","Date"},
},
{
KEY = "resp_headers_cache",
FLAG = "resp_headers_cache_flag",
-- nginx buildin resp headers which only support single value
SINGLE_VALUES = {"Server", "Date", "Content-Encoding","Location","Refresh","Last-Modified","Content-Range","Accept-Ranges", "WWW-Authenticate", "Expires", "E-Tag", "ETag", "Content-Length", "Content-Type"},
},
}
}

for _, v in ipairs(constants.CLUSTERING_SYNC_STATUS) do
Expand Down
71 changes: 69 additions & 2 deletions kong/globalpatches.lua
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ return function(options)
local error = error

local get_req_headers = ngx.req.get_headers
local set_req_header = ngx.req.set_header
local clear_req_header = ngx.req.clear_header
local add_req_header = require("ngx.req").add_header
local get_resp_headers = ngx.resp.get_headers
local add_resp_header = require("ngx.resp").add_header
local read_req_body = ngx.req.read_body
local get_uri_args = ngx.req.get_uri_args
local get_post_args = ngx.req.get_post_args
local decode_args = ngx.decode_args
Expand All @@ -139,14 +144,20 @@ return function(options)
local MAX_URI_ARGS
local MAX_POST_ARGS
local MAX_DECODE_ARGS
local header_cache = require("kong.header_cache_utils")

-- REQUEST HEADERS [
local function get_req_headers_real(max_req_headers, ...)
local cached_headers = header_cache.get_headers_cache(1)
if cached_headers then
return cached_headers
end
local request_headers, err = get_req_headers(max_req_headers or MAX_REQ_HEADERS or DEFAULT_MAX_REQ_HEADERS, ...)
if err == "truncated" then
kong.log.notice("request headers truncated")
end
return request_headers, err
header_cache.set_headers_cache(1, request_headers)
return header_cache.get_headers_cache(1), err
end

_G.ngx.req.get_headers = function(max_req_headers, ...)
Expand All @@ -157,15 +168,35 @@ return function(options)
_G.ngx.req.get_headers = get_req_headers_real
return get_req_headers_real(max_req_headers or MAX_REQ_HEADERS, ...)
end

_G.ngx.req.set_header = function(header_name, header_value)
set_req_header(header_name, header_value)
header_cache.set_header_cache(1, header_name, header_value, true)
end

_G.ngx.req.clear_header = function(header_name)
clear_req_header(header_name)
header_cache.set_header_cache(1, header_name, nil, true)
end

_G.ngx.req.add_header = function(header_name, header_value)
add_req_header(header_name, header_value)
header_cache.set_header_cache(1, header_name, header_value)
end
-- ]

-- RESPONSE HEADERS [
local function get_resp_headers_real(max_resp_headers, ...)
local cached_headers = header_cache.get_headers_cache(2)
if cached_headers then
return cached_headers
end
local response_headers, err = get_resp_headers(max_resp_headers or MAX_RESP_HEADERS or DEFAULT_MAX_RESP_HEADERS, ...)
if err == "truncated" then
kong.log.notice("response headers truncated")
end
return response_headers, err
header_cache.set_headers_cache(2, response_headers)
return header_cache.get_headers_cache(2), err;
end

_G.ngx.resp.get_headers = function(max_resp_headers, ...)
Expand All @@ -176,6 +207,42 @@ return function(options)
_G.ngx.resp.get_headers = get_resp_headers_real
return get_resp_headers_real(max_resp_headers or MAX_RESP_HEADERS, ...)
end

_G.ngx.resp.add_header = function(header_name, header_value)
add_resp_header(header_name, header_value)
header_cache.set_header_cache(2, header_name, header_value)
end

-- patch resp header getter/setter `ngx.header.HEADER`
do
local resp_header = ngx.header
local mt = table.new(0, 2)

mt.__index = function(t, k)
local value = header_cache.get_header_cache(2, k)
if not value then
value = resp_header[k]
header_cache.set_header_cache(2, k, value, true)
end
return value
end
mt.__newindex = function(t, k, v)
resp_header[k] = v
header_cache.set_header_cache(2, k, v, true)
end

ngx.header = setmetatable(table.new(0, 0), mt)
end
-- ]

-- READ REQUEST BODY [
_G.ngx.req.read_body = function()
-- for the same request, only one `read_body` call is needed
if not ngx.ctx.body_read then
read_req_body()
ngx.ctx.body_read = true
end
end
-- ]

-- URI ARGS [
Expand Down
211 changes: 211 additions & 0 deletions kong/header_cache_utils.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
local str_replace_char = require("resty.core.utils").str_replace_char
local CACHE_HEADERS = require("kong.constants").CACHE_HEADERS

local function normalize_header_name(header)
return header:lower()
-- return str_replace_char(header:lower(), "-", "_")
end

local function Set (list)
local set = {}
for _, l in ipairs(list) do set[normalize_header_name(l)] = true end
return set
end

do
for k, v in pairs(CACHE_HEADERS) do
v.SINGLE_VALUES = Set(v.SINGLE_VALUES)
end
end

local function normalize_header_value(value)
local tvalue = type(value)

if tvalue == "table" then
for i, v in ipairs(value) do
value[i] = normalize_header_value(v)
end
return value
elseif value == nil or (value == "") then
return nil
elseif tvalue == "string" then
return value
end

-- header is number or boolean
return tostring(value)
end

local function get_recover_cache(proxy)
if (next(proxy.kmagic_inner) ~= nil) then
if (next(proxy.kmagic_dirty) ~= nil) then
for k, v in pairs(proxy.kmagic_dirty) do
proxy.kmagic_inner[k] = v
end
proxy.kmagic_dirty = {}
end
if (next(proxy.kmagic_added)) then
for k, _ in pairs(proxy.kmagic_added) do
proxy.kmagic_inner[k] = nil
end
proxy.kmagic_added = {}
end
elseif next(proxy.kmagic_added) then
-- original cache is empty, just need to clear added
proxy.kmagic_added = {}
end
end


local function get_headers_cache_proxy(group)
local cache_key = CACHE_HEADERS[group].KEY
if ngx.ctx[cache_key] then
return ngx.ctx[cache_key]
end
ngx.ctx[cache_key] = {
kmagic_dirty = {},
kmagic_inner = {},
kmagic_added = {},
}
local pxy = ngx.ctx[cache_key]
local mt2 = {
__index = function(_, k)
return pxy.kmagic_inner[normalize_header_name(k)]
end,
__newindex = function(_, k, v)
k = normalize_header_name(k)
local t = pxy.kmagic_inner
local dirty = pxy.kmagic_dirty
local added = pxy.kmagic_added
if dirty[k] == nil and t[k] then
dirty[k] = t[k]
end
if added[k] == nil and t[k] == nil and v ~= nil then
added[k]=true
end
t[k] = v
end,
__pairs = function()
return next, pxy.kmagic_inner, nil
end
}
setmetatable(pxy, mt2)
return pxy
end

local function clear_header_cache_proxy(group)
-- ngx.print("INIT\n")
local proxy = get_headers_cache_proxy(group)
proxy.kmagic_inner = {}
proxy.kmagic_dirty = {}
proxy.kmagic_added = {}
end

local function get_headers_cache(group)
local proxy = get_headers_cache_proxy(group)
get_recover_cache(proxy)
return ngx.ctx[CACHE_HEADERS[group].FLAG] and proxy
end

local function builtin_header_single_handler(cache, key, value)
key = normalize_header_name(key)
if type(value) == "table" then
if (#value == 0) then
cache[key] = nil
else
cache[key] = normalize_header_value(value[#value])
end
else
cache[key] = normalize_header_value(value)
end
end

local function multi_value_handler(cache, key, value, override)
key = normalize_header_name(key)

-- if add_header with empty string (override == nil and value == ""), special handling to keep consistency
if (override or value ~= "") then
value = normalize_header_value(value)
end

if override then
-- override mode, assign directly
cache[key] = value
else
if not cache[key] then
-- if cache key not exists, assign directly
cache[key] = value
return
end

-- field exists for adding, change single value to list
if type(cache[key]) ~= "table" then
cache[key] = { cache[key] }
end

-- values insert
if type(value) == "table" then
for i, v in ipairs(value) do
table.insert(cache[key], v)
end
else
table.insert(cache[key], value)
end
end
end

local function set_header_cache(group, name, value, override)
name = normalize_header_name(name)
local proxy = get_headers_cache_proxy(group)
local cache = proxy.kmagic_inner
if (proxy.kmagic_dirty[name]) then
proxy.kmagic_dirty[name] = nil
elseif (proxy.kmagic_added[name]) then
proxy.kmagic_added[name] = nil
end
if CACHE_HEADERS[group].SINGLE_VALUES[name] then
builtin_header_single_handler(cache, name, value)
else
multi_value_handler(cache, name, value, override)
end
end

-- set cache headers by group[request/response]
local function set_headers_cache(group, values)
-- if no key specified, assign the whole cache group value
-- local cache_key = CACHE_HEADERS[group].KEY
local cache_flag = CACHE_HEADERS[group].FLAG
-- ngx.ctx[CACHE_HEADERS[group].key] = nil
clear_header_cache_proxy(group)
local proxy = get_headers_cache_proxy(group)
proxy.kmagic_inner = values
if not ngx.ctx[cache_flag] then
ngx.ctx[cache_flag] = true
end
end


-- get cache header
local function get_header_cache(group, key)
local proxy = get_headers_cache_proxy(group)
get_recover_cache(proxy)
return proxy[key]
end

local function clear_headers_cache()
clear_header_cache_proxy(1)
clear_header_cache_proxy(2)
-- ngx.ctx[CACHE_HEADERS[1].KEY] = nil
-- ngx.ctx[CACHE_HEADERS[2].KEY] = nil
ngx.ctx[CACHE_HEADERS[1].FLAG] = nil
ngx.ctx[CACHE_HEADERS[2].FLAG] = nil
end

return {
set_header_cache = set_header_cache,
set_headers_cache = set_headers_cache,
get_headers_cache = get_headers_cache,
get_header_cache = get_header_cache,
clear_headers_cache = clear_headers_cache,
normalize_header_name = normalize_header_name,
}
3 changes: 3 additions & 0 deletions kong/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ local pl_file = require "pl.file"
local req_dyn_hook = require "kong.dynamic_hook"
local uuid = require("kong.tools.uuid").uuid
local kong_time = require("kong.tools.time")
local clear_headers_cache = require("kong.header_cache_utils").clear_headers_cache


local kong = kong
Expand Down Expand Up @@ -1202,6 +1203,8 @@ function Kong.access()

execute_collecting_plugins_iterator(plugins_iterator, "access", ctx)

-- clear headers cache after access phase execution
clear_headers_cache()
if ctx.delayed_response then
ctx.KONG_ACCESS_ENDED_AT = get_updated_now_ms()
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT - ctx.KONG_ACCESS_START
Expand Down
Loading
Loading