diff --git a/spec/01-unit/28-request-aware-table_spec.lua b/spec/01-unit/28-request-aware-table_spec.lua index 08ebc183c45..b82456b65a1 100644 --- a/spec/01-unit/28-request-aware-table_spec.lua +++ b/spec/01-unit/28-request-aware-table_spec.lua @@ -1,39 +1,7 @@ -local utils = require "kong.tools.utils" local tablex = require "pl.tablex" local rat -local function assert_rw_allowed(tab, orig_t) - for k, v in pairs(orig_t or {}) do - -- reads from orig_t succeed - local val = assert.has_no.errors(function() return tab[k] end) - assert.equal(v, val) - end - - local k = utils.random_string() - local v = utils.random_string() - -- writing new values succeeds - assert.has_no.errors(function() tab[k] = v end) - -- reading new values succeeds - local val = assert.has_no.errors(function() return tab[k] end) - assert.equal(v, val) -end - -local function assert_rw_denied(tab, orig_t) - local err_str = "race condition detected" - for k, v in pairs(orig_t or {}) do - -- reads from orig_t error out - assert.error_matches(function() return nil, tab[k] == v end, err_str) - end - - local k = utils.random_string() - local v = utils.random_string() - -- writing new values errors out - assert.error_matches(function() tab[k] = v end, err_str) - -- reading new values errors out - assert.error_matches(function() return tab[k] == v end, err_str) -end - describe("Request aware table", function() local old_ngx local tab @@ -52,51 +20,16 @@ describe("Request aware table", function() end) describe("with concurrency check enabled", function() - local orig_t + local orig_t = {} before_each(function() - orig_t = { - k1 = utils.random_string(), - k2 = utils.random_string(), - } tab = rat.new(orig_t, "on") end) - it("allows access when there are no race conditions", function() - -- create a new RAT with request_id = 1 (clear after use) - _G.ngx.var.request_id = "1" - assert_rw_allowed(tab, orig_t) - tab.clear() - - -- reuse RAT with different request_id (allowed) - _G.ngx.var.request_id = "2" - assert_rw_allowed(tab) - end) - - it("denies access when there are race conditions", function() - -- create a new RAT with request_id = 1 (no clear) - _G.ngx.var.request_id = "1" - assert_rw_allowed(tab, orig_t) - - -- reuse RAT with different request_id (not allowed) - _G.ngx.var.request_id = "2" - assert_rw_denied(tab) - end) - - it("clears the table successfully", function() - -- create a new RAT with request_id = 1 (clear after use) - _G.ngx.var.request_id = "1" - assert_rw_allowed(tab, orig_t) - tab.clear() - - assert.same(0, tablex.size(orig_t)) - end) - it("allows defining a custom clear function", function() - -- create a new RAT with request_id = 1 (clear after use) - _G.ngx.var.request_id = "1" orig_t.persist = "persistent_value" - assert_rw_allowed(tab, orig_t) + orig_t.foo = "bar" + orig_t.baz = "qux" -- custom clear function that keeps persistent_value tab.clear(function(t) @@ -116,41 +49,4 @@ describe("Request aware table", function() assert.same(0, tablex.size(orig_t)) end) end) - - describe("with concurrency check disabled", function() - local orig_t - - before_each(function() - orig_t = { - k1 = utils.random_string(), - k2 = utils.random_string(), - } - tab = rat.new(orig_t, "off") - end) - - before_each(function() - tab.clear() - end) - - it("allows access when there are no race conditions", function() - -- create a new RAT with request_id = 1 (clear after use) - _G.ngx.var.request_id = "1" - assert_rw_allowed(tab, orig_t) - tab.clear() - - -- reuse RAT with different request_id (allowed) - _G.ngx.var.request_id = "2" - assert_rw_allowed(tab, orig_t) - end) - - it("allows access when there are race conditions", function() - -- create a new RAT with request_id = 1, (no clear) - _G.ngx.var.request_id = "1" - assert_rw_allowed(tab, orig_t) - - -- reuse RAT with different request_id (allowed with check disabled) - _G.ngx.var.request_id = "2" - assert_rw_allowed(tab, orig_t) - end) - end) end) diff --git a/spec/02-integration/05-proxy/31-request-aware-table_spec.lua b/spec/02-integration/05-proxy/31-request-aware-table_spec.lua new file mode 100644 index 00000000000..ca34bf9eedf --- /dev/null +++ b/spec/02-integration/05-proxy/31-request-aware-table_spec.lua @@ -0,0 +1,121 @@ +local helpers = require "spec.helpers" + + +local function clear_table(client, checks) + local res = client:get("/", { + query = { + checks = checks, + clear = true, + } + }) + assert.response(res).has.status(200) + assert.logfile().has.no.line("[error]", true) +end + +for _, checks in ipairs({ true, false }) do +for _, strategy in helpers.each_strategy() do + describe("request aware table tests [#" .. strategy .. "] .. checks=" .. tostring(checks), function() + local client + lazy_setup(function() + local bp = helpers.get_db_utils(strategy, { + "plugins", + "routes", + "services", + }, { + "request-aware-table" + }) + + local service = assert(bp.services:insert({ + url = helpers.mock_upstream_url + })) + + local route = bp.routes:insert({ + service = service, + paths = { "/" } + }) + + bp.plugins:insert({ + name = "request-aware-table", + route = { id = route.id }, + }) + + helpers.start_kong({ + database = strategy, + plugins = "bundled, request-aware-table", + nginx_conf = "spec/fixtures/custom_nginx.template", + }) + end) + + lazy_teardown(function() + helpers.stop_kong() + end) + + before_each(function() + helpers.clean_logfile() + client = helpers.proxy_client() + clear_table(client, checks) + end) + + after_each(function() + if client then + client:close() + end + end) + + describe("with concurrency check enabled", function() + it("allows access when there are no race conditions", function() + local res = client:get("/", { + query = { + checks = checks, + } + }) + assert.response(res).has.status(200) + assert.logfile().has.no.line("[error]", true) + end) + it("denies access when there are race conditions and checks are enabled", function() + -- access from request 1 (don't clear) + local ok, r = pcall(client.get, client, "/", { + query = { + checks = checks, + }, + }) + assert(ok) + assert.response(r).has.status(200) + + -- access from request 2 + ok, r = pcall(client.get, client, "/", { + query = { + checks = checks, + }, + }) + if checks then + assert(not ok) + assert.logfile().has.line("race condition detected", true) + else + assert(ok) + assert.response(r).has.status(200) + end + end) + it("allows access when table is cleared between requests", function() + -- access from request 1 (clear) + local r = client:get("/", { + query = { + checks = checks, + clear = true, + }, + }) + assert.response(r).has.status(200) + + -- access from request 2 + r = client:get("/", { + query = { + checks = checks, + }, + }) + assert.response(r).has.status(200) + assert.logfile().has.no.line("[error]", true) + end) + end) + end) +end +end \ No newline at end of file diff --git a/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/handler.lua b/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/handler.lua new file mode 100644 index 00000000000..c51c6aa29b6 --- /dev/null +++ b/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/handler.lua @@ -0,0 +1,52 @@ +local RAT = require "kong.tools.request_aware_table" + +local get_phase = ngx.get_phase +local ngx = ngx +local kong = kong + + +local _M = { + PRIORITY = 1001, + VERSION = "1.0", +} + +local checks_tab = RAT.new({}, "on") +local no_checks_tab = RAT.new({}, "off") + +local function access_tables() + local query = kong.request.get_query() + local tab + + if query.checks ~= "false" then + tab = checks_tab + else + tab = no_checks_tab + end + + if query.clear == "true" and get_phase() == "access" then + tab.clear() + end + + -- write access + tab.foo = "bar" + -- read access + ngx.log(ngx.DEBUG, "accessing to tab.foo" .. tab.foo) + + if query.clear == "true" and get_phase() == "body_filter" then + tab.clear() + end +end + +function _M:access(conf) + access_tables() +end + +function _M:header_filter(conf) + access_tables() +end + +function _M:body_filter(conf) + access_tables() +end + +return _M diff --git a/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/schema.lua b/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/schema.lua new file mode 100644 index 00000000000..2881307755a --- /dev/null +++ b/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/schema.lua @@ -0,0 +1,11 @@ +return { + name = "request-aware-table", + fields = { + { + config = { + type = "record", + fields = { } + } + } + } +}