-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #824 from Mashape/fix/request-transformer
refactor(req-transformer) append, replace of header/querystring
- Loading branch information
Showing
3 changed files
with
519 additions
and
211 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.