Files
kong--kong/kong/tools/request_aware_table.lua
2026-07-13 12:32:21 +08:00

124 lines
3.1 KiB
Lua

--- NOTE: tool is designed to assist with **detecting** request contamination
-- issues on CI, during test runs. It does not offer security safeguards.
--
-- TODO: need to resolve the following issues in debug mode,
-- 1. `:clear` could not clear elements inserted by `table.insert()`
-- 2. `table.concat()` couldn't work for elements assigned using `indexing`
local table_new = require("table.new")
local table_clear = require("table.clear")
local get_request_id = require("kong.observability.tracing.request_id").get
-- set in new()
local is_not_debug_mode
local error = error
local rawset = rawset
local rawget = rawget
local setmetatable = setmetatable
local ALLOWED_REQUEST_ID_K = "__allowed_request_id"
-- Check if access is allowed for table, based on the request ID
local function enforce_sequential_access(table)
local curr_request_id = get_request_id()
if not curr_request_id then
-- allow access and reset allowed request ID
rawset(table, ALLOWED_REQUEST_ID_K, nil)
return
end
local allowed_request_id = rawget(table, ALLOWED_REQUEST_ID_K)
if not allowed_request_id then
-- first access. Set allowed request ID and allow access
rawset(table, ALLOWED_REQUEST_ID_K, curr_request_id)
return
end
if curr_request_id ~= table[ALLOWED_REQUEST_ID_K] then
error("concurrent access from different request to shared table detected", 2)
end
end
local function clear_table(self)
if is_not_debug_mode then
table_clear(self)
return
end
table_clear(self.__data)
rawset(self, ALLOWED_REQUEST_ID_K, nil)
end
local __proxy_mt = {
__index = function(t, k)
if k == "clear" then
return clear_table
end
enforce_sequential_access(t)
return t.__data[k]
end,
__newindex = function(t, k, v)
if k == "clear" then
error("cannot set the 'clear' method of request aware table", 2)
end
enforce_sequential_access(t)
t.__data[k] = v
end,
}
local __direct_mt = {
__index = { clear = clear_table },
__newindex = function(t, k, v)
if k == "clear" then
error("cannot set the 'clear' method of request aware table", 2)
end
rawset(t, k, v)
end,
}
-- Request aware table constructor
--
-- Creates a new table with request-aware access logic to protect the
-- underlying data from race conditions.
-- The table includes a :clear() method to delete all elements.
--
-- The request-aware access logic is turned off when `debug_mode` is disabled.
--
-- @param narr (optional) pre allocated array elements
-- @param nrec (optional) pre allocated hash elements
-- @return The newly created table with request-aware access
local function new(narr, nrec)
local data = table_new(narr or 0, nrec or 0)
if is_not_debug_mode == nil then
is_not_debug_mode = (kong.configuration.log_level ~= "debug")
end
-- return table without proxy when debug_mode is disabled
if is_not_debug_mode then
return setmetatable(data, __direct_mt)
end
-- wrap table in proxy (for access checks) when debug_mode is enabled
return setmetatable({ __data = data }, __proxy_mt)
end
return {
new = new,
}