diff --git a/kong-1.4.0-0.rockspec b/kong-1.4.0-0.rockspec
index f8c5b2fe85e6..da399527b963 100644
--- a/kong-1.4.0-0.rockspec
+++ b/kong-1.4.0-0.rockspec
@@ -45,6 +45,7 @@ dependencies = {
"kong-proxy-cache-plugin ~> 1.2",
"kong-plugin-request-transformer ~> 1.2",
"kong-plugin-session ~> 2.1",
+ "kong-plugin-aws-lambda ~> 3.0",
}
build = {
type = "builtin",
@@ -344,10 +345,6 @@ build = {
["kong.plugins.bot-detection.schema"] = "kong/plugins/bot-detection/schema.lua",
["kong.plugins.bot-detection.rules"] = "kong/plugins/bot-detection/rules.lua",
- ["kong.plugins.aws-lambda.handler"] = "kong/plugins/aws-lambda/handler.lua",
- ["kong.plugins.aws-lambda.schema"] = "kong/plugins/aws-lambda/schema.lua",
- ["kong.plugins.aws-lambda.v4"] = "kong/plugins/aws-lambda/v4.lua",
-
["kong.plugins.request-termination.handler"] = "kong/plugins/request-termination/handler.lua",
["kong.plugins.request-termination.schema"] = "kong/plugins/request-termination/schema.lua",
}
diff --git a/kong/plugins/aws-lambda/handler.lua b/kong/plugins/aws-lambda/handler.lua
deleted file mode 100644
index 79b0205f286d..000000000000
--- a/kong/plugins/aws-lambda/handler.lua
+++ /dev/null
@@ -1,248 +0,0 @@
--- Copyright (C) Kong Inc.
-local aws_v4 = require "kong.plugins.aws-lambda.v4"
-local http = require "resty.http"
-local cjson = require "cjson.safe"
-local meta = require "kong.meta"
-local constants = require "kong.constants"
-
-
-local VIA_HEADER = constants.HEADERS.VIA
-local VIA_HEADER_VALUE = meta._NAME .. "/" .. meta._VERSION
-
-
-local tostring = tostring
-local tonumber = tonumber
-local type = type
-local fmt = string.format
-local ngx_encode_base64 = ngx.encode_base64
-
-
-local raw_content_types = {
- ["text/plain"] = true,
- ["text/html"] = true,
- ["application/xml"] = true,
- ["text/xml"] = true,
- ["application/soap+xml"] = true,
-}
-
-
-local AWS_PORT = 443
-
-
---[[
- Response format should be
- {
- "statusCode": httpStatusCode,
- "headers": { "headerName": "headerValue", ... },
- "body": "..."
- }
---]]
-local function validate_custom_response(response)
- if type(response.statusCode) ~= "number" then
- return nil, "statusCode must be a number"
- end
-
- if response.headers ~= nil and type(response.headers) ~= "table" then
- return nil, "headers must be a table"
- end
-
- if response.body ~= nil and type(response.body) ~= "string" then
- return nil, "body must be a string"
- end
-
- return true
-end
-
-
-local function extract_proxy_response(content)
- local serialized_content, err = cjson.decode(content)
- if not serialized_content then
- return nil, err
- end
-
- local ok, err = validate_custom_response(serialized_content)
- if not ok then
- return nil, err
- end
-
- local headers = serialized_content.headers or {}
- local body = serialized_content.body or ""
- headers["Content-Length"] = #body
-
- return {
- status_code = tonumber(serialized_content.statusCode),
- body = body,
- headers = headers,
- }
-end
-
-
-local AWSLambdaHandler = {}
-
-
-function AWSLambdaHandler:access(conf)
- local upstream_body = kong.table.new(0, 6)
- local var = ngx.var
-
- if conf.forward_request_body or conf.forward_request_headers
- or conf.forward_request_method or conf.forward_request_uri
- then
- -- new behavior to forward request method, body, uri and their args
- if conf.forward_request_method then
- upstream_body.request_method = kong.request.get_method()
- end
-
- if conf.forward_request_headers then
- upstream_body.request_headers = kong.request.get_headers()
- end
-
- if conf.forward_request_uri then
- local path = kong.request.get_path()
- local query = kong.request.get_raw_query()
- if query ~= "" then
- upstream_body.request_uri = path .. "?" .. query
- else
- upstream_body.request_uri = path
- end
- upstream_body.request_uri_args = kong.request.get_query()
- end
-
- if conf.forward_request_body then
- local content_type = kong.request.get_header("content-type")
- local body_raw = kong.request.get_raw_body()
- local body_args, err = kong.request.get_body()
- if err and err:match("content type") then
- body_args = {}
- if not raw_content_types[content_type] then
- -- don't know what this body MIME type is, base64 it just in case
- body_raw = ngx_encode_base64(body_raw)
- upstream_body.request_body_base64 = true
- end
- end
-
- upstream_body.request_body = body_raw
- upstream_body.request_body_args = body_args
- end
-
- else
- -- backwards compatible upstream body for configurations not specifying
- -- `forward_request_*` values
- local body_args = kong.request.get_body()
- upstream_body = kong.table.merge(kong.request.get_query(), body_args)
- end
-
- local upstream_body_json, err = cjson.encode(upstream_body)
- if not upstream_body_json then
- kong.log.err("could not JSON encode upstream body",
- " to forward request values: ", err)
- end
-
- local host = fmt("lambda.%s.amazonaws.com", conf.aws_region)
- local path = fmt("/2015-03-31/functions/%s/invocations",
- conf.function_name)
- local port = conf.port or AWS_PORT
-
- local opts = {
- region = conf.aws_region,
- service = "lambda",
- method = "POST",
- headers = {
- ["X-Amz-Target"] = "invoke",
- ["X-Amz-Invocation-Type"] = conf.invocation_type,
- ["X-Amz-Log-Type"] = conf.log_type,
- ["Content-Type"] = "application/x-amz-json-1.1",
- ["Content-Length"] = upstream_body_json and tostring(#upstream_body_json),
- },
- body = upstream_body_json,
- path = path,
- host = host,
- port = port,
- access_key = conf.aws_key,
- secret_key = conf.aws_secret,
- query = conf.qualifier and "Qualifier=" .. conf.qualifier
- }
-
- local request, err = aws_v4(opts)
- if err then
- kong.log.err(err)
- return kong.response.exit(500, { message = "An unexpected error occurred" })
- end
-
- -- Trigger request
- local client = http.new()
- client:set_timeout(conf.timeout)
- client:connect(host, port)
- local ok, err = client:ssl_handshake()
- if not ok then
- kong.log.err(err)
- return kong.response.exit(500, { message = "An unexpected error occurred" })
- end
-
- local res, err = client:request {
- method = "POST",
- path = request.url,
- body = request.body,
- headers = request.headers
- }
- if not res then
- kong.log.err(err)
- return kong.response.exit(500, { message = "An unexpected error occurred" })
- end
-
- local content = res:read_body()
- local headers = res.headers
-
- if var.http2 then
- headers["Connection"] = nil
- headers["Keep-Alive"] = nil
- headers["Proxy-Connection"] = nil
- headers["Upgrade"] = nil
- headers["Transfer-Encoding"] = nil
- end
-
- local ok, err = client:set_keepalive(conf.keepalive)
- if not ok then
- kong.log.err(err)
- return kong.response.exit(500, { message = "An unexpected error occurred" })
- end
-
- local status
-
- if conf.is_proxy_integration then
- local proxy_response, err = extract_proxy_response(content)
- if not proxy_response then
- kong.log.err(err)
- return kong.response.exit(502, { message = "Bad Gateway",
- error = "could not JSON decode Lambda " ..
- "function response: " .. err })
- end
-
- status = proxy_response.status_code
- headers = kong.table.merge(headers, proxy_response.headers)
- content = proxy_response.body
- end
-
- if not status then
- if conf.unhandled_status
- and headers["X-Amz-Function-Error"] == "Unhandled"
- then
- status = conf.unhandled_status
-
- else
- status = res.status
- end
- end
-
- if kong.configuration.enabled_headers[VIA_HEADER] then
- headers[VIA_HEADER] = VIA_HEADER_VALUE
- end
-
- return kong.response.exit(status, content, headers)
-end
-
-
-AWSLambdaHandler.PRIORITY = 750
-AWSLambdaHandler.VERSION = "2.0.0"
-
-
-return AWSLambdaHandler
diff --git a/kong/plugins/aws-lambda/schema.lua b/kong/plugins/aws-lambda/schema.lua
deleted file mode 100644
index 5c186b728bc8..000000000000
--- a/kong/plugins/aws-lambda/schema.lua
+++ /dev/null
@@ -1,97 +0,0 @@
-local typedefs = require "kong.db.schema.typedefs"
-
-local REGIONS = {
- "ap-northeast-1", "ap-northeast-2",
- "ap-south-1",
- "ap-southeast-1", "ap-southeast-2",
- "ca-central-1",
- "cn-north-1",
- "cn-northwest-1",
- "eu-central-1",
- "eu-west-1", "eu-west-2",
- "sa-east-1",
- "us-east-1", "us-east-2",
- "us-gov-west-1",
- "us-west-1", "us-west-2",
-}
-
-
-return {
- name = "aws-lambda",
- fields = {
- { run_on = typedefs.run_on_first },
- { protocols = typedefs.protocols_http },
- { config = {
- type = "record",
- fields = {
- { timeout = {
- type = "number",
- required = true,
- default = 60000
- } },
- { keepalive = {
- type = "number",
- required = true,
- default = 60000
- } },
- { aws_key = {
- type = "string",
- required = true
- } },
- { aws_secret = {
- type = "string",
- required = true
- } },
- { aws_region = {
- type = "string",
- required = true,
- one_of = REGIONS
- } },
- { function_name = {
- type = "string",
- required = true
- } },
- { qualifier = {
- type = "string"
- } },
- { invocation_type = {
- type = "string",
- required = true,
- default = "RequestResponse",
- one_of = { "RequestResponse", "Event", "DryRun" },
- } },
- { log_type = {
- type = "string",
- required = true,
- default = "Tail",
- one_of = { "Tail", "None" },
- } },
- { port = typedefs.port { default = 443 }, },
- { unhandled_status = {
- type = "integer",
- between = { 100, 999 },
- } },
- { forward_request_method = {
- type = "boolean",
- default = false
- } },
- { forward_request_uri = {
- type = "boolean",
- default = false
- } },
- { forward_request_headers = {
- type = "boolean",
- default = false
- } },
- { forward_request_body = {
- type = "boolean",
- default = false
- } },
- { is_proxy_integration = {
- type = "boolean",
- default = false
- } },
- }
- } },
- },
-}
diff --git a/kong/plugins/aws-lambda/v4.lua b/kong/plugins/aws-lambda/v4.lua
deleted file mode 100644
index d7c40de54600..000000000000
--- a/kong/plugins/aws-lambda/v4.lua
+++ /dev/null
@@ -1,227 +0,0 @@
--- Performs AWSv4 Signing
--- http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
-
-local resty_sha256 = require "resty.sha256"
-local pl_string = require "pl.stringx"
-local openssl_hmac = require "openssl.hmac"
-
-local ALGORITHM = "AWS4-HMAC-SHA256"
-
-local CHAR_TO_HEX = {};
-for i = 0, 255 do
- local char = string.char(i)
- local hex = string.format("%02x", i)
- CHAR_TO_HEX[char] = hex
-end
-
-local function hmac(secret, data)
- return openssl_hmac.new(secret, "sha256"):final(data)
-end
-
-local function hash(str)
- local sha256 = resty_sha256:new()
- sha256:update(str)
- return sha256:final()
-end
-
-local function hex_encode(str) -- From prosody's util.hex
- return (str:gsub(".", CHAR_TO_HEX))
-end
-
-local function percent_encode(char)
- return string.format("%%%02X", string.byte(char))
-end
-
-local function canonicalise_path(path)
- local segments = {}
- for segment in path:gmatch("/([^/]*)") do
- if segment == "" or segment == "." then
- segments = segments -- do nothing and avoid lint
- elseif segment == " .. " then
- -- intentionally discards components at top level
- segments[#segments] = nil
- else
- segments[#segments+1] = ngx.unescape_uri(segment):gsub("[^%w%-%._~]",
- percent_encode)
- end
- end
- local len = #segments
- if len == 0 then
- return "/"
- end
- -- If there was a slash on the end, keep it there.
- if path:sub(-1, -1) == "/" then
- len = len + 1
- segments[len] = ""
- end
- segments[0] = ""
- segments = table.concat(segments, "/", 0, len)
- return segments
-end
-
-local function canonicalise_query_string(query)
- local q = {}
- for key, val in query:gmatch("([^&=]+)=?([^&]*)") do
- key = ngx.unescape_uri(key):gsub("[^%w%-%._~]", percent_encode)
- val = ngx.unescape_uri(val):gsub("[^%w%-%._~]", percent_encode)
- q[#q+1] = key .. "=" .. val
- end
- table.sort(q)
- return table.concat(q, "&")
-end
-
-local function derive_signing_key(kSecret, date, region, service)
- local kDate = hmac("AWS4" .. kSecret, date)
- local kRegion = hmac(kDate, region)
- local kService = hmac(kRegion, service)
- local kSigning = hmac(kService, "aws4_request")
- return kSigning
-end
-
-local function prepare_awsv4_request(tbl)
- local domain = tbl.domain or "amazonaws.com"
- local region = tbl.region
- local service = tbl.service
- local request_method = tbl.method
- local canonicalURI = tbl.canonicalURI
- local path = tbl.path
- if path and not canonicalURI then
- canonicalURI = canonicalise_path(path)
- elseif canonicalURI == nil or canonicalURI == "" then
- canonicalURI = "/"
- end
- local canonical_querystring = tbl.canonical_querystring
- local query = tbl.query
- if query and not canonical_querystring then
- canonical_querystring = canonicalise_query_string(query)
- end
- local req_headers = tbl.headers or {}
- local req_payload = tbl.body
- local access_key = tbl.access_key
- local signing_key = tbl.signing_key
- local secret_key
- if not signing_key then
- secret_key = tbl.secret_key
- if secret_key == nil then
- return nil, "either 'signing_key' or 'secret_key' must be provided"
- end
- end
- local tls = tbl.tls
- if tls == nil then
- tls = true
- end
- local port = tbl.port or (tls and 443 or 80)
- local timestamp = tbl.timestamp or ngx.time()
- local req_date = os.date("!%Y%m%dT%H%M%SZ", timestamp)
- local date = os.date("!%Y%m%d", timestamp)
-
- local host = service .. "." .. region .. "." .. domain
- local host_header do -- If the "standard" port is not in use, the port should be added to the Host header
- local with_port
- if tls then
- with_port = port ~= 443
- else
- with_port = port ~= 80
- end
- if with_port then
- host_header = string.format("%s:%d", host, port)
- else
- host_header = host
- end
- end
-
- local headers = {
- ["X-Amz-Date"] = req_date;
- Host = host_header;
- }
- local add_auth_header = true
- for k, v in pairs(req_headers) do
- k = k:gsub("%f[^%z-]%w", string.upper) -- convert to standard header title case
- if k == "Authorization" then
- add_auth_header = false
- elseif v == false then -- don't allow a default value for this header
- v = nil
- end
- headers[k] = v
- end
-
- -- Task 1: Create a Canonical Request For Signature Version 4
- -- http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
- local canonical_headers, signed_headers do
- -- We structure this code in a way so that we only have to sort once.
- canonical_headers, signed_headers = {}, {}
- local i = 0
- for name, value in pairs(headers) do
- if value then -- ignore headers with 'false', they are used to override defaults
- i = i + 1
- local name_lower = name:lower()
- signed_headers[i] = name_lower
- if canonical_headers[name_lower] ~= nil then
- return nil, "header collision"
- end
- canonical_headers[name_lower] = pl_string.strip(value)
- end
- end
- table.sort(signed_headers)
- for j=1, i do
- local name = signed_headers[j]
- local value = canonical_headers[name]
- canonical_headers[j] = name .. ":" .. value .. "\n"
- end
- signed_headers = table.concat(signed_headers, ";", 1, i)
- canonical_headers = table.concat(canonical_headers, nil, 1, i)
- end
- local canonical_request =
- request_method .. '\n' ..
- canonicalURI .. '\n' ..
- (canonical_querystring or "") .. '\n' ..
- canonical_headers .. '\n' ..
- signed_headers .. '\n' ..
- hex_encode(hash(req_payload or ""))
-
- local hashed_canonical_request = hex_encode(hash(canonical_request))
- -- Task 2: Create a String to Sign for Signature Version 4
- -- http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
- local credential_scope = date .. "/" .. region .. "/" .. service .. "/aws4_request"
- local string_to_sign =
- ALGORITHM .. '\n' ..
- req_date .. '\n' ..
- credential_scope .. '\n' ..
- hashed_canonical_request
-
- -- Task 3: Calculate the AWS Signature Version 4
- -- http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
- if signing_key == nil then
- signing_key = derive_signing_key(secret_key, date, region, service)
- end
- local signature = hex_encode(hmac(signing_key, string_to_sign))
- -- Task 4: Add the Signing Information to the Request
- -- http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
- local authorization = ALGORITHM
- .. " Credential=" .. access_key .. "/" .. credential_scope
- .. ", SignedHeaders=" .. signed_headers
- .. ", Signature=" .. signature
- if add_auth_header then
- headers.Authorization = authorization
- end
-
- local target = path or canonicalURI
- if query or canonical_querystring then
- target = target .. "?" .. (query or canonical_querystring)
- end
- local scheme = tls and "https" or "http"
- local url = scheme .. "://" .. host_header .. target
-
- return {
- url = url,
- host = host,
- port = port,
- tls = tls,
- method = request_method,
- target = target,
- headers = headers,
- body = req_payload,
- }
-end
-
-return prepare_awsv4_request
diff --git a/spec/03-plugins/22-aws-lambda/01-access_spec.lua b/spec/03-plugins/22-aws-lambda/01-access_spec.lua
deleted file mode 100644
index 597aa6a0db75..000000000000
--- a/spec/03-plugins/22-aws-lambda/01-access_spec.lua
+++ /dev/null
@@ -1,882 +0,0 @@
-local cjson = require "cjson"
-local helpers = require "spec.helpers"
-local meta = require "kong.meta"
-
-
-local server_tokens = meta._SERVER_TOKENS
-
-
-local fixtures = {
- http_mock = {
- lambda_plugin = [[
-
- server {
- server_name mock_aws_lambda;
- listen 10001 ssl;
-
- ssl_certificate ${{SSL_CERT}};
- ssl_certificate_key ${{SSL_CERT_KEY}};
- ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
-
- location ~ "/2015-03-31/functions/(?:[^/])*/invocations" {
- content_by_lua_block {
- local function x()
- local function say(res, status)
- ngx.header["x-amzn-RequestId"] = "foo"
-
- if string.match(ngx.var.uri, "functionWithUnhandledError") then
- ngx.header["X-Amz-Function-Error"] = "Unhandled"
- end
-
- ngx.status = status
-
- if string.match(ngx.var.uri, "functionWithBadJSON") then
- local badRes = "{\"foo\":\"bar\""
- ngx.header["Content-Length"] = #badRes + 1
- ngx.say(badRes)
-
- elseif string.match(ngx.var.uri, "functionWithNoResponse") then
- ngx.header["Content-Length"] = 0
-
- elseif type(res) == 'string' then
- ngx.header["Content-Length"] = #res + 1
- ngx.say(res)
-
- else
- ngx.req.discard_body()
- ngx.header['Content-Length'] = 0
- end
-
- ngx.exit(0)
- end
-
- ngx.sleep(.2) -- mock some network latency
-
- local invocation_type = ngx.var.http_x_amz_invocation_type
- if invocation_type == 'Event' then
- say(nil, 202)
-
- elseif invocation_type == 'DryRun' then
- say(nil, 204)
- end
-
- local qargs = ngx.req.get_uri_args()
- ngx.req.read_body()
- local args = require("cjson").decode(ngx.req.get_body_data())
-
- say(ngx.req.get_body_data(), 200)
- end
- local ok, err = pcall(x)
- if not ok then
- ngx.log(ngx.ERR, "Mock error: ", err)
- end
- }
- }
- }
-
- ]]
- },
-}
-
-
-for _, strategy in helpers.each_strategy() do
- describe("Plugin: AWS Lambda (access) [#" .. strategy .. "]", function()
- local proxy_client
- local admin_client
-
- lazy_setup(function()
- local bp = helpers.get_db_utils(strategy, {
- "routes",
- "services",
- "plugins",
- })
-
- local route1 = bp.routes:insert {
- hosts = { "lambda.com" },
- }
-
- local route2 = bp.routes:insert {
- hosts = { "lambda2.com" },
- }
-
- local route3 = bp.routes:insert {
- hosts = { "lambda3.com" },
- }
-
- local route4 = bp.routes:insert {
- hosts = { "lambda4.com" },
- }
-
- local route5 = bp.routes:insert {
- hosts = { "lambda5.com" },
- }
-
- local route6 = bp.routes:insert {
- hosts = { "lambda6.com" },
- }
-
- local route7 = bp.routes:insert {
- hosts = { "lambda7.com" },
- }
-
- local route8 = bp.routes:insert {
- hosts = { "lambda8.com" },
- }
-
- local route9 = bp.routes:insert {
- hosts = { "lambda9.com" },
- protocols = { "http", "https" },
- service = bp.services:insert({
- protocol = "http",
- host = "httpbin.org",
- port = 80,
- })
- }
-
- local service10 = bp.services:insert({
- protocol = "http",
- host = "httpbin.org",
- port = 80,
- })
-
- local route10 = bp.routes:insert {
- hosts = { "lambda10.com" },
- protocols = { "http", "https" },
- service = service10
- }
-
- local service11 = bp.services:insert({
- protocol = "http",
- host = "httpbin.org",
- port = 80,
- })
-
- local route11 = bp.routes:insert {
- hosts = { "lambda11.com" },
- protocols = { "http", "https" },
- service = service11
- }
-
- local service12 = bp.services:insert({
- protocol = "http",
- host = "httpbin.org",
- port = 80,
- })
-
- local route12 = bp.routes:insert {
- hosts = { "lambda12.com" },
- protocols = { "http", "https" },
- service = service12
- }
-
- local route13 = bp.routes:insert {
- hosts = { "lambda13.com" },
- protocols = { "http", "https" },
- service = service12,
- }
-
- local route14 = bp.routes:insert {
- hosts = { "lambda14.com" },
- protocols = { "http", "https" },
- service = ngx.null,
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route1.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route2.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- invocation_type = "Event",
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route3.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- invocation_type = "DryRun",
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route4.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- timeout = 100,
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route5.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "functionWithUnhandledError",
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route6.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "functionWithUnhandledError",
- invocation_type = "Event",
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route7.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "functionWithUnhandledError",
- invocation_type = "DryRun",
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route8.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "functionWithUnhandledError",
- unhandled_status = 412,
- },
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route9.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- forward_request_method = true,
- forward_request_uri = true,
- forward_request_headers = true,
- forward_request_body = true,
- }
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route10.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- forward_request_method = true,
- forward_request_uri = false,
- forward_request_headers = true,
- forward_request_body = true,
- }
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route11.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- is_proxy_integration = true,
- }
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route12.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "functionWithBadJSON",
- is_proxy_integration = true,
- }
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route13.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "functionWithNoResponse",
- is_proxy_integration = true,
- }
- }
-
- bp.plugins:insert {
- name = "aws-lambda",
- route = { id = route14.id },
- config = {
- port = 10001,
- aws_key = "mock-key",
- aws_secret = "mock-secret",
- aws_region = "us-east-1",
- function_name = "kongLambdaTest",
- },
- }
-
- assert(helpers.start_kong({
- database = strategy,
- nginx_conf = "spec/fixtures/custom_nginx.template",
- }, nil, nil, fixtures))
- end)
-
- before_each(function()
- proxy_client = helpers.proxy_client()
- admin_client = helpers.admin_client()
- end)
-
- after_each(function ()
- proxy_client:close()
- admin_client:close()
- end)
-
- lazy_teardown(function()
- helpers.stop_kong()
- end)
-
- it("invokes a Lambda function with GET", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda.com"
- }
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
- assert.equal("some_value1", body.key1)
- assert.is_nil(res.headers["X-Amz-Function-Error"])
- end)
- it("invokes a Lambda function with POST params", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda.com",
- ["Content-Type"] = "application/x-www-form-urlencoded"
- },
- body = {
- key1 = "some_value_post1",
- key2 = "some_value_post2",
- key3 = "some_value_post3"
- }
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
- assert.equal("some_value_post1", body.key1)
- end)
- it("invokes a Lambda function with POST json body", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda.com",
- ["Content-Type"] = "application/json"
- },
- body = {
- key1 = "some_value_json1",
- key2 = "some_value_json2",
- key3 = "some_value_json3"
- }
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
- assert.equal("some_value_json1", body.key1)
- end)
- it("passes empty json arrays unmodified", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda.com",
- ["Content-Type"] = "application/json"
- },
- body = '[{}, []]'
- })
- assert.res_status(200, res)
- assert.equal('[{},[]]', string.gsub(res:read_body(), "\n",""))
- end)
- it("invokes a Lambda function with POST and both querystring and body params", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post?key1=from_querystring",
- headers = {
- ["Host"] = "lambda.com",
- ["Content-Type"] = "application/x-www-form-urlencoded"
- },
- body = {
- key2 = "some_value_post2",
- key3 = "some_value_post3"
- }
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
- assert.equal("from_querystring", body.key1)
- end)
- it("invokes a Lambda function with POST and xml payload, custom header and query partameter", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post?key1=from_querystring",
- headers = {
- ["Host"] = "lambda9.com",
- ["Content-Type"] = "application/xml",
- ["custom-header"] = "someheader"
- },
- body = ""
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
-
- -- request_method
- assert.equal("POST", body.request_method)
-
- -- request_uri
- assert.equal("/post?key1=from_querystring", body.request_uri)
- assert.is_table(body.request_uri_args)
-
- -- request_headers
- assert.equal("someheader", body.request_headers["custom-header"])
- assert.equal("lambda9.com", body.request_headers.host)
-
- -- request_body
- assert.equal("", body.request_body)
- assert.is_table(body.request_body_args)
- end)
- it("invokes a Lambda function with POST and json payload, custom header and query partameter", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post?key1=from_querystring",
- headers = {
- ["Host"] = "lambda10.com",
- ["Content-Type"] = "application/json",
- ["custom-header"] = "someheader"
- },
- body = { key2 = "some_value" }
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
-
- -- request_method
- assert.equal("POST", body.request_method)
-
- -- no request_uri
- assert.is_nil(body.request_uri)
- assert.is_nil(body.request_uri_args)
-
- -- request_headers
- assert.equal("lambda10.com", body.request_headers.host)
- assert.equal("someheader", body.request_headers["custom-header"])
-
- -- request_body
- assert.equal("some_value", body.request_body_args.key2)
- assert.is_table(body.request_body_args)
- end)
- it("invokes a Lambda function with POST and txt payload, custom header and query partameter", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post?key1=from_querystring",
- headers = {
- ["Host"] = "lambda9.com",
- ["Content-Type"] = "text/plain",
- ["custom-header"] = "someheader"
- },
- body = "some text"
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
-
- -- request_method
- assert.equal("POST", body.request_method)
-
- -- request_uri
- assert.equal("/post?key1=from_querystring", body.request_uri)
- assert.is_table(body.request_uri_args)
-
- -- request_headers
- assert.equal("someheader", body.request_headers["custom-header"])
- assert.equal("lambda9.com", body.request_headers.host)
-
- -- request_body
- assert.equal("some text", body.request_body)
- assert.is_nil(body.request_body_base64)
- assert.is_table(body.request_body_args)
- end)
- it("invokes a Lambda function with POST and binary payload and custom header", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post?key1=from_querystring",
- headers = {
- ["Host"] = "lambda9.com",
- ["Content-Type"] = "application/octet-stream",
- ["custom-header"] = "someheader"
- },
- body = "01234"
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
-
- -- request_method
- assert.equal("POST", body.request_method)
-
- -- request_uri
- assert.equal("/post?key1=from_querystring", body.request_uri)
- assert.is_table(body.request_uri_args)
-
- -- request_headers
- assert.equal("lambda9.com", body.request_headers.host)
- assert.equal("someheader", body.request_headers["custom-header"])
-
- -- request_body
- assert.equal(ngx.encode_base64('01234'), body.request_body)
- assert.is_true(body.request_body_base64)
- assert.is_table(body.request_body_args)
- end)
- it("invokes a Lambda function with POST params and Event invocation_type", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda2.com",
- ["Content-Type"] = "application/x-www-form-urlencoded"
- },
- body = {
- key1 = "some_value_post1",
- key2 = "some_value_post2",
- key3 = "some_value_post3"
- }
- })
- assert.res_status(202, res)
- assert.is_string(res.headers["x-amzn-RequestId"])
- end)
- it("invokes a Lambda function with POST params and DryRun invocation_type", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda3.com",
- ["Content-Type"] = "application/x-www-form-urlencoded"
- },
- body = {
- key1 = "some_value_post1",
- key2 = "some_value_post2",
- key3 = "some_value_post3"
- }
- })
- assert.res_status(204, res)
- assert.is_string(res.headers["x-amzn-RequestId"])
- end)
- it("errors on connection timeout", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda4.com",
- }
- })
- assert.res_status(500, res)
- end)
-
- it("invokes a Lambda function with an unhandled function error (and no unhandled_status set)", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda5.com"
- }
- })
- assert.res_status(200, res)
- assert.equal("Unhandled", res.headers["X-Amz-Function-Error"])
- end)
- it("invokes a Lambda function with an unhandled function error with Event invocation type", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda6.com"
- }
- })
- assert.res_status(202, res)
- assert.equal("Unhandled", res.headers["X-Amz-Function-Error"])
- end)
- it("invokes a Lambda function with an unhandled function error with DryRun invocation type", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda7.com"
- }
- })
- assert.res_status(204, res)
- assert.equal("Unhandled", res.headers["X-Amz-Function-Error"])
- end)
- it("invokes a Lambda function with an unhandled function error", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda8.com"
- }
- })
- assert.res_status(412, res)
- assert.equal("Unhandled", res.headers["X-Amz-Function-Error"])
- end)
-
- it("returns server tokens with Via header", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda.com"
- }
- })
-
- assert.equal(server_tokens, res.headers["Via"])
- end)
-
- it("returns Content-Length header", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda.com"
- }
- })
-
- assert.equal(65, tonumber(res.headers["Content-Length"]))
- end)
-
-
- describe("config.is_proxy_integration = true", function()
- it("sets proper status code on custom response from Lambda", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda11.com",
- ["Content-Type"] = "application/json"
- },
- body = {
- statusCode = 201,
- }
- })
- local body = assert.res_status(201, res)
- assert.equal(0, tonumber(res.headers["Content-Length"]))
- assert.equal(nil, res.headers["X-Custom-Header"])
- assert.equal("", body)
- end)
-
- it("sets proper status code/headers/body on custom response from Lambda", function()
- -- the lambda function must return a string
- -- for the custom response "body" property
- local body = cjson.encode({
- key1 = "some_value_post1",
- key2 = "some_value_post2",
- key3 = "some_value_post3",
- })
-
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda11.com",
- ["Content-Type"] = "application/json",
- },
- body = {
- statusCode = 201,
- body = body,
- headers = {
- ["X-Custom-Header"] = "Hello world!"
- }
- }
- })
-
- local res_body = assert.res_status(201, res)
- assert.equal(79, tonumber(res.headers["Content-Length"]))
- assert.equal("Hello world!", res.headers["X-Custom-Header"])
- assert.equal(body, res_body)
- end)
-
- it("override duplicated headers with value from the custom response from Lambda", function()
- -- the default "x-amzn-RequestId" returned is "foo"
- -- let's check it is overriden with a custom value
- local headers = {
- ["x-amzn-RequestId"] = "bar",
- }
-
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda11.com",
- ["Content-Type"] = "application/json",
- },
- body = {
- statusCode = 201,
- headers = headers,
- }
- })
-
- assert.res_status(201, res)
- assert.equal("bar", res.headers["x-amzn-RequestId"])
- end)
-
- it("returns HTTP 502 when 'status' property of custom response is not a number", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda11.com",
- ["Content-Type"] = "application/json",
- },
- body = {
- statusCode = "hello",
- }
- })
-
- assert.res_status(502, res)
- local b = assert.response(res).has.jsonbody()
- assert.equal("Bad Gateway", b.message)
- end)
-
- it("returns HTTP 502 when 'headers' property of custom response is not a table", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda11.com",
- ["Content-Type"] = "application/json",
- },
- body = {
- headers = "hello",
- }
- })
-
- assert.res_status(502, res)
- local b = assert.response(res).has.jsonbody()
- assert.equal("Bad Gateway", b.message)
- end)
-
- it("returns HTTP 502 when 'body' property of custom response is not a string", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda11.com",
- ["Content-Type"] = "application/json",
- },
- body = {
- statusCode = 201,
- body = 1234,
- }
- })
-
- assert.res_status(502, res)
- local b = assert.response(res).has.jsonbody()
- assert.equal("Bad Gateway", b.message)
- end)
-
- it("returns HTTP 502 with when response from lambda is not valid JSON", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda12.com",
- }
- })
-
- assert.res_status(502, res)
- local b = assert.response(res).has.jsonbody()
- assert.equal("Bad Gateway", b.message)
- end)
-
- it("returns HTTP 502 on empty response from Lambda", function()
- local res = assert(proxy_client:send {
- method = "POST",
- path = "/post",
- headers = {
- ["Host"] = "lambda13.com",
- }
- })
-
- assert.res_status(502, res)
- local b = assert.response(res).has.jsonbody()
- assert.equal("Bad Gateway", b.message)
- end)
-
- it("invokes a Lambda function with GET using serviceless route", function()
- local res = assert(proxy_client:send {
- method = "GET",
- path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
- headers = {
- ["Host"] = "lambda14.com"
- }
- })
- assert.res_status(200, res)
- local body = assert.response(res).has.jsonbody()
- assert.is_string(res.headers["x-amzn-RequestId"])
- assert.equal("some_value1", body.key1)
- assert.is_nil(res.headers["X-Amz-Function-Error"])
- end)
- end)
- end)
-end
diff --git a/spec/03-plugins/22-aws-lambda/02-schema_spec.lua b/spec/03-plugins/22-aws-lambda/02-schema_spec.lua
deleted file mode 100644
index bbda7062ba34..000000000000
--- a/spec/03-plugins/22-aws-lambda/02-schema_spec.lua
+++ /dev/null
@@ -1,55 +0,0 @@
-local schema_def = require "kong.plugins.aws-lambda.schema"
-local validate_plugin_config_schema = require("spec.helpers").validate_plugin_config_schema
-
-
-local kong = {
- table = require("kong.pdk.table").new()
-}
-
-
-local DEFAULTS = {
- timeout = 60000,
- keepalive = 60000,
- aws_key = "my-key",
- aws_secret = "my-secret",
- aws_region = "us-east-1",
- function_name = "my-function",
- invocation_type = "RequestResponse",
- log_type = "Tail",
- port = 443,
-}
-
-
-local function v(config)
- return validate_plugin_config_schema(
- kong.table.merge(DEFAULTS, config),
- schema_def
- )
-end
-
-
-describe("Plugin: AWS Lambda (schema)", function()
- it("accepts nil Unhandled Response Status Code", function()
- local ok, err = v({ unhandled_status = nil })
- assert.truthy(ok)
- assert.is_nil(err)
- end)
-
- it("accepts correct Unhandled Response Status Code", function()
- local ok, err = v({ unhandled_status = 412 })
- assert.truthy(ok)
- assert.is_nil(err)
- end)
-
- it("errors with Unhandled Response Status Code less than 100", function()
- local ok, err = v({ unhandled_status = 99 })
- assert.falsy(ok)
- assert.equal("value should be between 100 and 999", err.config.unhandled_status)
- end)
-
- it("errors with Unhandled Response Status Code greater than 999", function()
- local ok, err = v({ unhandled_status = 1000 })
- assert.falsy(ok)
- assert.equal("value should be between 100 and 999", err.config.unhandled_status)
- end)
-end)