Skip to content

Commit

Permalink
refactor(req-transformer) support for append, replace of header/query…
Browse files Browse the repository at this point in the history
…string

Fix for issue #393 for response-transformer motivated fix for this plugin too. New config append/replace added to support adding new value to existing header/querystring and
replcaing  existing header/querystring with new value.
  • Loading branch information
Shashi Ranjan committed Dec 23, 2015
1 parent 49afc66 commit a181a75
Show file tree
Hide file tree
Showing 3 changed files with 519 additions and 211 deletions.
313 changes: 195 additions & 118 deletions kong/plugins/request-transformer/access.lua
Original file line number Diff line number Diff line change
@@ -1,165 +1,242 @@
local utils = require "kong.tools.utils"
local stringy = require "stringy"
local Multipart = require "multipart"
local cjson = require "cjson"
local multipart = require "multipart"

local table_insert = table.insert
local req_set_uri_args = ngx.req.set_uri_args
local req_get_uri_args = ngx.req.get_uri_args
local req_set_header = ngx.req.set_header
local req_get_headers = ngx.req.get_headers
local req_read_body = ngx.req.read_body
local req_set_body_data = ngx.req.set_body_data
local req_get_body_data = ngx.req.get_body_data
local req_clear_header = ngx.req.clear_header
local req_get_post_args = ngx.req.get_post_args
local encode_args = ngx.encode_args
local type = type
local string_len = string.len

local unpack = unpack

local _M = {}

local APPLICATION_JSON = "application/json"
local CONTENT_LENGTH = "content-length"
local FORM_URLENCODED = "application/x-www-form-urlencoded"
local MULTIPART_DATA = "multipart/form-data"
local CONTENT_TYPE = "content-type"
local HOST = "host"

local function iterate_and_exec(val, cb)
if utils.table_size(val) > 0 then
for _, entry in ipairs(val) do
local parts = stringy.split(entry, ":")
cb(parts[1], utils.table_size(parts) == 2 and parts[2] or nil)

local function iter(config_array)
return function(config_array, i, previous_name, previous_value)
i = i + 1
local current_pair = config_array[i]
if current_pair == nil then -- n + 1
return nil
end
end
local current_name, current_value = unpack(stringy.split(current_pair, ":"))
return i, current_name, current_value
end, config_array, 0
end

local function get_content_type()
local header_value = ngx.req.get_headers()[CONTENT_TYPE]
local header_value = req_get_headers()[CONTENT_TYPE]
if header_value then
return stringy.strip(header_value):lower()
end
end

function _M.execute(conf)
if conf.add then

-- Add headers
if conf.add.headers then
iterate_and_exec(conf.add.headers, function(name, value)
ngx.req.set_header(name, value)
if name:lower() == HOST then -- Host header has a special treatment
ngx.var.upstream_host = value
end
end)
end
local function append_value(current_value, value)
local current_value_type = type(current_value)

-- Add Querystring
if conf.add.querystring then
local querystring = ngx.req.get_uri_args()
iterate_and_exec(conf.add.querystring, function(name, value)
querystring[name] = value
end)
ngx.req.set_uri_args(querystring)
end
if current_value_type == "string" then
return { current_value, value }
elseif current_value_type == "table" then
table_insert(current_value, value)
return current_value
else
return { value }
end
end

if conf.add.form then
local content_type = get_content_type()
if content_type and stringy.startswith(content_type, FORM_URLENCODED) then
-- Call ngx.req.read_body to read the request body first
ngx.req.read_body()
local function transform_headers(conf)
-- Remove header(s)
for _, name, value in iter(conf.remove.headers) do
req_clear_header(name)
end

local parameters = ngx.req.get_post_args()
iterate_and_exec(conf.add.form, function(name, value)
parameters[name] = value
end)
local encoded_args = ngx.encode_args(parameters)
ngx.req.set_header(CONTENT_LENGTH, string.len(encoded_args))
ngx.req.set_body_data(encoded_args)
elseif content_type and stringy.startswith(content_type, MULTIPART_DATA) then
-- Call ngx.req.read_body to read the request body first
ngx.req.read_body()

local body = ngx.req.get_body_data()
local parameters = Multipart(body and body or "", content_type)
iterate_and_exec(conf.add.form, function(name, value)
parameters:set_simple(name, value)
end)
local new_data = parameters:tostring()
ngx.req.set_header(CONTENT_LENGTH, string.len(new_data))
ngx.req.set_body_data(new_data)
-- Replace header(s)
for _, name, value in iter(conf.replace.headers) do
if req_get_headers()[name] then
req_set_header(name, value)
if name:lower() == HOST then -- Host header has a special treatment
ngx.var.backend_host = value
end
end
end

-- Add header(s)
for _, name, value in iter(conf.add.headers) do
if not req_get_headers()[name] then
req_set_header(name, value)
if name:lower() == HOST then -- Host header has a special treatment
ngx.var.backend_host = value
end
end
end

if conf.add.json then
local content_type = get_content_type()
if content_type and stringy.startswith(get_content_type(), APPLICATION_JSON) then
ngx.req.read_body()
local parameters = cjson.decode(ngx.req.get_body_data())

iterate_and_exec(conf.add.json, function(name, value)
local v = cjson.encode(value)
if stringy.startswith(v, "\"") and stringy.endswith(v, "\"") then
v = v:sub(2, v:len() - 1):gsub("\\\"", "\"") -- To prevent having double encoded quotes
end
parameters[name] = v
end)
-- Append header(s)
for _, name, value in iter(conf.append.headers) do
req_set_header(name, append_value(req_get_headers()[name], value))
if name:lower() == HOST then -- Host header has a special treatment
ngx.var.backend_host = value
end
end
end

local new_data = cjson.encode(parameters)
ngx.req.set_header(CONTENT_LENGTH, string.len(new_data))
ngx.req.set_body_data(new_data)
local function transform_querystrings(conf)
-- Remove querystring(s)
if conf.remove.querystring then
local querystring = req_get_uri_args()
for _, name, value in iter(conf.remove.querystring) do
querystring[name] = nil
end
req_set_uri_args(querystring)
end

if conf.remove then
-- Replace querystring(s)
if conf.replace.querystring then
local querystring = req_get_uri_args()
for _, name, value in iter(conf.replace.querystring) do
if querystring[name] then
querystring[name] = value
end
end
req_set_uri_args(querystring)
end

-- Remove headers
if conf.remove.headers then
iterate_and_exec(conf.remove.headers, function(name, value)
ngx.req.clear_header(name)
end)
-- Add querystring(s)
if conf.add.querystring then
local querystring = req_get_uri_args()
for _, name, value in iter(conf.add.querystring) do
if not querystring[name] then
querystring[name] = value
end
end
req_set_uri_args(querystring)
end

if conf.remove.querystring then
local querystring = ngx.req.get_uri_args()
iterate_and_exec(conf.remove.querystring, function(name)
querystring[name] = nil
end)
ngx.req.set_uri_args(querystring)
-- Append querystring(s)
if conf.append.querystring then
local querystring = req_get_uri_args()
for _, name, value in iter(conf.append.querystring) do
querystring[name] = append_value(querystring[name], value)
end
req_set_uri_args(querystring)
end
end

if conf.remove.form then
local content_type = get_content_type()
if content_type and stringy.startswith(content_type, FORM_URLENCODED) then
local parameters = ngx.req.get_post_args()

iterate_and_exec(conf.remove.form, function(name)
parameters[name] = nil
end)

local encoded_args = ngx.encode_args(parameters)
ngx.req.set_header(CONTENT_LENGTH, string.len(encoded_args))
ngx.req.set_body_data(encoded_args)
elseif content_type and stringy.startswith(content_type, MULTIPART_DATA) then
-- Call ngx.req.read_body to read the request body first
ngx.req.read_body()

local body = ngx.req.get_body_data()
local parameters = Multipart(body and body or "", content_type)
iterate_and_exec(conf.remove.form, function(name)
parameters:delete(name)
end)
local new_data = parameters:tostring()
ngx.req.set_header(CONTENT_LENGTH, string.len(new_data))
ngx.req.set_body_data(new_data)
local function transform_form_params(conf)
-- Remove form parameter(s)
if conf.remove.form then
local content_type = get_content_type()
if content_type and stringy.startswith(content_type, FORM_URLENCODED) then
req_read_body()
local parameters = req_get_post_args()

for _, name, value in iter(conf.remove.form) do
parameters[name] = nil
end

local encoded_args = encode_args(parameters)
req_set_header(CONTENT_LENGTH, string_len(encoded_args))
req_set_body_data(encoded_args)
elseif content_type and stringy.startswith(content_type, MULTIPART_DATA) then
-- Call req_read_body to read the request body first
req_read_body()

local body = req_get_body_data()
local parameters = multipart(body and body or "", content_type)
for _, name, value in iter(conf.remove.form) do
parameters:delete(name)
end
local new_data = parameters:tostring()
req_set_header(CONTENT_LENGTH, string_len(new_data))
req_set_body_data(new_data)
end
end

if conf.remove.json then
local content_type = get_content_type()
if content_type and stringy.startswith(get_content_type(), APPLICATION_JSON) then
ngx.req.read_body()
local parameters = cjson.decode(ngx.req.get_body_data())
-- Replace form parameter(s)
if conf.replace.form then
local content_type = get_content_type()
if content_type and stringy.startswith(content_type, FORM_URLENCODED) then
-- Call req_read_body to read the request body first
req_read_body()

iterate_and_exec(conf.remove.json, function(name)
parameters[name] = nil
end)
local parameters = req_get_post_args()
for _, name, value in iter(conf.replace.form) do
if parameters[name] then
parameters[name] = value
end
end
local encoded_args = encode_args(parameters)
req_set_header(CONTENT_LENGTH, string_len(encoded_args))
req_set_body_data(encoded_args)
elseif content_type and stringy.startswith(content_type, MULTIPART_DATA) then
-- Call req_read_body to read the request body first
req_read_body()

local body = req_get_body_data()
local parameters = multipart(body and body or "", content_type)
for _, name, value in iter(conf.replace.form) do
if parameters:get(name) then
parameters:delete(name)
parameters:set_simple(name, value)
end
end
local new_data = parameters:tostring()
req_set_header(CONTENT_LENGTH, string_len(new_data))
req_set_body_data(new_data)
end
end

local new_data = cjson.encode(parameters)
ngx.req.set_header(CONTENT_LENGTH, string.len(new_data))
ngx.req.set_body_data(new_data)
-- Add form parameter(s)
if conf.add.form then
local content_type = get_content_type()
if content_type and stringy.startswith(content_type, FORM_URLENCODED) then
-- Call req_read_body to read the request body first
req_read_body()

local parameters = req_get_post_args()
for _, name, value in iter(conf.add.form) do
if not parameters[name] then
parameters[name] = value
end
end
local encoded_args = encode_args(parameters)
req_set_header(CONTENT_LENGTH, string_len(encoded_args))
req_set_body_data(encoded_args)
elseif content_type and stringy.startswith(content_type, MULTIPART_DATA) then
-- Call req_read_body to read the request body first
req_read_body()

local body = req_get_body_data()
local parameters = multipart(body and body or "", content_type)
for _, name, value in iter(conf.add.form) do
if not parameters:get(name) then
parameters:set_simple(name, value)
end
end
local new_data = parameters:tostring()
req_set_header(CONTENT_LENGTH, string_len(new_data))
req_set_body_data(new_data)
end
end
end

function _M.execute(conf)
transform_form_params(conf)
transform_headers(conf)
transform_querystrings(conf)
end

return _M
Loading

0 comments on commit a181a75

Please sign in to comment.