Skip to content

Commit 1ce0dd8

Browse files
committed
fix(patch): make cjson instances inherit decode_array_with_array_mt
lua-cjson options are per-instance and cjson.new() starts from the compile-time defaults, so enabling decode_array_with_array_mt on the cjson.safe singleton in core/json.lua never reaches a dependency that keeps a private instance. Those dependencies decode arrays without the array metatable, and an empty array is re-encoded as an object. Enable the option on both cjson singletons and wrap cjson.new / cjson.safe.new so instances created afterwards inherit it. This replaces the api7-lua-resty-session dependency swap: the fix now covers every dependency with a private instance (lua-resty-healthcheck, lua-resty-worker-events, lua-resty-aws, ...), not just lua-resty-session.
1 parent de6fb22 commit 1ce0dd8

3 files changed

Lines changed: 49 additions & 1 deletion

File tree

apisix-master-0.rockspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ dependencies = {
4646
"api7-lua-resty-jwt = 0.2.6-0",
4747
"lua-resty-hmac-ffi = 0.06-1",
4848
"lua-resty-cookie = 0.4.1-1",
49-
"api7-lua-resty-session = 4.1.6-0",
49+
"lua-resty-session = 4.1.5-1",
5050
"lua-resty-openapi-validator = 1.0.5-1",
5151
"opentracing-openresty = 0.1-0",
5252
"lua-resty-radixtree = 2.9.2-0",

apisix/patch.lua

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,31 @@ local function luasocket_tcp()
364364
end
365365

366366

367+
-- lua-cjson options are per-instance, and cjson.new() always starts from the
368+
-- compile-time defaults instead of inheriting the singleton's configuration.
369+
-- core/json.lua only enables decode_array_with_array_mt on the `cjson.safe`
370+
-- singleton, so every dependency holding a private instance (lua-resty-session,
371+
-- lua-resty-healthcheck, lua-resty-worker-events, lua-resty-aws, ...) decodes
372+
-- arrays without the array metatable, and an empty array is then re-encoded as
373+
-- an object. Enable the option on both singletons and let every instance created
374+
-- afterwards inherit it. This only changes the default: a library explicitly
375+
-- calling decode_array_with_array_mt(false) on its own instance still wins.
376+
local function patch_cjson(cjson)
377+
cjson.decode_array_with_array_mt(true)
378+
379+
local original_new = cjson.new
380+
cjson.new = function (...)
381+
local instance = original_new(...)
382+
patch_cjson(instance)
383+
return instance
384+
end
385+
end
386+
387+
367388
function _M.patch()
389+
patch_cjson(require("cjson"))
390+
patch_cjson(require("cjson.safe"))
391+
368392
-- make linter happy
369393
-- luacheck: ignore
370394
ngx_socket.tcp = function ()

t/core/json.t

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,27 @@ b.d: 1
193193
e: text
194194
a or default: default
195195
top-level null: nil
196+
197+
198+
199+
=== TEST 9: cjson instances created by dependencies keep empty arrays as arrays
200+
--- config
201+
location /t {
202+
content_by_lua_block {
203+
-- a dependency that keeps a private instance, e.g. lua-resty-session
204+
local safe_instance = require("cjson.safe").new()
205+
ngx.say("cjson.safe instance: ",
206+
safe_instance.encode(safe_instance.decode('{"arr":[]}')))
207+
208+
local instance = require("cjson").new()
209+
ngx.say("cjson instance: ", instance.encode(instance.decode('{"arr":[]}')))
210+
211+
-- the plain singleton is a separate config from cjson.safe
212+
local cjson = require("cjson")
213+
ngx.say("cjson singleton: ", cjson.encode(cjson.decode('{"arr":[]}')))
214+
}
215+
}
216+
--- response_body
217+
cjson.safe instance: {"arr":[]}
218+
cjson instance: {"arr":[]}
219+
cjson singleton: {"arr":[]}

0 commit comments

Comments
 (0)