diff --git a/changelog/unreleased/kong/wasm-dynamic-properties.yml b/changelog/unreleased/kong/wasm-dynamic-properties.yml new file mode 100644 index 00000000000..4c8fb4d17b4 --- /dev/null +++ b/changelog/unreleased/kong/wasm-dynamic-properties.yml @@ -0,0 +1,5 @@ +message: > + Extend support for getting and setting Gateway values via proxy-wasm + properties in the `kong.*` namespace. +type: feature +scope: Core diff --git a/kong-3.6.0-0.rockspec b/kong-3.6.0-0.rockspec index b722cafb750..c49b7e137fb 100644 --- a/kong-3.6.0-0.rockspec +++ b/kong-3.6.0-0.rockspec @@ -198,6 +198,7 @@ build = { ["kong.runloop.plugin_servers.mp_rpc"] = "kong/runloop/plugin_servers/mp_rpc.lua", ["kong.runloop.plugin_servers.pb_rpc"] = "kong/runloop/plugin_servers/pb_rpc.lua", ["kong.runloop.wasm"] = "kong/runloop/wasm.lua", + ["kong.runloop.wasm.properties"] = "kong/runloop/wasm/properties.lua", ["kong.workspaces"] = "kong/workspaces/init.lua", diff --git a/kong/runloop/wasm.lua b/kong/runloop/wasm.lua index 3ae3f7e8c02..004c08ea565 100644 --- a/kong/runloop/wasm.lua +++ b/kong/runloop/wasm.lua @@ -40,6 +40,7 @@ local json_schema = require "kong.db.schema.json" local pl_file = require "pl.file" local pl_path = require "pl.path" local constants = require "kong.constants" +local properties = require "kong.runloop.wasm.properties" ---@module 'resty.wasmx.proxy_wasm' @@ -682,6 +683,155 @@ local function disable(reason) end +local function register_property_handlers() + properties.reset() + + properties.add_getter("kong.client.protocol", function(kong) + return true, kong.client.get_protocol(), true + end) + + properties.add_getter("kong.nginx.subsystem", function(kong) + return true, kong.nginx.get_subsystem(), true + end) + + properties.add_getter("kong.node.id", function(kong) + return true, kong.node.get_id(), true + end) + + properties.add_getter("kong.node.memory_stats", function(kong) + local stats = kong.node.get_memory_stats() + if not stats then + return false + end + return true, cjson_encode(stats), false + end) + + properties.add_getter("kong.request.forwarded_host", function(kong) + return true, kong.request.get_forwarded_host(), true + end) + + properties.add_getter("kong.request.forwarded_port", function(kong) + return true, kong.request.get_forwarded_port(), true + end) + + properties.add_getter("kong.request.forwarded_scheme", function(kong) + return true, kong.request.get_forwarded_scheme(), true + end) + + properties.add_getter("kong.request.port", function(kong) + return true, kong.request.get_port(), true + end) + + properties.add_getter("kong.response.source", function(kong) + return true, kong.request.get_source(), false + end) + + properties.add_setter("kong.response.status", function(kong, _, _, status) + return true, kong.response.set_status(tonumber(status)), false + end) + + properties.add_getter("kong.router.route", function(kong) + local route = kong.router.get_route() + if not route then + return true, nil, true + end + return true, cjson_encode(route), true + end) + + properties.add_getter("kong.router.service", function(kong) + local service = kong.router.get_service() + if not service then + return true, nil, true + end + return true, cjson_encode(service), true + end) + + properties.add_setter("kong.service.target", function(kong, _, _, target) + local host, port = target:match("^(.*):([0-9]+)$") + port = tonumber(port) + if not (host and port) then + return false + end + + kong.service.set_target(host, port) + return true, target, false + end) + + properties.add_setter("kong.service.upstream", function(kong, _, _, upstream) + local ok, err = kong.service.set_upstream(upstream) + if not ok then + kong.log.err(err) + return false + end + + return true, upstream, false + end) + + properties.add_setter("kong.service.request.scheme", function(kong, _, _, scheme) + kong.service.request.set_scheme(scheme) + return true, scheme, false + end) + + properties.add_getter("kong.route_id", function(_, _, ctx) + local value = ctx.route and ctx.route.id + local ok = value ~= nil + local const = ok + return ok, value, const + end) + + properties.add_getter("kong.service.response.status", function(kong) + return true, kong.service.response.get_status(), false + end) + + properties.add_getter("kong.service_id", function(_, _, ctx) + local value = ctx.service and ctx.service.id + local ok = value ~= nil + local const = ok + return ok, value, const + end) + + properties.add_getter("kong.version", function(kong) + return true, kong.version, true + end) + + properties.add_namespace_handlers("kong.ctx.shared", + function(kong, _, _, key) + local value = kong.ctx.shared[key] + local ok = value ~= nil + value = ok and tostring(value) or nil + return ok, value, false + end, + + function(kong, _, _, key, value) + kong.ctx.shared[key] = value + return true + end + ) + + properties.add_namespace_handlers("kong.configuration", + function(kong, _, _, key) + local value = kong.configuration[key] + if value ~= nil then + if type(value) == "table" then + value = cjson_decode(value) + else + value = tostring(value) + end + + return true, value, true + end + + return false + end, + + function() + -- kong.configuration is read-only: setter rejects all + return false + end + ) +end + + local function enable(kong_config) set_available_filters(kong_config.wasm_modules_parsed) @@ -690,6 +840,8 @@ local function enable(kong_config) proxy_wasm = proxy_wasm or require "resty.wasmx.proxy_wasm" + register_property_handlers() + ENABLED = true STATUS = STATUS_ENABLED end @@ -746,18 +898,6 @@ function _M.init_worker() end -local function set_proxy_wasm_property(property, value) - if not value then - return - end - - local ok, err = proxy_wasm.set_property(property, value) - if not ok then - log(ERR, "failed to set proxy-wasm '", property, "' property: ", err) - end -end - - --- -- Lookup and execute the filter chain that applies to the current request -- (if any). @@ -788,8 +928,12 @@ function _M.attach(ctx) return kong.response.error(500) end - set_proxy_wasm_property("kong.route_id", ctx.route and ctx.route.id) - set_proxy_wasm_property("kong.service_id", ctx.service and ctx.service.id) + ok, err = proxy_wasm.set_host_properties_handlers(properties.get, + properties.set) + if not ok then + log(CRIT, "failed setting host property handlers: ", err) + return kong.response.error(500) + end ok, err = proxy_wasm.start() if not ok then diff --git a/kong/runloop/wasm/properties.lua b/kong/runloop/wasm/properties.lua new file mode 100644 index 00000000000..14ef3feae80 --- /dev/null +++ b/kong/runloop/wasm/properties.lua @@ -0,0 +1,129 @@ +local _M = {} + +local clear_tab = require "table.clear" + +local kong = kong +local ngx = ngx + + +local simple_getters = {} +local simple_setters = {} +local namespace_handlers = {} + +local get_namespace, rebuild_namespaces +do + local patterns = {} + local handlers = {} + local namespaces_len = 0 + + function rebuild_namespaces() + clear_tab(patterns) + clear_tab(handlers) + + for ns, handler in pairs(namespace_handlers) do + table.insert(patterns, ns .. ".") + table.insert(handlers, handler) + end + + namespaces_len = #patterns + end + + local find = string.find + local sub = string.sub + + ---@param property string + ---@return table? namespace + ---@return string? key + function get_namespace(property) + for i = 1, namespaces_len do + local from, to = find(property, patterns[i], nil, true) + if from == 1 then + local key = sub(property, to + 1) + return handlers[i], key + end + end + end +end + + +function _M.reset() + clear_tab(simple_getters) + clear_tab(simple_setters) + clear_tab(namespace_handlers) + rebuild_namespaces() +end + + +function _M.add_getter(name, handler) + assert(type(name) == "string") + assert(type(handler) == "function") + + simple_getters[name] = handler +end + + +function _M.add_setter(name, handler) + assert(type(name) == "string") + assert(type(handler) == "function") + + simple_setters[name] = handler +end + + +function _M.add_namespace_handlers(name, get, set) + assert(type(name) == "string") + assert(type(get) == "function") + assert(type(set) == "function") + + namespace_handlers[name] = { get = get, set = set } + rebuild_namespaces() +end + + +---@param name string +---@return boolean? ok +---@return string? value_or_error +---@return boolean? is_const +function _M.get(name) + local ok, value, const = false, nil, nil + + local getter = simple_getters[name] + if getter then + ok, value, const = getter(kong, ngx, ngx.ctx) + + else + local ns, key = get_namespace(name) + + if ns then + ok, value, const = ns.get(kong, ngx, ngx.ctx, key) + end + end + + return ok, value, const +end + + +---@param name string +---@param value string|nil +---@return boolean? ok +---@return string? cached_value +---@return boolean? is_const +function _M.set(name, value) + local ok, cached_value, const = false, nil, nil + + local setter = simple_setters[name] + if setter then + ok, cached_value, const = setter(kong, ngx, ngx.ctx, value) + + else + local ns, key = get_namespace(name) + if ns then + ok, cached_value, const = ns.set(kong, ngx, ngx.ctx, key, value) + end + end + + return ok, cached_value, const +end + + +return _M diff --git a/spec/02-integration/20-wasm/04-proxy-wasm_spec.lua b/spec/02-integration/20-wasm/04-proxy-wasm_spec.lua index 86305377b68..96e610f78fe 100644 --- a/spec/02-integration/20-wasm/04-proxy-wasm_spec.lua +++ b/spec/02-integration/20-wasm/04-proxy-wasm_spec.lua @@ -8,6 +8,9 @@ local HEADER_NAME_INPUT = "X-PW-Input" local HEADER_NAME_DISPATCH_ECHO = "X-PW-Dispatch-Echo" local HEADER_NAME_ADD_REQ_HEADER = "X-PW-Add-Header" local HEADER_NAME_ADD_RESP_HEADER = "X-PW-Add-Resp-Header" +local HEADER_NAME_LUA_PROPERTY = "X-Lua-Property" +local HEADER_NAME_LUA_VALUE = "X-Lua-Value" +local UUID_PATTERN = "%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x" local DNS_HOSTNAME = "wasm.test" local MOCK_UPSTREAM_DNS_ADDR = DNS_HOSTNAME .. ":" .. helpers.mock_upstream_port @@ -36,6 +39,15 @@ describe("proxy-wasm filters (#wasm) (#" .. strategy .. ")", function() port = helpers.mock_upstream_port, }) + local mock_upstream = assert(bp.upstreams:insert { + name = "mock_upstream", + }) + + assert(bp.targets:insert { + upstream = { id = mock_upstream.id }, + target = helpers.mock_upstream_host .. ":" .. helpers.mock_upstream_port, + }) + r_single = assert(bp.routes:insert { paths = { "/single" }, strip_path = true, @@ -63,6 +75,58 @@ describe("proxy-wasm filters (#wasm) (#" .. strategy .. ")", function() }, }) + local r_lua = assert(bp.routes:insert { + paths = { "/lua" }, + strip_path = true, + service = mock_service, + }) + + assert(bp.filter_chains:insert { + route = r_lua, + filters = { + { name = "tests" }, + }, + }) + + assert(bp.plugins:insert { + name = "pre-function", + config = { + access = {([[ + local property = kong.request.get_header(%q) + + if property then + local value = kong.request.get_header(%q) + kong.log.notice("Setting kong.ctx.shared.", property, " to '", value, "'") + kong.ctx.shared[property] = value + end + ]]):format(HEADER_NAME_LUA_PROPERTY, HEADER_NAME_LUA_VALUE) + }, + }, + }) + + assert(bp.plugins:insert { + name = "post-function", + config = { + header_filter = {([[ + local property = kong.request.get_header(%q) + if property then + local value = kong.ctx.shared[property] + local header = %q + + if value then + kong.log.notice("Setting ", header, " response header to '", value, "'") + kong.response.set_header(header, value) + else + kong.log.notice("Clearing ", header, " response header") + kong.response.clear_header(header) + end + end + ]]):format(HEADER_NAME_LUA_PROPERTY, HEADER_NAME_LUA_VALUE) + }, + }, + }) + + -- XXX our dns mock fixture doesn't work when called from wasm land hosts_file = os.tmpname() assert(helpers.file.write(hosts_file, @@ -73,6 +137,7 @@ describe("proxy-wasm filters (#wasm) (#" .. strategy .. ")", function() nginx_conf = "spec/fixtures/custom_nginx.template", wasm = true, dns_hostsfile = hosts_file, + plugins = "pre-function,post-function", })) end) @@ -256,6 +321,337 @@ describe("proxy-wasm filters (#wasm) (#" .. strategy .. ")", function() assert.logfile().has.no.line("[crit]", true, 0) end) + it("read kong.client.protocol", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "client.protocol", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.equal("http", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.nginx.subsystem", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "nginx.subsystem", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.equal("http", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.node.id", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "node.id", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.matches(UUID_PATTERN, body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.node.memory_stats", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "node.memory_stats", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.matches("{.*lua_shared_dicts.*}", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.request.forwarded_host", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "request.forwarded_host", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.matches("^[a-z.0-9%-]+$", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.request.forwarded_port", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "request.forwarded_port", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.matches("^[0-9]+$", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.request.forwarded_scheme", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "request.forwarded_scheme", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.equal("http", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + pending("read kong.response.source", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_PHASE] = "log", + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "response.source", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.equal("service", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.router.route", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "router.route", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.equal(json.id, r_single.id) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.router.service", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "router.service", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.equal(json.id, mock_service.id) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("write kong.service.target", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local target = helpers.mock_upstream_host .. ":" .. + helpers.mock_upstream_port + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "set_kong_property", + [HEADER_NAME_INPUT] = "service.target=" .. target, + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + assert.res_status(200, res) + -- TODO read back property + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + -- observing weird behavior in this one: + -- target is being set to mock_upstream:15555 instead of + -- 127.0.0.1:1555 as expected... + pending("write kong.service.upstream", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_PHASE] = "request_headers", + [HEADER_NAME_TEST] = "set_kong_property", + [HEADER_NAME_INPUT] = "service.upstream=mock_upstream", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + assert.res_status(200, res) + -- TODO read back property + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("write kong.service.request.scheme", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "set_kong_property", + [HEADER_NAME_INPUT] = "service.request.scheme=http", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + assert.res_status(200, res) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + pending("read kong.service.response.status", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_PHASE] = "log", + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "service.response.status", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.equal("200", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("write kong.response.status", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_PHASE] = "response_headers", + [HEADER_NAME_TEST] = "set_kong_property", + [HEADER_NAME_INPUT] = "response.status=203", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + assert.res_status(203, res) + -- TODO read back property + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("read kong.configuration", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/single/status/200", + headers = { + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "configuration.role", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.equal("traditional", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + it("read kong.route_id", function() local client = helpers.proxy_client() finally(function() client:close() end) @@ -296,6 +692,72 @@ describe("proxy-wasm filters (#wasm) (#" .. strategy .. ")", function() assert.logfile().has.no.line("[crit]", true, 0) end) + it("read kong.ctx.shared[]", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/lua/status/200", + headers = { + [HEADER_NAME_LUA_PROPERTY] = "foo", + [HEADER_NAME_LUA_VALUE] = "bar", + [HEADER_NAME_TEST] = "get_kong_property", + [HEADER_NAME_INPUT] = "ctx.shared.foo", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + local body = assert.res_status(200, res) + assert.equal("bar", body) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("write kong.ctx.shared[]", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/lua/status/200", + headers = { + [HEADER_NAME_LUA_PROPERTY] = "foo", + [HEADER_NAME_TEST] = "set_kong_property", + [HEADER_NAME_INPUT] = "ctx.shared.foo=bar", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + assert.res_status(200, res) + local value = assert.response(res).has.header(HEADER_NAME_LUA_VALUE) + assert.same("bar", value) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + + it("clear kong.ctx.shared[]", function() + local client = helpers.proxy_client() + finally(function() client:close() end) + + local res = assert(client:send { + method = "GET", + path = "/lua/status/200", + headers = { + [HEADER_NAME_LUA_PROPERTY] = "foo", + [HEADER_NAME_LUA_VALUE] = "bar", + [HEADER_NAME_TEST] = "set_kong_property", + [HEADER_NAME_INPUT] = "ctx.shared.foo", + [HEADER_NAME_DISPATCH_ECHO] = "on", + } + }) + + assert.res_status(200, res) + assert.response(res).has.no.header(HEADER_NAME_LUA_VALUE) + assert.logfile().has.no.line("[error]", true, 0) + assert.logfile().has.no.line("[crit]", true, 0) + end) + it("send an http dispatch, return its response body", function() local client = helpers.proxy_client() finally(function() client:close() end) diff --git a/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs b/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs index 651ee154478..83da6555d6a 100644 --- a/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs +++ b/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs @@ -20,6 +20,11 @@ impl TestHttp { } } + fn set_prop(&self, ns: &str, prop: &str, value: Option<&str>) { + let value: Option<&[u8]> = value.map(|v| v.as_bytes()); + self.set_property(vec![ns, prop], value); + } + fn send_http_dispatch(&mut self, config: TestConfig) -> Action { let mut timeout = Duration::from_secs(0); let mut headers = Vec::new(); @@ -112,6 +117,17 @@ impl TestHttp { info!("[proxy-wasm] kong.{}: \"{:?}\"", name, value); self.send_plain_response(StatusCode::OK, Some(&value)) } + "set_kong_property" => { + if let Some(input) = opt_input { + let (key, value) = match input.split_once('=') { + Some((key, value)) => (key, Some(value)), + None => (input.as_ref(), None), + }; + + self.set_prop("kong", key, value); + info!("[proxy-wasm] kong.{} = \"{:?}\"", key, value); + } + } "echo_http_dispatch" => { let config = TestConfig::from_str(&opt_input.unwrap_or("".to_string())) .expect("invalid configuration");