Skip to content
Open
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
19 changes: 18 additions & 1 deletion apisix/core/table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ local pairs = pairs
local type = type
local ngx_re = require("ngx.re")
local isarray = require("table.isarray")
local str_byte = string.byte
local str_has_prefix = require("apisix.core.string").has_prefix
local DOT = str_byte(".")


local _M = {
Expand Down Expand Up @@ -120,6 +123,18 @@ end

local deepcopy
do
-- the members of `parent` are shallow-copied when `parent` is `prefix`
-- itself or one of its descendants. The match has to stop at a path
-- separator, otherwise a prefix like "self.value.plugins" would also
-- claim a sibling named "self.value.plugins2".
local function under_shallow_prefix(parent, prefix)
if not str_has_prefix(parent, prefix) then
return false
end

return #parent == #prefix or str_byte(parent, #prefix + 1) == DOT
end

local function _deepcopy(orig, copied, parent, opts)
-- If the array-like table contains nil in the middle,
-- the len might be smaller than the expected.
Expand All @@ -130,7 +145,9 @@ do
copied[orig] = copy
for orig_key, orig_value in pairs(orig) do
local path = parent .. "." .. tostring(orig_key)
if opts and array_find(opts.shallows, path) then
if opts and (array_find(opts.shallows, path) or
(opts.shallow_prefix and
under_shallow_prefix(parent, opts.shallow_prefix))) then
copy[orig_key] = orig_value
Comment thread
AlinsRan marked this conversation as resolved.
else
if type(orig_value) == "table" then
Expand Down
9 changes: 8 additions & 1 deletion apisix/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,14 @@ local function merge_consumer_route(route_conf, consumer_conf, consumer_group_co
return route_conf
end

local new_route_conf = core.table.deepcopy(route_conf)
-- some plugins cache request-time state on their conf object (resolved DNS
-- nodes, probed backend versions, ...). Deep-copying the plugin confs would
-- hand every consumer its own copy and drop that state, so keep them shared
-- by reference, as they already are on the route without consumer auth. The
-- `plugins` container itself is still a fresh table, so the merge below
-- overwrites its keys without touching the original route conf.
local new_route_conf = core.table.deepcopy(route_conf,
{ shallow_prefix = "self.value.plugins" })

if has_group_plugins then
for name, conf in pairs(consumer_group_conf.value.plugins) do
Expand Down
62 changes: 62 additions & 0 deletions t/core/table.t
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,65 @@ tab_copied.a.b.c == tab.a.b.c1: true
tab_copied.a.b.c == t1: true
tab_copied.x.y == tab.x.y: false
tab_copied.x.y == t2: false



=== TEST 13: shallow_prefix
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local deepcopy = core.table.deepcopy
local plugin1 = {name = "plugin1"}
local plugin2 = {name = "plugin2"}
local tab = {
plugins = {
p1 = plugin1,
p2 = plugin2,
},
}

local tab_copied = deepcopy(tab, { shallow_prefix = "self.plugins" })

tab_copied.plugins.p1 = {name = "plugin1_new_modified"}
tab_copied.plugins.p2.name = "plugin2_modified"

ngx.say("table copied: ", require("toolkit.json").encode(tab_copied))
ngx.say("table original: ", require("toolkit.json").encode(tab))
}
}
--- request
GET /t
--- response_body
table copied: {"plugins":{"p1":{"name":"plugin1_new_modified"},"p2":{"name":"plugin2_modified"}}}
table original: {"plugins":{"p1":{"name":"plugin1"},"p2":{"name":"plugin2_modified"}}}
--- no_error_log
[error]



=== TEST 14: shallow_prefix only matches at a path separator
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local deepcopy = core.table.deepcopy
local tab = {
plugins = { p1 = {name = "p1"} },
-- a sibling whose name starts with the prefix
plugins2 = { p2 = {name = "p2"} },
}

local tab_copied = deepcopy(tab, { shallow_prefix = "self.plugins" })

ngx.say("plugins.p1 shared: ", tab_copied.plugins.p1 == tab.plugins.p1)
ngx.say("plugins2.p2 shared: ", tab_copied.plugins2.p2 == tab.plugins2.p2)
}
}
--- request
GET /t
--- response_body
plugins.p1 shared: true
plugins2.p2 shared: false
--- no_error_log
[error]
Loading