Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugins): Request-Transformer rename behavior #13358

Merged
merged 1 commit into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/req-trans-rename.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**Request-Transformer**: Fixed an issue where renamed query parameters, url-encoded body parameters, and json body parameters were not handled properly when target name is the same as the source name in the request."
type: feature
scope: Plugin
48 changes: 30 additions & 18 deletions kong/plugins/request-transformer/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,19 @@ local function append_value(current_value, value)
end
end

local function rename(tbl, old_name, new_name)
if old_name == new_name then
return
end

local value = tbl[old_name]
if value then
tbl[old_name] = nil
tbl[new_name] = value
return true
end
end

local function transform_headers(conf, template_env)
local headers = get_headers()
local headers_to_remove = {}
Expand All @@ -164,11 +177,17 @@ local function transform_headers(conf, template_env)

-- Rename headers(s)
for _, old_name, new_name in iter(conf.rename.headers, template_env) do
old_name = old_name:lower()
local value = headers[old_name]
if value then
headers[new_name:lower()] = value
headers[old_name] = nil
local lower_old_name, lower_new_name = old_name:lower(), new_name:lower()
-- headers by default are case-insensitive
-- but if we have a case change, we need to handle it as a special case
local need_remove
if lower_old_name == lower_new_name then
need_remove = rename(headers, old_name, new_name)
else
need_remove = rename(headers, lower_old_name, lower_new_name)
end

if need_remove then
headers_to_remove[old_name] = true
end
end
Expand Down Expand Up @@ -229,9 +248,7 @@ local function transform_querystrings(conf, template_env)

-- Rename querystring(s)
for _, old_name, new_name in iter(conf.rename.querystring, template_env) do
local value = querystring[old_name]
querystring[new_name] = value
querystring[old_name] = nil
rename(querystring, old_name, new_name)
end

for _, name, value in iter(conf.replace.querystring, template_env) do
Expand Down Expand Up @@ -274,10 +291,7 @@ local function transform_json_body(conf, body, content_length, template_env)

if content_length > 0 and #conf.rename.body > 0 then
for _, old_name, new_name in iter(conf.rename.body, template_env) do
local value = parameters[old_name]
parameters[new_name] = value
parameters[old_name] = nil
renamed = true
renamed = rename(parameters, old_name, new_name) or renamed
end
end

Expand Down Expand Up @@ -325,10 +339,7 @@ local function transform_url_encoded_body(conf, body, content_length, template_e

if content_length > 0 and #conf.rename.body > 0 then
for _, old_name, new_name in iter(conf.rename.body, template_env) do
local value = parameters[old_name]
parameters[new_name] = value
parameters[old_name] = nil
renamed = true
renamed = rename(parameters, old_name, new_name) or renamed
end
end

Expand Down Expand Up @@ -369,8 +380,9 @@ local function transform_multipart_body(conf, body, content_length, content_type

if content_length > 0 and #conf.rename.body > 0 then
for _, old_name, new_name in iter(conf.rename.body, template_env) do
if parameters:get(old_name) then
local value = parameters:get(old_name).value
local para = parameters:get(old_name)
if para and old_name ~= new_name then
local value = para.value
parameters:set_simple(new_name, value)
parameters:delete(old_name)
renamed = true
Expand Down
73 changes: 72 additions & 1 deletion spec/03-plugins/36-request-transformer/02-access_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ local admin_api = require "spec.fixtures.admin_api"
local helpers = require "spec.helpers"
local cjson = require "cjson"
local pl_file = require "pl.file"
local http_mock = require "spec.helpers.http_mock"

local MOCK_PORT = helpers.get_available_port()

local fmt = string.format

Expand All @@ -16,7 +19,7 @@ end

for _, strategy in helpers.each_strategy() do
describe("Plugin: request-transformer(access) [#" .. strategy .. "]", function()
local client
local client, mock_server

lazy_setup(function()
local bp = helpers.get_db_utils(strategy, {
Expand Down Expand Up @@ -487,6 +490,30 @@ describe("Plugin: request-transformer(access) [#" .. strategy .. "]", function()
}
}

do -- rename case tests
-- as assert.request(r) does not support case-sensitive header checks
-- we need to use a mock server
mock_server = http_mock.new(MOCK_PORT)
mock_server:start()
local mock_service = bp.services:insert {
url = "http://localhost:" .. MOCK_PORT
}
local route = bp.routes:insert {
hosts = { "rename.mock" },
service = mock_service,
}
bp.plugins:insert {
route = { id = route.id },
name = "request-transformer",
config = {
rename = {
headers = { "rename:Rename", "identical:identical" },
querystring = { "inexist:exist" },
}
}
}
end

assert(helpers.start_kong({
database = strategy,
plugins = "bundled, request-transformer",
Expand All @@ -495,6 +522,7 @@ describe("Plugin: request-transformer(access) [#" .. strategy .. "]", function()
end)

lazy_teardown(function()
mock_server:stop()
helpers.stop_kong()
end)

Expand Down Expand Up @@ -909,6 +937,49 @@ describe("Plugin: request-transformer(access) [#" .. strategy .. "]", function()
local json = assert.request(r).has.jsonbody()
assert.equals("{\"emptyarray\":[]}", json.data)
end)
it("rename correctly when only changing capitalization", function()
local r = assert(client:send {
method = "GET",
path = "/request",
headers = {
host = "rename.mock",
["rename"] = "true",
}
})
assert.response(r).has.status(200)
local ret = mock_server:get_request()

assert.equals("true", ret.headers["Rename"])
assert.is_nil(ret.headers["rename"])
end)
-- but should we override with a value?
it("does not override existing value with nil", function()
local r = assert(client:send {
method = "GET",
path = "/request?exist=true",
headers = {
host = "rename.mock",
}
})
assert.response(r).has.status(200)
local ret = mock_server:get_request()

assert.equals("/request?exist=true", ret.uri)
end)
it("does not remove when renaming to the identical name", function()
local r = assert(client:send {
method = "GET",
path = "/request",
headers = {
host = "rename.mock",
["identical"] = "true",
}
})
assert.response(r).has.status(200)
local ret = mock_server:get_request()

assert.equals("true", ret.headers["identical"])
end)
end)

describe("replace", function()
Expand Down
Loading