diff --git a/apisix/core/request_json.lua b/apisix/core/request_json.lua index 90ec9a058fa8..d635681f6a8c 100644 --- a/apisix/core/request_json.lua +++ b/apisix/core/request_json.lua @@ -19,6 +19,8 @@ local config_local = require("apisix.core.config_local") local core_json = require("apisix.core.json") local qjson = require("qjson") local simdjson = require("resty.simdjson") +local pcall = pcall +local tostring = tostring local simdjson_parser, simdjson_err = simdjson.new() @@ -28,6 +30,39 @@ local configured_name local _M = {} +local function qjson_decode(str) + local ok, decoded, err = pcall(qjson.decode, str) + if not ok then + return nil, tostring(decoded) + end + + if decoded == nil then + return nil, err + end + + ok, decoded, err = pcall(qjson.materialize, decoded) + if not ok then + return nil, tostring(decoded) + end + + if decoded == nil then + return nil, err + end + + return decoded +end + + +local function qjson_encode(data) + local ok, encoded, err = pcall(qjson.encode, data) + if not ok then + return nil, tostring(encoded) + end + + return encoded, err +end + + function _M.decode(str) if not configured_name then configured_name = config_local.local_conf().apisix.request_body_json_lib @@ -42,12 +77,7 @@ function _M.decode(str) return simdjson_parser:decode(str) end - local decoded, err = qjson.decode(str) - if not decoded then - return nil, err - end - - return qjson.materialize(decoded) + return qjson_decode(str) end @@ -57,7 +87,7 @@ function _M.encode(data) end if configured_name == "qjson" then - return qjson.encode(data) + return qjson_encode(data) end -- simdjson encode is slower than cjson, so simdjson mode only uses it for decode. diff --git a/t/core/request.t b/t/core/request.t index fb72d075e13f..d0d4c145f20e 100644 --- a/t/core/request.t +++ b/t/core/request.t @@ -629,7 +629,50 @@ cjson encode: {"lib":"body"} -=== TEST 19: simdjson preserves empty arrays for cjson encoding +=== TEST 19: qjson decode and encode errors are returned +--- yaml_config +apisix: + request_body_json_lib: qjson +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local request_json = require("apisix.core.request_json") + + ngx.ctx.api_ctx = {} + + local body, body_err = core.request.get_json_request_body_table() + ngx.say("body nil: ", body == nil) + ngx.say("body error: ", body_err and body_err.message and + body_err.message:find("could not parse JSON request body:", 1, true) == 1) + + local decoded, decode_err = request_json.decode("{") + ngx.say("decode nil: ", decoded == nil) + ngx.say("decode error: ", type(decode_err) == "string" and #decode_err > 0) + + local encoded, encode_err = request_json.encode({bad = function() end}) + ngx.say("encode nil: ", encoded == nil) + ngx.say("encode error: ", type(encode_err) == "string" and #encode_err > 0) + } + } +--- request +POST /t +{ +--- more_headers +Content-Type: application/json +--- response_body +body nil: true +body error: true +decode nil: true +decode error: true +encode nil: true +encode error: true +--- no_error_log +[error] + + + +=== TEST 20: simdjson preserves empty arrays for cjson encoding --- yaml_config apisix: request_body_json_lib: simdjson @@ -653,7 +696,7 @@ tags array: true -=== TEST 20: ai transport encoders use request_json +=== TEST 21: ai transport encoders use request_json --- config location /t { content_by_lua_block {