175 lines
5.2 KiB
Lua
175 lines
5.2 KiB
Lua
local http = require "resty.http"
|
|
local clone = require "table.clone"
|
|
local sandbox = require "kong.tools.sandbox"
|
|
local cycle_aware_deep_copy = require("kong.tools.table").cycle_aware_deep_copy
|
|
local pl_template = require "pl.template"
|
|
local lua_enabled = sandbox.configuration.enabled
|
|
local sandbox_enabled = sandbox.configuration.sandbox_enabled
|
|
local get_request_headers = kong.request.get_headers
|
|
local get_uri_args = kong.request.get_query
|
|
local rawset = rawset
|
|
local str_find = string.find
|
|
local tostring = tostring
|
|
local null = ngx.null
|
|
local EMPTY = require("kong.tools.table").EMPTY
|
|
|
|
local CONTENT_TYPE_HEADER_NAME = "Content-Type"
|
|
local DEFAULT_CONTENT_TYPE_HEADER = "application/x-protobuf"
|
|
local DEFAULT_HEADERS = {
|
|
[CONTENT_TYPE_HEADER_NAME] = DEFAULT_CONTENT_TYPE_HEADER
|
|
}
|
|
|
|
local _log_prefix = "[otel] "
|
|
|
|
local function http_export_request(conf, pb_data, headers)
|
|
local httpc = http.new()
|
|
httpc:set_timeouts(conf.connect_timeout, conf.send_timeout, conf.read_timeout)
|
|
local res, err = httpc:request_uri(conf.endpoint, {
|
|
method = "POST",
|
|
body = pb_data,
|
|
headers = headers,
|
|
})
|
|
|
|
if not res then
|
|
return false, "failed to send request: " .. err
|
|
|
|
elseif res and res.status ~= 200 then
|
|
return false, "response error: " .. tostring(res.status) .. ", body: " .. tostring(res.body)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
local function get_headers(conf_headers)
|
|
if not conf_headers or conf_headers == null then
|
|
return DEFAULT_HEADERS
|
|
end
|
|
|
|
if conf_headers[CONTENT_TYPE_HEADER_NAME] then
|
|
return conf_headers
|
|
end
|
|
|
|
local headers = clone(conf_headers)
|
|
headers[CONTENT_TYPE_HEADER_NAME] = DEFAULT_CONTENT_TYPE_HEADER
|
|
return headers
|
|
end
|
|
|
|
|
|
local compile_opts = {
|
|
escape = "\xff", -- disable '#' as a valid template escape
|
|
}
|
|
|
|
local template_cache = setmetatable( {}, { __mode = "k" })
|
|
|
|
local __meta_environment = {
|
|
__index = function(self, key)
|
|
local lazy_loaders = {
|
|
headers = function(self)
|
|
return get_request_headers() or EMPTY
|
|
end,
|
|
query_params = function(self)
|
|
return get_uri_args() or EMPTY
|
|
end,
|
|
uri_captures = function(self)
|
|
return (ngx.ctx.router_matches or EMPTY).uri_captures or EMPTY
|
|
end,
|
|
shared = function(self)
|
|
return ((kong or EMPTY).ctx or EMPTY).shared or EMPTY
|
|
end,
|
|
}
|
|
local loader = lazy_loaders[key]
|
|
if not loader then
|
|
if lua_enabled and not sandbox_enabled then
|
|
return _G[key]
|
|
end
|
|
return
|
|
end
|
|
-- set the result on the table to not load again
|
|
local value = loader()
|
|
rawset(self, key, value)
|
|
return value
|
|
end,
|
|
__newindex = function(self)
|
|
error("This environment is read-only.")
|
|
end,
|
|
}
|
|
|
|
|
|
local function param_value(source_template, resource_attributes, template_env)
|
|
if not source_template or source_template == "" then
|
|
return nil
|
|
end
|
|
|
|
if not lua_enabled then
|
|
-- Detect expressions in the source template
|
|
local expr = str_find(source_template, "%$%(.*%)")
|
|
if expr then
|
|
return nil, "loading of untrusted Lua code disabled because " ..
|
|
"'untrusted_lua' config option is set to 'off'"
|
|
end
|
|
-- Lua is disabled, no need to render the template
|
|
return source_template
|
|
end
|
|
|
|
-- find compiled templates for this plugin-configuration array
|
|
local compiled_templates = template_cache[resource_attributes]
|
|
if not compiled_templates then
|
|
compiled_templates = {}
|
|
-- store it by `resource_attributes` which is part of the plugin `conf` table
|
|
-- it will be GC'ed at the same time as `conf` and hence invalidate the
|
|
-- compiled templates here as well as the cache-table has weak-keys
|
|
template_cache[resource_attributes] = compiled_templates
|
|
end
|
|
|
|
-- Find or compile the specific template
|
|
local compiled_template = compiled_templates[source_template]
|
|
if not compiled_template then
|
|
local res
|
|
compiled_template, res = pl_template.compile(source_template, compile_opts)
|
|
if res then
|
|
return source_template
|
|
end
|
|
compiled_templates[source_template] = compiled_template
|
|
end
|
|
|
|
return compiled_template:render(template_env)
|
|
end
|
|
|
|
local function compile_resource_attributes(resource_attributes)
|
|
if not resource_attributes then
|
|
return EMPTY
|
|
end
|
|
|
|
local template_env = {}
|
|
if lua_enabled and sandbox_enabled then
|
|
-- load the sandbox environment to be used to render the template
|
|
template_env = cycle_aware_deep_copy(sandbox.configuration.environment)
|
|
-- here we can optionally add functions to expose to the sandbox, eg:
|
|
-- tostring = tostring,
|
|
-- because headers may contain array elements such as duplicated headers
|
|
-- type is a useful function in these cases. See issue #25.
|
|
template_env.type = type
|
|
end
|
|
setmetatable(template_env, __meta_environment)
|
|
local compiled_resource_attributes = {}
|
|
for current_name, current_value in pairs(resource_attributes) do
|
|
local res, err = param_value(current_value, resource_attributes, template_env)
|
|
if not res then
|
|
return nil, err
|
|
end
|
|
|
|
compiled_resource_attributes[current_name] = res
|
|
end
|
|
return compiled_resource_attributes
|
|
end
|
|
|
|
|
|
|
|
return {
|
|
http_export_request = http_export_request,
|
|
get_headers = get_headers,
|
|
_log_prefix = _log_prefix,
|
|
compile_resource_attributes = compile_resource_attributes,
|
|
}
|