diff --git a/apisix/core/ctx.lua b/apisix/core/ctx.lua index 64d1093312db..bfe9b3199e60 100644 --- a/apisix/core/ctx.lua +++ b/apisix/core/ctx.lua @@ -208,6 +208,8 @@ do -- var.args should not be cached as it can be changed via set_uri_args args = true, is_args = true, + -- var.status changes after the response status is determined + status = true, } local ngx_var_names = { diff --git a/apisix/plugin.lua b/apisix/plugin.lua index 65179364e741..2fb1ac4e7492 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -525,7 +525,7 @@ function _M.trace_expected_plugins_for_debug(api_ctx) end -local function meta_filter(ctx, plugin_name, plugin_conf) +local function meta_filter(ctx, plugin_name, plugin_conf, phase) local filter = plugin_conf._meta and plugin_conf._meta.filter if not filter then return true @@ -533,7 +533,8 @@ local function meta_filter(ctx, plugin_name, plugin_conf) local match_cache_key = ctx.conf_type .. "#" .. ctx.conf_id .. "#" - .. ctx.conf_version .. "#" .. plugin_name .. "#meta_filter_matched" + .. ctx.conf_version .. "#" .. plugin_name .. "#" + .. phase .. "#meta_filter_matched" if ctx[match_cache_key] ~= nil then return ctx[match_cache_key] end @@ -1356,7 +1357,7 @@ function _M.run_plugin(phase, plugins, api_ctx) local phase_func = plugins[i][exec_phase] if phase_func then local conf = plugins[i + 1] - if not meta_filter(api_ctx, plugins[i]["name"], conf)then + if not meta_filter(api_ctx, plugins[i]["name"], conf, phase) then goto CONTINUE end @@ -1403,7 +1404,7 @@ function _M.run_plugin(phase, plugins, api_ctx) for i = 1, #plugins, 2 do local phase_func = plugins[i][phase] local conf = plugins[i + 1] - if phase_func and meta_filter(api_ctx, plugins[i]["name"], conf) then + if phase_func and meta_filter(api_ctx, plugins[i]["name"], conf, phase) then -- skip a plugin already run as a workflow action, before any meta hooks if api_ctx._skip_plugins and api_ctx._skip_plugins[plugins[i]["name"]] then goto CONTINUE @@ -1536,7 +1537,7 @@ function _M.lua_response_filter(api_ctx, headers, body, no_flush, wait) local phase_func = plugins[i]["lua_body_filter"] if phase_func then local conf = plugins[i + 1] - if not meta_filter(api_ctx, plugins[i]["name"], conf)then + if not meta_filter(api_ctx, plugins[i]["name"], conf, "lua_body_filter") then goto CONTINUE end diff --git a/t/core/ctx2.t b/t/core/ctx2.t index 7782ac9125cd..b620e27536a2 100644 --- a/t/core/ctx2.t +++ b/t/core/ctx2.t @@ -447,3 +447,26 @@ find ctx.var.a6_labels_zone: Singapore --- response_body find ctx.var.a6_count: 1 find ctx.var.a6_count: 2 + + + +=== TEST 20: status is not cached across response changes +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local ctx = {} + core.ctx.set_vars_meta(ctx) + + ngx.status = 201 + local first = ctx.var.status + ngx.status = 202 + local second = ctx.var.status + + ngx.status = 200 + core.ctx.release_vars(ctx) + ngx.say(first, ", ", second) + } + } +--- response_body +201, 202 diff --git a/t/plugin/file-logger3.t b/t/plugin/file-logger3.t new file mode 100644 index 000000000000..5f033c6f5448 --- /dev/null +++ b/t/plugin/file-logger3.t @@ -0,0 +1,186 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +no_long_string(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: configure response-phase filter +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + os.remove("file-logger-phase-filter.log") + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "file-logger": { + "path": "file-logger-phase-filter.log", + "log_format": { + "status": "$status", + "upstream_status": "$upstream_status" + }, + "_meta": { + "filter": [ + ["upstream_status", "==", "201"] + ] + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: reevaluate filter after upstream response +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin").test + local code = t("/specific_status", ngx.HTTP_GET, nil, nil, + { ["x-test-upstream-status"] = "201" }) + + local file, err = io.open("file-logger-phase-filter.log", "r") + if not file then + core.log.error("failed to open log file: ", err) + ngx.status = 500 + return + end + + local entry = core.json.decode(file:read("*l")) + file:close() + + ngx.status = code + ngx.say("status: ", entry.status) + ngx.say("upstream_status: ", entry.upstream_status) + } + } +--- error_code: 201 +--- response_body +status: 201 +upstream_status: 201 + + + +=== TEST 3: configure a filtered logger and an unfiltered logger +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + os.remove("file-logger-status-cache.log") + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "http-logger": { + "uri": "http://127.0.0.1:1980/log", + "_meta": { + "filter": [ + ["status", "==", 404] + ] + } + }, + "file-logger": { + "path": "file-logger-status-cache.log", + "log_format": { + "status": "$status" + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 4: filtered logger does not cache status for another logger +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin").test + local code = t("/specific_status", ngx.HTTP_GET, nil, nil, + { ["x-test-upstream-status"] = "201" }) + + local file, err = io.open("file-logger-status-cache.log", "r") + if not file then + core.log.error("failed to open log file: ", err) + ngx.status = 500 + return + end + + local entry = core.json.decode(file:read("*l")) + file:close() + + ngx.status = code + ngx.say("status: ", entry.status) + } + } +--- error_code: 201 +--- response_body +status: 201