diff --git a/kong-0.5.4-1.rockspec b/kong-0.5.4-1.rockspec index e1a4332dba1b..c15147539cf0 100644 --- a/kong-0.5.4-1.rockspec +++ b/kong-0.5.4-1.rockspec @@ -167,8 +167,8 @@ build = { ["kong.plugins.request-transformer.schema"] = "kong/plugins/request-transformer/schema.lua", ["kong.plugins.response-transformer.handler"] = "kong/plugins/response-transformer/handler.lua", - ["kong.plugins.response-transformer.body_filter"] = "kong/plugins/response-transformer/body_filter.lua", - ["kong.plugins.response-transformer.header_filter"] = "kong/plugins/response-transformer/header_filter.lua", + ["kong.plugins.response-transformer.body_transformer"] = "kong/plugins/response-transformer/body_transformer.lua", + ["kong.plugins.response-transformer.header_transformer"] = "kong/plugins/response-transformer/header_transformer.lua", ["kong.plugins.response-transformer.schema"] = "kong/plugins/response-transformer/schema.lua", ["kong.plugins.cors.handler"] = "kong/plugins/cors/handler.lua", diff --git a/kong/plugins/response-transformer/body_filter.lua b/kong/plugins/response-transformer/body_filter.lua deleted file mode 100644 index 8825a0775e7e..000000000000 --- a/kong/plugins/response-transformer/body_filter.lua +++ /dev/null @@ -1,92 +0,0 @@ -local utils = require "kong.tools.utils" -local stringy = require "stringy" -local cjson = require "cjson" - -local _M = {} - -local APPLICATION_JSON = "application/json" -local CONTENT_TYPE = "content-type" - -local function get_content_type() - local header_value = ngx.header[CONTENT_TYPE] - if header_value then - return stringy.strip(header_value):lower() - end - return nil -end - -local function read_response_body() - local chunk, eof = ngx.arg[1], ngx.arg[2] - local buffered = ngx.ctx.buffered - if not buffered then - buffered = {} - ngx.ctx.buffered = buffered - end - if chunk ~= "" then - buffered[#buffered + 1] = chunk - ngx.arg[1] = nil - end - if eof then - local response_body = table.concat(buffered) - return response_body - end - return nil -end - -local function read_json_body() - local body = read_response_body() - if body then - local status, res = pcall(cjson.decode, body) - if status then - return res - end - end - return nil -end - -local function set_json_body(json) - local body = cjson.encode(json) - ngx.arg[1] = body -end - -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) - end - end -end - -function _M.execute(conf) - if not conf then return end - - local is_json_body = stringy.startswith(get_content_type(), APPLICATION_JSON) - - if ((conf.add and conf.add.json) or (conf.remove and conf.remove.json)) and is_json_body then - local json_body = read_json_body() - if json_body then - - if conf.add and conf.add.json then - 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 - json_body[name] = v - end) - end - - if conf.remove and conf.remove.json then - iterate_and_exec(conf.remove.json, function(name) - json_body[name] = nil - end) - end - - set_json_body(json_body) - end - end - -end - -return _M diff --git a/kong/plugins/response-transformer/body_transformer.lua b/kong/plugins/response-transformer/body_transformer.lua new file mode 100644 index 000000000000..0f7a1ab680f6 --- /dev/null +++ b/kong/plugins/response-transformer/body_transformer.lua @@ -0,0 +1,93 @@ +local stringy = require "stringy" +local cjson = require "cjson" + +local table_insert = table.insert +local pcall = pcall +local string_find = string.find +local unpack = unpack +local type = type + +local _M = {} + +local function read_json_body(body) + if body then + local status, res = pcall(cjson.decode, body) + if status then + return res + end + end +end + +local function append_value(current_value, value) + local current_value_type = type(current_value) + + 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 + +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 + local current_name, current_value = unpack(stringy.split(current_pair, ":")) + return i, current_name, current_value + end, config_array, 0 +end + +function _M.is_json_body(content_type) + return content_type and string_find(content_type:lower(), "application/json", nil, true) +end + +function _M.transform_json_body(conf, buffered_data) + local json_body = read_json_body(buffered_data) + if json_body == nil then return end + + -- remove key:value to body + for _, name in iter(conf.remove.json) do + json_body[name] = nil + end + + -- replace key:value to body + for _, name, value in iter(conf.replace.json) do + 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 + if json_body[name] then + json_body[name] = v + end + end + + -- add new key:value to body + for _, name, value in iter(conf.add.json) do + 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 + if not json_body[name] then + json_body[name] = v + end + end + + -- append new key:value or value to existing key + for _, name, value in iter(conf.append.json) do + 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 + json_body[name] = append_value(json_body[name],v) + end + + return cjson.encode(json_body) +end + +return _M diff --git a/kong/plugins/response-transformer/handler.lua b/kong/plugins/response-transformer/handler.lua index b4d442465f6d..f6f176ef0743 100644 --- a/kong/plugins/response-transformer/handler.lua +++ b/kong/plugins/response-transformer/handler.lua @@ -1,6 +1,6 @@ local BasePlugin = require "kong.plugins.base_plugin" -local body_filter = require "kong.plugins.response-transformer.body_filter" -local header_filter = require "kong.plugins.response-transformer.header_filter" +local body_filter = require "kong.plugins.response-transformer.body_transformer" +local header_filter = require "kong.plugins.response-transformer.header_transformer" local ResponseTransformerHandler = BasePlugin:extend() @@ -8,14 +8,28 @@ function ResponseTransformerHandler:new() ResponseTransformerHandler.super.new(self, "response-transformer") end +function ResponseTransformerHandler:access(conf) + ResponseTransformerHandler.super.access(self) + ngx.ctx.buffer = "" +end + function ResponseTransformerHandler:header_filter(conf) ResponseTransformerHandler.super.header_filter(self) - header_filter.execute(conf) + header_filter.transform_headers(conf, ngx.header) end function ResponseTransformerHandler:body_filter(conf) ResponseTransformerHandler.super.body_filter(self) - body_filter.execute(conf) + if body_filter.is_json_body(ngx.header["content-type"]) then + local chunk, eof = ngx.arg[1], ngx.arg[2] + if eof then + local body = body_filter.transform_json_body(conf, ngx.ctx.buffer) + ngx.arg[1] = body + else + ngx.ctx.buffer = ngx.ctx.buffer..chunk + ngx.arg[1] = nil + end + end end ResponseTransformerHandler.PRIORITY = 800 diff --git a/kong/plugins/response-transformer/header_filter.lua b/kong/plugins/response-transformer/header_filter.lua deleted file mode 100644 index 217b9c4b32aa..000000000000 --- a/kong/plugins/response-transformer/header_filter.lua +++ /dev/null @@ -1,64 +0,0 @@ -local utils = require "kong.tools.utils" -local stringy = require "stringy" - -local _M = {} - -local APPLICATION_JSON = "application/json" -local CONTENT_TYPE = "content-type" -local CONTENT_LENGTH = "content-length" - -local function get_content_type() - local header_value = ngx.header[CONTENT_TYPE] - if header_value then - return stringy.strip(header_value):lower() - end - return nil -end - -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) - end - end -end - -function _M.execute(conf) - local is_json_body = stringy.startswith(get_content_type(), APPLICATION_JSON) - - if conf.add then - - -- Add headers - if conf.add.headers then - iterate_and_exec(conf.add.headers, function(name, value) - ngx.header[name] = value - end) - end - - -- Removing the header because the body is going to change - if conf.add.json and is_json_body then - ngx.header[CONTENT_LENGTH] = nil - end - - end - - if conf.remove then - - -- Remove headers - if conf.remove.headers then - iterate_and_exec(conf.remove.headers, function(name, value) - ngx.header[name] = nil - end) - end - - -- Removing the header because the body is going to change - if conf.remove.json and is_json_body then - ngx.header[CONTENT_LENGTH] = nil - end - - end - -end - -return _M diff --git a/kong/plugins/response-transformer/header_transformer.lua b/kong/plugins/response-transformer/header_transformer.lua new file mode 100644 index 000000000000..a823d698bbd4 --- /dev/null +++ b/kong/plugins/response-transformer/header_transformer.lua @@ -0,0 +1,81 @@ +local stringy = require "stringy" + +local table_insert = table.insert +local unpack = unpack +local type = type +local string_find = string.find + +local _M = {} + +local function iter(config_array) + return function(config_array, i, previous_header_name, previous_header_value) + i = i + 1 + local header_to_test = config_array[i] + if header_to_test == nil then -- n + 1 + return nil + end + local header_to_test_name, header_to_test_value = unpack(stringy.split(header_to_test, ":")) + return i, header_to_test_name, header_to_test_value + end, config_array, 0 +end + +local function append_value(current_value, value) + local current_value_type = type(current_value) + + 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 + +local function is_json_body(content_type) + return content_type and string_find(content_type:lower(), "application/json", nil, true) +end + +local function is_body_transform_set(conf) + return #conf.add.json > 0 or #conf.remove.json > 0 or #conf.replace.json > 0 or #conf.append.json > 0 +end + +--- +-- # Example: +-- ngx.headers = header_filter.transform_headers(conf, ngx.headers) +-- We run transformations in following order: remove, replace, add, append. +-- @param[type=table] conf Plugin configuration. +-- @param[type=table] ngx_headers Table of headers, that should be `ngx.headers` +-- @return table A table containing the new headers. +function _M.transform_headers(conf, ngx_headers) + -- remove headers + for _, header_name, header_value in iter(conf.remove.headers) do + ngx_headers[header_name] = nil + end + + -- replace headers + for _, header_name, header_value in iter(conf.replace.headers) do + if ngx_headers[header_name] ~= nil then + ngx_headers[header_name] = header_value + end + end + + -- add headers + for _, header_name, header_value in iter(conf.add.headers) do + if ngx_headers[header_name] == nil then + ngx_headers[header_name] = header_value + end + end + + -- append headers + for _, header_name, header_value in iter(conf.append.headers) do + ngx_headers[header_name] = append_value(ngx_headers[header_name], header_value) + end + + -- Removing the content-length header because the body is going to change + if is_body_transform_set(conf) and is_json_body(ngx_headers["content-type"]) then + ngx_headers["content-length"] = nil + end +end + +return _M diff --git a/kong/plugins/response-transformer/schema.lua b/kong/plugins/response-transformer/schema.lua index db024d57f48f..e2d9522b719d 100644 --- a/kong/plugins/response-transformer/schema.lua +++ b/kong/plugins/response-transformer/schema.lua @@ -1,16 +1,39 @@ return { fields = { - add = { type = "table", schema = { + -- add: Add a value (to response headers or response JSON body) only if the key does not already exist. + remove = { + type = "table", + schema = { fields = { - json = { type = "array" }, - headers = { type = "array" } + json = {type = "array", default = {}}, + headers = {type = "array", default = {}} } } }, - remove = { type = "table", schema = { + replace = { + type = "table", + schema = { fields = { - json = { type = "array" }, - headers = { type = "array" } + json = {type = "array", default = {}}, + headers = {type = "array", default = {}} + } + } + }, + add = { + type = "table", + schema = { + fields = { + json = {type = "array", default = {}}, + headers = {type = "array", default = {}} + } + } + }, + append = { + type = "table", + schema = { + fields = { + json = {type = "array", default = {}}, + headers = {type = "array", default = {}} } } } diff --git a/spec/plugins/response-transformer/big_resonse_body_spec.lua b/spec/plugins/response-transformer/big_resonse_body_spec.lua new file mode 100644 index 000000000000..20d2230cecd9 --- /dev/null +++ b/spec/plugins/response-transformer/big_resonse_body_spec.lua @@ -0,0 +1,59 @@ +local spec_helper = require "spec.spec_helpers" +local http_client = require "kong.tools.http_client" +local cjson = require "cjson" + +local STUB_POST_URL = spec_helper.PROXY_URL.."/post" + +local function create_big_data(size) + return string.format([[ + {"mock_json":{"big_field":"%s"}} + ]], string.rep("*", size)) +end + +describe("Response Transformer Plugin #proxy", function() + setup(function() + spec_helper.prepare_db() + spec_helper.insert_fixtures { + api = { + {name = "tests-response-transformer", request_host = "response.com", upstream_url = "http://httpbin.org"} + }, + plugin = { + { + name = "response-transformer", + config = { + add = { + json = {"p1:v1"} + }, + remove = { + json = {"json"} + } + }, + __api = 1 + } + } + } + spec_helper.start_kong() + end) + + teardown(function() + spec_helper.stop_kong() + end) + + describe("Test add", function() + it("should add new parameters on GET", function() + local response, status = http_client.post(STUB_POST_URL, {create_big_data(1 * 1024 * 1024)}, {host = "response.com", ["content-type"] = "application/json"}) + assert.equal(200, status) + local body = cjson.decode(response) + assert.equal("v1", body["p1"]) + end) + end) + + describe("Test remove", function() + it("should remove parameters on GET", function() + local response, status = http_client.post(STUB_POST_URL, {create_big_data(1 * 1024 * 1024)}, {host = "response.com", ["content-type"] = "application/json"}) + assert.equal(200, status) + local body = cjson.decode(response) + assert.falsy(body["json"]) + end) + end) +end) diff --git a/spec/plugins/response-transformer/body_transformer_spec.lua b/spec/plugins/response-transformer/body_transformer_spec.lua new file mode 100644 index 000000000000..c2681cdc3fb8 --- /dev/null +++ b/spec/plugins/response-transformer/body_transformer_spec.lua @@ -0,0 +1,163 @@ +local body_transformer = require "kong.plugins.response-transformer.body_transformer" +local cjson = require "cjson" + +describe("response-transformer body_transformer", function() + describe("transform_json_body()", function() + describe("add", function() + local conf = { + remove = { + json = {} + }, + replace = { + json = {} + }, + add = { + json = {"p1:v1", "p3:v3", "p4:\"v1\""} + }, + append = { + json = {} + }, + } + it("should add parameter", function() + local json = [[{"p2":"v1"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({p1 = "v1", p2 = "v1", p3 = "v3", p4 = '"v1"'}, body_json) + end) + it("should add value in double quotes", function() + local json = [[{"p2":"v1"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({p1 = "v1", p2 = "v1", p3 = "v3", p4 = '"v1"'}, body_json) + end) + end) + + describe("append", function() + local conf = { + remove = { + json = {} + }, + replace = { + json = {} + }, + add = { + json = {} + }, + append = { + json = {"p1:v1", "p3:\"v1\""} + }, + } + it("should add new key:value if key does not exists", function() + local json = [[{"p2":"v1"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({ p2 = "v1", p1 = {"v1"}, p3 = {'"v1"'}}, body_json) + end) + it("should append value if key exists", function() + local json = [[{"p1":"v2"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({ p1 = {"v2","v1"}, p3 = {'"v1"'}}, body_json) + end) + it("should append value in double quotes", function() + local json = [[{"p3":"v2"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({p1 = {"v1"}, p3 = {"v2",'"v1"'}}, body_json) + end) + end) + + describe("remove", function() + local conf = { + remove = { + json = {"p1", "p2"} + }, + replace = { + json = {} + }, + add = { + json = {} + }, + append = { + json = {} + } + } + it("should remove parameter", function() + local json = [[{"p1" : "v1", "p2" : "v1"}]] + local body = body_transformer.transform_json_body(conf, json) + assert.equal("{}", body) + end) + end) + + describe("replace", function() + local conf = { + remove = { + json = {} + }, + replace = { + json = {"p1:v2", "p2:\"v2\""} + }, + add = { + json = {} + }, + append = { + json = {} + } + } + it("should replace parameter if it exists", function() + local json = [[{"p1" : "v1", "p2" : "v1"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({p1 = "v2", p2 = '"v2"'}, body_json) + end) + it("should not add value to parameter if parameter does not exists", function() + local json = [[{"p1" : "v1"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({p1 = "v2"}, body_json) + end) + it("should replce to double quoted value", function() + local json = [[{"p2" : "v1"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({p2 = '"v2"'}, body_json) + end) + end) + + describe("remove, replace, add, append", function() + local conf = { + remove = { + json = {"p1"} + }, + replace = { + json = {"p2:v2"} + }, + add = { + json = {"p3:v1"} + }, + append = { + json = {"p3:v2"} + }, + } + it("should remove `p1` and add `p2'", function() + local json = [[{"p1" : "v1", "p2" : "v1"}]] + local body = body_transformer.transform_json_body(conf, json) + local body_json = cjson.decode(body) + assert.same({p2 = "v2", p3 = {"v1", "v2"}}, body_json) + end) + end) + end) + + describe("is_json_body()", function() + it("should be true when content-type application/json passed", function() + assert.truthy(body_transformer.is_json_body("application/json")) + assert.truthy(body_transformer.is_json_body("application/json; charset=utf-8")) + end) + it("should be true when content-type is multiple values along with application/json passed", function() + assert.truthy(body_transformer.is_json_body("application/x-www-form-urlencoded, application/json")) + end) + it("should be fail when content-type not application/json", function() + assert.falsy(body_transformer.is_json_body("application/x-www-form-urlencoded")) + end) + end) +end) \ No newline at end of file diff --git a/spec/plugins/response-transformer/filter_spec.lua b/spec/plugins/response-transformer/filter_spec.lua index 189a3d6c3f9f..b9698219de79 100644 --- a/spec/plugins/response-transformer/filter_spec.lua +++ b/spec/plugins/response-transformer/filter_spec.lua @@ -3,41 +3,25 @@ local http_client = require "kong.tools.http_client" local cjson = require "cjson" local STUB_GET_URL = spec_helper.PROXY_URL.."/get" -local STUB_POST_URL = spec_helper.PROXY_URL.."/post" local STUB_HEADERS_URL = spec_helper.PROXY_URL.."/response-headers" describe("Response Transformer Plugin #proxy", function() - setup(function() spec_helper.prepare_db() spec_helper.insert_fixtures { api = { - {name = "tests-response-transformer", request_host = "response.com", upstream_url = "http://httpbin.org"}, - {name = "tests-response-transformer2", request_host = "response2.com", upstream_url = "http://httpbin.org"} + {name = "tests-response-transformer", request_host = "response.com", upstream_url = "http://httpbin.org"} }, plugin = { { name = "response-transformer", config = { - add = { - headers = {"x-added:true", "x-added2:true"}, - json = {"newjsonparam:newvalue"} - }, remove = { - headers = {"x-to-remove"}, - json = {"origin"} + headers = {"Access-Control-Allow-Origin"}, + json = {"url"} } }, __api = 1 - }, - { - name = "response-transformer", - config = { - add = { - headers = {"Cache-Control:max-age=86400"} - } - }, - __api = 2 } } } @@ -49,52 +33,20 @@ describe("Response Transformer Plugin #proxy", function() spec_helper.stop_kong() end) - describe("Test adding parameters", function() - - it("should add new headers", function() - local _, status, headers = http_client.get(STUB_GET_URL, {}, {host = "response.com"}) - assert.are.equal(200, status) - assert.are.equal("true", headers["x-added"]) - assert.are.equal("true", headers["x-added2"]) - end) - - it("should add new parameters on GET", function() + describe("Test transforming parameters", function() + it("should remove a parameter", function() local response, status = http_client.get(STUB_GET_URL, {}, {host = "response.com"}) - assert.are.equal(200, status) - local body = cjson.decode(response) - assert.are.equal("newvalue", body["newjsonparam"]) - end) - - it("should add new parameters on POST", function() - local response, status = http_client.post(STUB_POST_URL, {}, {host = "response.com"}) - assert.are.equal(200, status) + assert.equal(200, status) local body = cjson.decode(response) - assert.are.equal("newvalue", body["newjsonparam"]) + assert.falsy(body.url) end) - - it("should add new headers", function() - local _, status, headers = http_client.get(STUB_GET_URL, {}, {host = "response2.com"}) - assert.are.equal(200, status) - assert.are.equal("max-age=86400", headers["cache-control"]) - end) - end) - - describe("Test removing parameters", function() - + + describe("Test transforming headers", function() it("should remove a header", function() - local _, status, headers = http_client.get(STUB_HEADERS_URL, {["x-to-remove"] = "true"}, {host = "response.com"}) - assert.are.equal(200, status) - assert.falsy(headers["x-to-remove"]) + local _, status, headers = http_client.get(STUB_HEADERS_URL, {}, {host = "response.com"}) + assert.equal(200, status) + assert.falsy(headers["access-control-allow-origin"]) end) - - it("should remove a parameter on GET", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "response.com"}) - assert.are.equal(200, status) - local body = cjson.decode(response) - assert.falsy(body.origin) - end) - end) - end) diff --git a/spec/plugins/response-transformer/header_transformer_spec.lua b/spec/plugins/response-transformer/header_transformer_spec.lua new file mode 100644 index 000000000000..595df2fede18 --- /dev/null +++ b/spec/plugins/response-transformer/header_transformer_spec.lua @@ -0,0 +1,323 @@ +local header_transformer = require "kong.plugins.response-transformer.header_transformer" +local CONTENT_LENGTH = "content-length" + +describe("response-transformer header_transformer", function() + describe("execute_headers()", function() + describe("remove", function() + local conf = { + remove = { + headers = {"h1", "h2", "h3"} + }, + replace = { + headers = {} + }, + add = { + json = {"p1:v1"}, + headers = {} + }, + append = { + headers = {} + } + } + it("should remove all the headers", function() + local ngx_headers = {h1 = "value1", h2 = {"value2a", "value2b"}} + header_transformer.transform_headers(conf, ngx_headers) + assert.same({}, ngx_headers) + end) + it("should set content-length nil", function() + local ngx_headers = {h1 = "value1", h2 = {"value2a", "value2b"}, [CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("replace", function() + local conf = { + remove = { + headers = {} + }, + replace = { + headers = {"h1:v1", "h2:v2"} + }, + add = { + json = {"p1:v1"}, + headers = {} + }, + append = { + headers = {} + } + } + it("should replace a header if the header only exists", function() + local req_ngx_headers = {h1 = "value1", h2 = {"value2a", "value2b"}} + header_transformer.transform_headers(conf, req_ngx_headers) + assert.same({h1 = "v1", h2 = "v2"}, req_ngx_headers) + end) + it("should not add a new header if the header does not already exist", function() + local req_ngx_headers = {h2 = {"value2a", "value2b"}} + header_transformer.transform_headers(conf, req_ngx_headers) + assert.same({h2 = "v2"}, req_ngx_headers) + end) + it("should set content-length nil", function() + local ngx_headers = {h1 = "value1", h2 = {"value2a", "value2b"}, [CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("add", function() + local conf = { + remove = { + headers = {} + }, + replace = { + headers = {} + }, + add = { + json = {"p1:v1"}, + headers = {"h2:v2"} + }, + append = { + headers = {} + } + } + it("should add a header if the header does not exists", function() + local req_ngx_headers = {h1 = "v1"} + header_transformer.transform_headers(conf, req_ngx_headers) + assert.same({h1 = "v1", h2 = "v2"}, req_ngx_headers) + end) + it("should not add a new header if the header already exist", function() + local req_ngx_headers = {h1 = "v1", h2 = "v3"} + header_transformer.transform_headers(conf, req_ngx_headers) + assert.same({h1 = "v1", h2 = "v3"}, req_ngx_headers) + end) + it("should set content-length nil", function() + local ngx_headers = {h1 = "v1", [CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("append", function() + local conf = { + remove = { + headers = {} + }, + replace = { + headers = {} + }, + add = { + json = {"p1:v1"}, + headers = {} + }, + append = { + headers = {"h1:v2"} + } + } + it("should add a header if the header does not exists", function() + local req_ngx_headers = {} + header_transformer.transform_headers(conf, req_ngx_headers) + assert.same({"v2"}, req_ngx_headers["h1"]) + end) + it("should append header if the header already exist", function() + local req_ngx_headers = {h1 = "v1"} + header_transformer.transform_headers(conf, req_ngx_headers) + assert.same({h1 = {"v1", "v2"}}, req_ngx_headers) + end) + it("should set content-length nil", function() + local ngx_headers = {h1 = "v1", [CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("performing remove, replace, add, append togeather", function() + local conf = { + remove = { + headers = {"h1:v1"} + }, + replace = { + headers = {"h2:v3"} + }, + add = { + json = {"p1:v1"}, + headers = {"h3:v3"} + }, + append = { + headers = {"h3:v4"} + } + } + it("should transform all headers", function() + local req_ngx_headers = {h1 = "v1", h2 = "v2"} + header_transformer.transform_headers(conf, req_ngx_headers) + assert.same({h2 = "v3", h3 = {"v3", "v4"}}, req_ngx_headers) + end) + it("should set content-length nil", function() + local ngx_headers = {h1 = "v1", [CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("remove content-type", function() + describe("remove json", function() + local conf = { + remove = { + json = {"p1"}, + headers = {"h1", "h2"} + }, + replace = { + json = {}, + headers = {} + }, + add = { + json = {}, + headers = {} + }, + append = { + json = {}, + headers = {} + } + } + it("should set content-length nil if application/json passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should set content-length nil if application/json and charset passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if content-type not json", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/x-www-form-urlencoded; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if any of json not set", function() + conf.remove.json = {} + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("replace json", function() + local conf = { + remove = { + json = {}, + headers = {} + }, + replace = { + json = {"p1:v1", "p2:v1"}, + headers = {"h1:v1", "h2:v2"} + }, + add = { + json = {}, + headers = {} + }, + append = { + json = {}, + headers = {} + } + } + it("should set content-length nil if application/json passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should set content-length nil if application/json and charset passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if content-type not json", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/x-www-form-urlencoded; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if any of json not set", function() + conf.replace.json = {} + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("replace json", function() + local conf = { + remove = { + json = {}, + headers = {} + }, + replace = { + json = {}, + headers = {} + }, + add = { + json = {"p1:v1", "p2:v1"}, + headers = {"h1:v1", "h2:v2"} + }, + append = { + json = {}, + headers = {} + } + } + it("should set content-length nil if application/json passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should set content-length nil if application/json and charset passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if content-type not json", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/x-www-form-urlencoded; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if any of json not set", function() + conf.add.json = {} + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + end) + describe("replace json", function() + local conf = { + remove = { + json = {}, + headers = {} + }, + replace = { + json = {}, + headers = {} + }, + add = { + json = {}, + headers = {} + }, + append = { + json = {"p1:v1", "p2:v1"}, + headers = {"h1:v1", "h2:v2"} + } + } + it("should set content-length nil if application/json passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should set content-length nil if application/json and charset passed", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.falsy(ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if content-type not json", function() + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/x-www-form-urlencoded; charset=utf-8"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + it("should not set content-length nil if any of json not set", function() + conf.append.json = {} + local ngx_headers = {[CONTENT_LENGTH] = "100", ["content-type"] = "application/json"} + header_transformer.transform_headers(conf, ngx_headers) + assert.equal('100', ngx_headers[CONTENT_LENGTH]) + end) + end) + end) + end) +end) \ No newline at end of file diff --git a/spec/unit/dao/entities_schemas_spec.lua b/spec/unit/dao/entities_schemas_spec.lua index 7ef8e240106b..20ed57ec9b3c 100644 --- a/spec/unit/dao/entities_schemas_spec.lua +++ b/spec/unit/dao/entities_schemas_spec.lua @@ -302,11 +302,13 @@ describe("Entities Schemas", function() local valid = validate_entity(plugin, plugins_schema, {dao = dao_stub}) assert.same({key_names = {"apikey"}, hide_credentials = false}, plugin.config) assert.True(valid) - - -- Insert reauest-transformer, whose default config has no default values, and should be empty + end) + + it("should be valid if no value is specified for subfield and if the config schema has default as empty array", function() + -- Insert response-transformer, whose default config has no default values, and should be empty local plugin2 = {name = "response-transformer", api_id = "stub"} - valid = validate_entity(plugin2, plugins_schema, {dao = dao_stub}) - assert.same({}, plugin2.config) + local valid = validate_entity(plugin2, plugins_schema, {dao = dao_stub}) + assert.same({remove = {headers = {}}, replace = {headers = {}}, add = {headers = {}}, append = {headers = {}}}, plugin2.config) assert.True(valid) end)