This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 8f0b066 feat: faas plugin refactoring with url path forwarding (#5616)
8f0b066 is described below
commit 8f0b066c86257ad6af19f9b3b7e209ece95d17c9
Author: Bisakh <[email protected]>
AuthorDate: Mon Nov 29 11:58:49 2021 +0530
feat: faas plugin refactoring with url path forwarding (#5616)
---
apisix/plugins/azure-functions.lua | 98 ++----------------
apisix/plugins/serverless/generic-upstream.lua | 135 +++++++++++++++++++++++++
t/plugin/azure-functions.t | 115 +++++++++++++++++++++
3 files changed, 261 insertions(+), 87 deletions(-)
diff --git a/apisix/plugins/azure-functions.lua
b/apisix/plugins/azure-functions.lua
index 1597f2a..0b0e64d 100644
--- a/apisix/plugins/azure-functions.lua
+++ b/apisix/plugins/azure-functions.lua
@@ -14,30 +14,15 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
-local core = require("apisix.core")
-local http = require("resty.http")
local plugin = require("apisix.plugin")
-local ngx = ngx
-local plugin_name = "azure-functions"
+local plugin_name, plugin_version, priority = "azure-functions", 0.1, -1900
-local schema = {
+local azure_authz_schema = {
type = "object",
properties = {
- function_uri = {type = "string"},
- authorization = {
- type = "object",
- properties = {
- apikey = {type = "string"},
- clientid = {type = "string"}
- }
- },
- timeout = {type = "integer", minimum = 100, default = 3000},
- ssl_verify = {type = "boolean", default = true},
- keepalive = {type = "boolean", default = true},
- keepalive_timeout = {type = "integer", minimum = 1000, default =
60000},
- keepalive_pool = {type = "integer", minimum = 1, default = 5}
- },
- required = {"function_uri"}
+ apikey = {type = "string"},
+ clientid = {type = "string"}
+ }
}
local metadata_schema = {
@@ -48,31 +33,8 @@ local metadata_schema = {
}
}
-local _M = {
- version = 0.1,
- priority = -1900,
- name = plugin_name,
- schema = schema,
- metadata_schema = metadata_schema
-}
-
-function _M.check_schema(conf, schema_type)
- if schema_type == core.schema.TYPE_METADATA then
- return core.schema.check(metadata_schema, conf)
- end
- return core.schema.check(schema, conf)
-end
-
-function _M.access(conf, ctx)
- local uri_args = core.request.get_uri_args(ctx)
- local headers = core.request.headers(ctx) or {}
- local req_body, err = core.request.get_body()
-
- if err then
- core.log.error("error while reading request body: ", err)
- return 400
- end
-
+local function request_processor(conf, ctx, params)
+ local headers = params.headers or {}
-- set authorization headers if not already set by the client
-- we are following not to overwrite the authz keys
if not headers["x-functions-key"] and
@@ -91,47 +53,9 @@ function _M.access(conf, ctx)
end
end
- headers["host"] = nil
- local params = {
- method = ngx.req.get_method(),
- body = req_body,
- query = uri_args,
- headers = headers,
- keepalive = conf.keepalive,
- ssl_verify = conf.ssl_verify
- }
-
- -- Keepalive options
- if conf.keepalive then
- params.keepalive_timeout = conf.keepalive_timeout
- params.keepalive_pool = conf.keepalive_pool
- end
-
- local httpc = http.new()
- httpc:set_timeout(conf.timeout)
-
- local res, err = httpc:request_uri(conf.function_uri, params)
-
- if not res or err then
- core.log.error("failed to process azure function, err: ", err)
- return 503
- end
-
- -- According to RFC7540
https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2, endpoint
- -- must not generate any connection specific headers for HTTP/2 requests.
- local response_headers = res.headers
- if ngx.var.http2 then
- response_headers["Connection"] = nil
- response_headers["Keep-Alive"] = nil
- response_headers["Proxy-Connection"] = nil
- response_headers["Upgrade"] = nil
- response_headers["Transfer-Encoding"] = nil
- end
-
- -- setting response headers
- core.response.set_header(response_headers)
-
- return res.status, res.body
+ params.headers = headers
end
-return _M
+
+return require("apisix.plugins.serverless.generic-upstream")(plugin_name,
+ plugin_version, priority, request_processor, azure_authz_schema,
metadata_schema)
diff --git a/apisix/plugins/serverless/generic-upstream.lua
b/apisix/plugins/serverless/generic-upstream.lua
new file mode 100644
index 0000000..0ae59b6
--- /dev/null
+++ b/apisix/plugins/serverless/generic-upstream.lua
@@ -0,0 +1,135 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+local ngx = ngx
+local require = require
+local type = type
+local string = string
+
+return function(plugin_name, version, priority, request_processor,
authz_schema, metadata_schema)
+ local core = require("apisix.core")
+ local http = require("resty.http")
+ local url = require("net.url")
+
+ if request_processor and type(request_processor) ~= "function" then
+ return "Failed to generate plugin due to invalid header processor
type, " ..
+ "expected: function, received: " .. type(request_processor)
+ end
+
+ local schema = {
+ type = "object",
+ properties = {
+ function_uri = {type = "string"},
+ authorization = authz_schema,
+ timeout = {type = "integer", minimum = 100, default = 3000},
+ ssl_verify = {type = "boolean", default = true},
+ keepalive = {type = "boolean", default = true},
+ keepalive_timeout = {type = "integer", minimum = 1000, default =
60000},
+ keepalive_pool = {type = "integer", minimum = 1, default = 5}
+ },
+ required = {"function_uri"}
+ }
+
+ local _M = {
+ version = version,
+ priority = priority,
+ name = plugin_name,
+ schema = schema,
+ metadata_schema = metadata_schema
+ }
+
+ function _M.check_schema(conf, schema_type)
+ if schema_type == core.schema.TYPE_METADATA then
+ return core.schema.check(metadata_schema, conf)
+ end
+ return core.schema.check(schema, conf)
+ end
+
+ function _M.access(conf, ctx)
+ local uri_args = core.request.get_uri_args(ctx)
+ local headers = core.request.headers(ctx) or {}
+
+ local req_body, err = core.request.get_body()
+
+ if err then
+ core.log.error("error while reading request body: ", err)
+ return 400
+ end
+
+ -- forward the url path came through the matched uri
+ local url_decoded = url.parse(conf.function_uri)
+ local path = url_decoded.path or "/"
+
+ if ctx.curr_req_matched and ctx.curr_req_matched[":ext"] then
+ local end_path = ctx.curr_req_matched[":ext"]
+
+ if path:byte(-1) == string.byte("/") or end_path:byte(1) ==
string.byte("/") then
+ path = path .. end_path
+ else
+ path = path .. "/" .. end_path
+ end
+ end
+
+
+ headers["host"] = url_decoded.host
+ local params = {
+ method = ngx.req.get_method(),
+ body = req_body,
+ query = uri_args,
+ headers = headers,
+ path = path,
+ keepalive = conf.keepalive,
+ ssl_verify = conf.ssl_verify
+ }
+
+ -- Keepalive options
+ if conf.keepalive then
+ params.keepalive_timeout = conf.keepalive_timeout
+ params.keepalive_pool = conf.keepalive_pool
+ end
+
+ -- modify request info (if required)
+ request_processor(conf, ctx, params)
+
+ local httpc = http.new()
+ httpc:set_timeout(conf.timeout)
+
+ local res, err = httpc:request_uri(conf.function_uri, params)
+
+ if not res or err then
+ core.log.error("failed to process ", plugin_name, ", err: ", err)
+ return 503
+ end
+
+ -- According to RFC7540
https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2,
+ -- endpoint must not generate any connection specific headers for
HTTP/2 requests.
+ local response_headers = res.headers
+ if ngx.var.http2 then
+ response_headers["Connection"] = nil
+ response_headers["Keep-Alive"] = nil
+ response_headers["Proxy-Connection"] = nil
+ response_headers["Upgrade"] = nil
+ response_headers["Transfer-Encoding"] = nil
+ end
+
+ -- setting response headers
+ core.response.set_header(response_headers)
+
+ return res.status, res.body
+ end
+
+ return _M
+end
diff --git a/t/plugin/azure-functions.t b/t/plugin/azure-functions.t
index ea4d064..af58913 100644
--- a/t/plugin/azure-functions.t
+++ b/t/plugin/azure-functions.t
@@ -42,6 +42,24 @@ add_block_preprocessor(sub {
}
}
+ location /api {
+ content_by_lua_block {
+ ngx.say("invocation /api successful")
+ }
+ }
+
+ location /api/httptrigger {
+ content_by_lua_block {
+ ngx.say("invocation /api/httptrigger successful")
+ }
+ }
+
+ location /api/http/trigger {
+ content_by_lua_block {
+ ngx.say("invocation /api/http/trigger successful")
+ }
+ }
+
location /azure-demo {
content_by_lua_block {
$inside_lua_block
@@ -375,3 +393,100 @@ ngx.say("Authz-Header - " .. headers["x-functions-key"]
or "")
passed
passed
Authz-Header - metadata_key
+
+
+
+=== TEST 10: check if url path being forwarded correctly by creating a semi
correct path uri
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ -- creating a semi path route
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "azure-functions": {
+ "function_uri": "http://localhost:8765/api"
+ }
+ },
+ "uri": "/azure/*"
+ }]]
+ )
+ if code >= 300 then
+ ngx.status = code
+ ngx.say("fail")
+ return
+ end
+
+ ngx.say(body)
+
+ local code, _, body = t("/azure/httptrigger", "GET")
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.print(body)
+ }
+ }
+--- response_body
+passed
+invocation /api/httptrigger successful
+
+
+
+=== TEST 11: check multilevel url path forwarding
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, _, body = t("/azure/http/trigger", "GET")
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.print(body)
+ }
+ }
+--- response_body
+invocation /api/http/trigger successful
+
+
+
+=== TEST 12: check url path forwarding containing multiple slashes
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, _, body = t("/azure///http////trigger", "GET")
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.print(body)
+ }
+ }
+--- response_body
+invocation /api/http/trigger successful
+
+
+
+=== TEST 13: check url path forwarding with no excess path
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, _, body = t("/azure/", "GET")
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.print(body)
+ }
+ }
+--- response_body
+invocation /api successful