2009 lines
57 KiB
Lua
2009 lines
57 KiB
Lua
-- Kong, the biggest ape in town
|
|
--
|
|
-- /\ ____
|
|
-- <> ( oo )
|
|
-- <>_| ^^ |_
|
|
-- <> @ \
|
|
-- /~~\ . . _ |
|
|
-- /~~~~\ | |
|
|
-- /~~~~~~\/ _| |
|
|
-- |[][][]/ / [m]
|
|
-- |[][][[m]
|
|
-- |[][][]|
|
|
-- |[][][]|
|
|
-- |[][][]|
|
|
-- |[][][]|
|
|
-- |[][][]|
|
|
-- |[][][]|
|
|
-- |[][][]|
|
|
-- |[][][]|
|
|
-- |[|--|]|
|
|
-- |[| |]|
|
|
-- ========
|
|
-- ==========
|
|
-- |[[ ]]|
|
|
-- ==========
|
|
|
|
local pcall = pcall
|
|
|
|
|
|
assert(package.loaded["resty.core"], "lua-resty-core must be loaded; make " ..
|
|
"sure 'lua_load_resty_core' is not "..
|
|
"disabled.")
|
|
|
|
|
|
local constants = require "kong.constants"
|
|
do
|
|
-- let's ensure the required shared dictionaries are
|
|
-- declared via lua_shared_dict in the Nginx conf
|
|
|
|
for _, dict in ipairs(constants.DICTS) do
|
|
if not ngx.shared[dict] then
|
|
return error("missing shared dict '" .. dict .. "' in Nginx " ..
|
|
"configuration, are you using a custom template? " ..
|
|
"Make sure the 'lua_shared_dict " .. dict .. " [SIZE];' " ..
|
|
"directive is defined.")
|
|
end
|
|
end
|
|
|
|
-- if we're running `nginx -t` then don't initialize
|
|
if os.getenv("KONG_NGINX_CONF_CHECK") then
|
|
return {
|
|
init = function() end,
|
|
}
|
|
end
|
|
end
|
|
|
|
|
|
require("kong.globalpatches")()
|
|
|
|
|
|
local kong_global = require "kong.global"
|
|
local PHASES = kong_global.phases
|
|
|
|
|
|
_G.kong = kong_global.new() -- no versioned PDK for plugins for now
|
|
|
|
|
|
local DB = require "kong.db"
|
|
local meta = require "kong.meta"
|
|
local lapis = require "lapis"
|
|
local runloop = require "kong.runloop.handler"
|
|
local stream_api = require "kong.tools.stream_api"
|
|
local declarative = require "kong.db.declarative"
|
|
local ngx_balancer = require "ngx.balancer"
|
|
local kong_resty_ctx = require "kong.resty.ctx"
|
|
local certificate = require "kong.runloop.certificate"
|
|
local concurrency = require "kong.concurrency"
|
|
local cache_warmup = require "kong.cache.warmup"
|
|
local balancer = require "kong.runloop.balancer"
|
|
local kong_error_handlers = require "kong.error_handlers"
|
|
local plugin_servers = require "kong.runloop.plugin_servers"
|
|
local lmdb_txn = require "resty.lmdb.transaction"
|
|
local instrumentation = require "kong.observability.tracing.instrumentation"
|
|
local process = require "ngx.process"
|
|
local tablepool = require "tablepool"
|
|
local table_new = require "table.new"
|
|
local emmy_debugger = require "kong.tools.emmy_debugger"
|
|
local get_ctx_table = require("resty.core.ctx").get_ctx_table
|
|
local admin_gui = require "kong.admin_gui"
|
|
local wasm = require "kong.runloop.wasm"
|
|
local reports = require "kong.reports"
|
|
local pl_file = require "pl.file"
|
|
local req_dyn_hook = require "kong.dynamic_hook"
|
|
local uuid = require("kong.tools.uuid").uuid
|
|
local kong_time = require("kong.tools.time")
|
|
|
|
|
|
local kong = kong
|
|
local ngx = ngx
|
|
local var = ngx.var
|
|
local arg = ngx.arg
|
|
local header = ngx.header
|
|
local ngx_log = ngx.log
|
|
local ngx_ALERT = ngx.ALERT
|
|
local ngx_CRIT = ngx.CRIT
|
|
local ngx_ERR = ngx.ERR
|
|
local ngx_WARN = ngx.WARN
|
|
local ngx_NOTICE = ngx.NOTICE
|
|
local ngx_INFO = ngx.INFO
|
|
local ngx_DEBUG = ngx.DEBUG
|
|
local is_http_module = ngx.config.subsystem == "http"
|
|
local is_stream_module = ngx.config.subsystem == "stream"
|
|
local worker_id = ngx.worker.id
|
|
local fmt = string.format
|
|
local type = type
|
|
local error = error
|
|
local ipairs = ipairs
|
|
local assert = assert
|
|
local tostring = tostring
|
|
local coroutine = coroutine
|
|
local concat = table.concat
|
|
local fetch_table = tablepool.fetch
|
|
local release_table = tablepool.release
|
|
local get_last_failure = ngx_balancer.get_last_failure
|
|
local set_current_peer = ngx_balancer.set_current_peer
|
|
local set_timeouts = ngx_balancer.set_timeouts
|
|
local set_more_tries = ngx_balancer.set_more_tries
|
|
local enable_keepalive = ngx_balancer.enable_keepalive
|
|
|
|
|
|
local time_ns = kong_time.time_ns
|
|
local get_now_ms = kong_time.get_now_ms
|
|
local get_start_time_ms = kong_time.get_start_time_ms
|
|
local get_updated_now_ms = kong_time.get_updated_now_ms
|
|
|
|
|
|
local req_dyn_hook_run_hook = req_dyn_hook.run_hook
|
|
local req_dyn_hook_is_group_enabled = req_dyn_hook.is_group_enabled
|
|
|
|
|
|
local DECLARATIVE_LOAD_KEY = constants.DECLARATIVE_LOAD_KEY
|
|
|
|
|
|
local CTX_NS = "ctx"
|
|
local CTX_NARR = 0
|
|
local CTX_NREC = 50 -- normally Kong has ~32 keys in ctx
|
|
|
|
|
|
local UPSTREAM_KEEPALIVE_POOL_SIZE
|
|
local UPSTREAM_KEEPALIVE_IDLE_TIMEOUT
|
|
local UPSTREAM_KEEPALIVE_MAX_REQUESTS
|
|
|
|
|
|
local declarative_entities
|
|
local declarative_meta
|
|
local declarative_hash
|
|
local schema_state
|
|
|
|
|
|
local stash_init_worker_error
|
|
local log_init_worker_errors
|
|
do
|
|
local init_worker_errors
|
|
local init_worker_errors_str
|
|
local ctx_k = {}
|
|
|
|
|
|
stash_init_worker_error = function(err)
|
|
if err == nil then
|
|
return
|
|
end
|
|
|
|
err = tostring(err)
|
|
|
|
if not init_worker_errors then
|
|
init_worker_errors = {}
|
|
end
|
|
|
|
table.insert(init_worker_errors, err)
|
|
init_worker_errors_str = concat(init_worker_errors, ", ")
|
|
|
|
return ngx_log(ngx_CRIT, "worker initialization error: ", err,
|
|
"; this node must be restarted")
|
|
end
|
|
|
|
|
|
log_init_worker_errors = function(ctx)
|
|
if not init_worker_errors_str or ctx[ctx_k] then
|
|
return
|
|
end
|
|
|
|
ctx[ctx_k] = true
|
|
|
|
return ngx_log(ngx_ALERT, "unsafe request processing due to earlier ",
|
|
"initialization errors; this node must be ",
|
|
"restarted (", init_worker_errors_str, ")")
|
|
end
|
|
end
|
|
|
|
|
|
local is_data_plane
|
|
local is_control_plane
|
|
local is_dbless
|
|
do
|
|
is_data_plane = function(config)
|
|
return config.role == "data_plane"
|
|
end
|
|
|
|
|
|
is_control_plane = function(config)
|
|
return config.role == "control_plane"
|
|
end
|
|
|
|
|
|
is_dbless = function(config)
|
|
return config.database == "off"
|
|
end
|
|
end
|
|
|
|
|
|
local reset_kong_shm
|
|
do
|
|
local preserve_keys = {
|
|
"kong:node_id",
|
|
constants.DYN_LOG_LEVEL_KEY,
|
|
constants.DYN_LOG_LEVEL_TIMEOUT_AT_KEY,
|
|
"events:requests",
|
|
"events:requests:http",
|
|
"events:requests:https",
|
|
"events:requests:h2c",
|
|
"events:requests:h2",
|
|
"events:requests:grpc",
|
|
"events:requests:grpcs",
|
|
"events:requests:ws",
|
|
"events:requests:wss",
|
|
"events:requests:go_plugins",
|
|
"events:km:visit",
|
|
"events:streams",
|
|
"events:streams:tcp",
|
|
"events:streams:tls",
|
|
"events:ai:response_tokens",
|
|
"events:ai:prompt_tokens",
|
|
"events:ai:requests",
|
|
}
|
|
|
|
reset_kong_shm = function(config)
|
|
local kong_shm = ngx.shared.kong
|
|
|
|
local preserved = {}
|
|
|
|
if is_dbless(config) then
|
|
if not (config.declarative_config or config.declarative_config_string) then
|
|
preserved[DECLARATIVE_LOAD_KEY] = kong_shm:get(DECLARATIVE_LOAD_KEY)
|
|
end
|
|
end
|
|
|
|
for _, key in ipairs(preserve_keys) do
|
|
preserved[key] = kong_shm:get(key) -- ignore errors
|
|
end
|
|
|
|
kong_shm:flush_all()
|
|
for key, value in pairs(preserved) do
|
|
kong_shm:set(key, value)
|
|
end
|
|
kong_shm:flush_expired(0)
|
|
end
|
|
end
|
|
|
|
|
|
local function setup_plugin_context(ctx, plugin, conf)
|
|
if plugin.handler._go then
|
|
ctx.ran_go_plugin = true
|
|
end
|
|
|
|
kong_global.set_named_ctx(kong, "plugin", plugin.handler, ctx)
|
|
kong_global.set_namespaced_log(kong, plugin.name, ctx)
|
|
ctx.plugin_id = conf.__plugin_id
|
|
end
|
|
|
|
|
|
local function reset_plugin_context(ctx, old_ws)
|
|
kong_global.reset_log(kong, ctx)
|
|
|
|
if old_ws then
|
|
ctx.workspace = old_ws
|
|
end
|
|
end
|
|
|
|
|
|
local function execute_init_worker_plugins_iterator(plugins_iterator, ctx)
|
|
local iterator, plugins = plugins_iterator:get_init_worker_iterator()
|
|
if not iterator then
|
|
return
|
|
end
|
|
|
|
local errors
|
|
|
|
for _, plugin in iterator, plugins, 0 do
|
|
kong_global.set_namespaced_log(kong, plugin.name, ctx)
|
|
|
|
-- guard against failed handler in "init_worker" phase only because it will
|
|
-- cause Kong to not correctly initialize and can not be recovered automatically.
|
|
local ok, err = pcall(plugin.handler.init_worker, plugin.handler)
|
|
if not ok then
|
|
errors = errors or {}
|
|
errors[#errors + 1] = {
|
|
plugin = plugin.name,
|
|
err = err,
|
|
}
|
|
end
|
|
|
|
kong_global.reset_log(kong, ctx)
|
|
end
|
|
|
|
return errors
|
|
end
|
|
|
|
|
|
local function execute_global_plugins_iterator(plugins_iterator, phase, ctx)
|
|
if not plugins_iterator.has_plugins then
|
|
return
|
|
end
|
|
|
|
local iterator, plugins = plugins_iterator:get_global_iterator(phase)
|
|
if not iterator then
|
|
return
|
|
end
|
|
|
|
local old_ws = ctx.workspace
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:plugin_iterator")
|
|
end
|
|
|
|
for _, plugin, configuration in iterator, plugins, 0 do
|
|
local span
|
|
if phase == "rewrite" then
|
|
span = instrumentation.plugin_rewrite(plugin)
|
|
end
|
|
|
|
setup_plugin_context(ctx, plugin, configuration)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:plugin", plugin.name, ctx.plugin_id)
|
|
end
|
|
|
|
plugin.handler[phase](plugin.handler, configuration)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:plugin")
|
|
end
|
|
|
|
reset_plugin_context(ctx, old_ws)
|
|
|
|
if span then
|
|
span:finish()
|
|
end
|
|
end
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:plugin_iterator")
|
|
end
|
|
end
|
|
|
|
|
|
local function execute_collecting_plugins_iterator(plugins_iterator, phase, ctx)
|
|
if not plugins_iterator.has_plugins then
|
|
return
|
|
end
|
|
|
|
local iterator, plugins = plugins_iterator:get_collecting_iterator(ctx)
|
|
if not iterator then
|
|
return
|
|
end
|
|
|
|
ctx.delay_response = true
|
|
|
|
local old_ws = ctx.workspace
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:plugin_iterator")
|
|
end
|
|
|
|
for _, plugin, configuration in iterator, plugins, 0 do
|
|
if not ctx.delayed_response then
|
|
local span
|
|
if phase == "access" then
|
|
span = instrumentation.plugin_access(plugin)
|
|
end
|
|
|
|
setup_plugin_context(ctx, plugin, configuration)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook( "timing", "before:plugin", plugin.name, ctx.plugin_id)
|
|
end
|
|
|
|
local co = coroutine.create(plugin.handler[phase])
|
|
local cok, cerr = coroutine.resume(co, plugin.handler, configuration)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:plugin")
|
|
end
|
|
|
|
if not cok then
|
|
-- set tracing error
|
|
if span then
|
|
span:record_error(cerr)
|
|
span:set_status(2)
|
|
end
|
|
|
|
kong.log.err(cerr)
|
|
ctx.delayed_response = {
|
|
status_code = 500,
|
|
content = { message = "An unexpected error occurred" },
|
|
}
|
|
|
|
-- plugin that throws runtime exception should be marked as `error`
|
|
ctx.KONG_UNEXPECTED = true
|
|
end
|
|
|
|
reset_plugin_context(ctx, old_ws)
|
|
|
|
-- ends tracing span
|
|
if span then
|
|
span:finish()
|
|
end
|
|
end
|
|
end
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:plugin_iterator")
|
|
end
|
|
|
|
ctx.delay_response = nil
|
|
end
|
|
|
|
|
|
local function execute_collected_plugins_iterator(plugins_iterator, phase, ctx)
|
|
if not plugins_iterator.has_plugins then
|
|
return
|
|
end
|
|
|
|
local iterator, plugins = plugins_iterator:get_collected_iterator(phase, ctx)
|
|
if not iterator then
|
|
return
|
|
end
|
|
|
|
local old_ws = ctx.workspace
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:plugin_iterator")
|
|
end
|
|
|
|
for _, plugin, configuration in iterator, plugins, 0 do
|
|
local span
|
|
if phase == "header_filter" then
|
|
span = instrumentation.plugin_header_filter(plugin)
|
|
end
|
|
|
|
setup_plugin_context(ctx, plugin, configuration)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:plugin", plugin.name, ctx.plugin_id)
|
|
end
|
|
|
|
plugin.handler[phase](plugin.handler, configuration)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:plugin")
|
|
end
|
|
|
|
reset_plugin_context(ctx, old_ws)
|
|
|
|
if span then
|
|
span:finish()
|
|
end
|
|
end
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:plugin_iterator")
|
|
end
|
|
end
|
|
|
|
|
|
local function execute_cache_warmup(kong_config)
|
|
if is_dbless(kong_config) then
|
|
return true
|
|
end
|
|
|
|
if worker_id() == 0 then
|
|
local ok, err = cache_warmup.execute(kong_config.db_cache_warmup_entities)
|
|
if not ok then
|
|
return nil, err
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
local function flush_delayed_response(ctx)
|
|
ctx.delay_response = nil
|
|
ctx.buffered_proxying = nil
|
|
|
|
if type(ctx.delayed_response_callback) == "function" then
|
|
ctx.delayed_response_callback(ctx)
|
|
return -- avoid tail call
|
|
end
|
|
|
|
local dr = ctx.delayed_response
|
|
local message = dr.content and dr.content.message or dr.content
|
|
kong.response.error(dr.status_code, message, dr.headers)
|
|
end
|
|
|
|
|
|
local function has_declarative_config(kong_config)
|
|
local declarative_config = kong_config.declarative_config
|
|
local declarative_config_string = kong_config.declarative_config_string
|
|
|
|
return declarative_config or declarative_config_string,
|
|
declarative_config ~= nil, -- is filename
|
|
declarative_config_string ~= nil -- is string
|
|
end
|
|
|
|
|
|
local function parse_declarative_config(kong_config, dc)
|
|
local declarative_config, is_file, is_string = has_declarative_config(kong_config)
|
|
|
|
local entities, err, _, meta, hash
|
|
if not declarative_config then
|
|
-- return an empty configuration,
|
|
-- including only the default workspace
|
|
entities, _, _, meta, hash = dc:parse_table({ _format_version = "3.0" })
|
|
return entities, nil, meta, hash
|
|
end
|
|
|
|
if is_file then
|
|
entities, err, _, meta, hash = dc:parse_file(declarative_config)
|
|
|
|
elseif is_string then
|
|
entities, err, _, meta, hash = dc:parse_string(declarative_config)
|
|
end
|
|
|
|
if not entities then
|
|
if is_file then
|
|
return nil, "error parsing declarative config file " ..
|
|
declarative_config .. ":\n" .. err
|
|
|
|
elseif is_string then
|
|
return nil, "error parsing declarative string " ..
|
|
declarative_config .. ":\n" .. err
|
|
end
|
|
end
|
|
|
|
return entities, nil, meta, hash
|
|
end
|
|
|
|
|
|
local function declarative_init_build()
|
|
local default_ws = kong.db.workspaces:select_by_name("default")
|
|
kong.default_workspace = default_ws and default_ws.id or kong.default_workspace
|
|
|
|
local ok, err = runloop.build_plugins_iterator("init")
|
|
if not ok then
|
|
return nil, "error building initial plugins iterator: " .. err
|
|
end
|
|
|
|
ok, err = runloop.build_router("init")
|
|
if not ok then
|
|
return nil, "error building initial router: " .. err
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
local function load_declarative_config(kong_config, entities, meta, hash)
|
|
local opts = {
|
|
name = "declarative_config",
|
|
}
|
|
|
|
local kong_shm = ngx.shared.kong
|
|
local ok, err = concurrency.with_worker_mutex(opts, function()
|
|
local value = kong_shm:get(DECLARATIVE_LOAD_KEY)
|
|
if value then
|
|
return true
|
|
end
|
|
local ok, err = declarative.load_into_cache(entities, meta, hash)
|
|
if not ok then
|
|
return nil, err
|
|
end
|
|
|
|
if kong_config.declarative_config then
|
|
kong.log.notice("declarative config loaded from ",
|
|
kong_config.declarative_config)
|
|
end
|
|
|
|
ok, err = kong_shm:safe_set(DECLARATIVE_LOAD_KEY, true)
|
|
if not ok then
|
|
kong.log.warn("failed marking declarative_config as loaded: ", err)
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
if ok then
|
|
return declarative_init_build()
|
|
end
|
|
|
|
return nil, err
|
|
end
|
|
|
|
|
|
local function list_migrations(migtable)
|
|
local list = {}
|
|
for _, t in ipairs(migtable) do
|
|
local mignames = {}
|
|
for _, mig in ipairs(t.migrations) do
|
|
table.insert(mignames, mig.name)
|
|
end
|
|
table.insert(list, fmt("%s (%s)", t.subsystem, concat(mignames, ", ")))
|
|
end
|
|
return concat(list, " ")
|
|
end
|
|
|
|
|
|
-- Kong public context handlers.
|
|
-- @section kong_handlers
|
|
|
|
local Kong = {}
|
|
|
|
|
|
function Kong.init()
|
|
local pl_path = require "pl.path"
|
|
local conf_loader = require "kong.conf_loader"
|
|
|
|
-- check if kong global is the correct one
|
|
if not kong.version then
|
|
error("configuration error: make sure your template is not setting a " ..
|
|
"global named 'kong' (please use 'Kong' instead)")
|
|
end
|
|
|
|
-- retrieve kong_config
|
|
local conf_path = pl_path.join(ngx.config.prefix(), ".kong_env")
|
|
local config = assert(conf_loader(conf_path, nil, { from_kong_env = true }))
|
|
|
|
UPSTREAM_KEEPALIVE_POOL_SIZE = config.upstream_keepalive_pool_size
|
|
UPSTREAM_KEEPALIVE_IDLE_TIMEOUT = config.upstream_keepalive_idle_timeout
|
|
UPSTREAM_KEEPALIVE_MAX_REQUESTS = config.upstream_keepalive_max_requests
|
|
|
|
-- The dns client has been initialized in conf_loader, so we set it directly.
|
|
-- Other modules should use 'kong.dns' to avoid reinitialization.
|
|
kong.dns = assert(package.loaded["kong.resty.dns.client"])
|
|
|
|
reset_kong_shm(config)
|
|
|
|
-- special math.randomseed from kong.globalpatches not taking any argument.
|
|
-- Must only be called in the init or init_worker phases, to avoid
|
|
-- duplicated seeds.
|
|
math.randomseed()
|
|
|
|
kong_global.init_pdk(kong, config)
|
|
instrumentation.init(config)
|
|
wasm.init(config)
|
|
|
|
local db = assert(DB.new(config))
|
|
instrumentation.db_query(db.connector)
|
|
assert(db:init_connector())
|
|
|
|
-- check state of migration only if there is an external database
|
|
if not is_dbless(config) then
|
|
ngx_log(ngx_DEBUG, "checking database schema state")
|
|
local migrations_utils = require "kong.cmd.utils.migrations"
|
|
schema_state = assert(db:schema_state())
|
|
migrations_utils.check_state(schema_state)
|
|
|
|
if schema_state.missing_migrations or schema_state.pending_migrations then
|
|
if schema_state.missing_migrations then
|
|
ngx_log(ngx_WARN, "database is missing some migrations:\n",
|
|
schema_state.missing_migrations)
|
|
end
|
|
|
|
if schema_state.pending_migrations then
|
|
ngx_log(ngx_WARN, "database has pending migrations:\n",
|
|
schema_state.pending_migrations)
|
|
end
|
|
end
|
|
end
|
|
|
|
assert(db:connect())
|
|
|
|
kong.db = db
|
|
|
|
if config.proxy_ssl_enabled or config.stream_ssl_enabled then
|
|
certificate.init()
|
|
end
|
|
|
|
if is_http_module and (is_data_plane(config) or is_control_plane(config))
|
|
then
|
|
kong.clustering = require("kong.clustering").new(config)
|
|
|
|
if config.cluster_rpc then
|
|
kong.rpc = require("kong.clustering.rpc.manager").new(config, kong.node.get_id())
|
|
|
|
if config.cluster_rpc_sync then
|
|
kong.sync = require("kong.clustering.services.sync").new(db, is_control_plane(config))
|
|
kong.sync:init(kong.rpc)
|
|
end
|
|
end
|
|
end
|
|
|
|
assert(db.vaults:load_vault_schemas(config.loaded_vaults))
|
|
|
|
-- Load plugins as late as possible so that everything is set up
|
|
assert(db.plugins:load_plugin_schemas(config.loaded_plugins))
|
|
|
|
if is_stream_module then
|
|
stream_api.load_handlers()
|
|
end
|
|
|
|
if is_dbless(config) then
|
|
local dc, err = declarative.new_config(config)
|
|
if not dc then
|
|
error(err)
|
|
end
|
|
|
|
kong.db.declarative_config = dc
|
|
|
|
if is_http_module or
|
|
(#config.proxy_listeners == 0 and
|
|
#config.admin_listeners == 0 and
|
|
#config.status_listeners == 0)
|
|
then
|
|
declarative_entities, err, declarative_meta, declarative_hash =
|
|
parse_declarative_config(kong.configuration, dc)
|
|
|
|
if not declarative_entities then
|
|
error(err)
|
|
end
|
|
|
|
kong.vault.warmup(declarative_entities)
|
|
end
|
|
|
|
else
|
|
local default_ws = db.workspaces:select_by_name("default")
|
|
kong.default_workspace = default_ws and default_ws.id
|
|
|
|
local ok, err = runloop.build_plugins_iterator("init")
|
|
if not ok then
|
|
error("error building initial plugins: " .. tostring(err))
|
|
end
|
|
|
|
if not is_control_plane(config) then
|
|
assert(runloop.build_router("init"))
|
|
|
|
ok, err = wasm.check_enabled_filters()
|
|
if not ok then
|
|
error("[wasm]: " .. err)
|
|
end
|
|
end
|
|
|
|
ok, err = runloop.set_init_versions_in_cache()
|
|
if not ok then
|
|
error("error setting initial versions for router and plugins iterator in cache: " ..
|
|
tostring(err))
|
|
end
|
|
end
|
|
|
|
db:close()
|
|
|
|
require("resty.kong.var").patch_metatable()
|
|
|
|
-- NOTE: privileged_agent is disabled when rpc sync is on
|
|
if config.dedicated_config_processing and is_data_plane(config) and not kong.sync then
|
|
-- TODO: figure out if there is better value than 4096
|
|
-- 4096 is for the cocurrency of the lua-resty-timer-ng
|
|
local ok, err = process.enable_privileged_agent(4096)
|
|
if not ok then
|
|
error(err)
|
|
end
|
|
end
|
|
|
|
if config.request_debug and config.role ~= "control_plane" and is_http_module then
|
|
local token = config.request_debug_token or uuid()
|
|
|
|
local request_debug_token_file = pl_path.join(config.prefix,
|
|
constants.REQUEST_DEBUG_TOKEN_FILE)
|
|
|
|
if pl_path.exists(request_debug_token_file) then
|
|
local ok, err = pl_file.delete(request_debug_token_file)
|
|
if not ok then
|
|
ngx.log(ngx.ERR, "failed to delete old .request_debug_token file: ", err)
|
|
end
|
|
end
|
|
|
|
local ok, err = pl_file.write(request_debug_token_file, token)
|
|
if not ok then
|
|
ngx.log(ngx.ERR, "failed to write .request_debug_token file: ", err)
|
|
end
|
|
|
|
kong.request_debug_token = token
|
|
ngx.log(ngx.NOTICE,
|
|
constants.REQUEST_DEBUG_LOG_PREFIX,
|
|
" token for request debugging: ",
|
|
kong.request_debug_token)
|
|
end
|
|
end
|
|
|
|
|
|
function Kong.init_worker()
|
|
|
|
emmy_debugger.init()
|
|
|
|
local ctx = ngx.ctx
|
|
|
|
ctx.KONG_PHASE = PHASES.init_worker
|
|
|
|
-- special math.randomseed from kong.globalpatches not taking any argument.
|
|
-- Must only be called in the init or init_worker phases, to avoid
|
|
-- duplicated seeds.
|
|
math.randomseed()
|
|
|
|
|
|
-- setup timerng to _G.kong
|
|
kong.timer = _G.timerng
|
|
_G.timerng = nil
|
|
|
|
kong.timer:set_debug(kong.configuration.log_level == "debug")
|
|
kong.timer:start()
|
|
|
|
-- init DB
|
|
|
|
local ok, err = kong.db:init_worker()
|
|
if not ok then
|
|
stash_init_worker_error("failed to instantiate 'kong.db' module: " .. err)
|
|
return
|
|
end
|
|
|
|
if worker_id() == 0 and not is_dbless(kong.configuration) then
|
|
if schema_state.missing_migrations then
|
|
ngx_log(ngx_WARN, "missing migrations: ",
|
|
list_migrations(schema_state.missing_migrations))
|
|
end
|
|
|
|
if schema_state.pending_migrations then
|
|
ngx_log(ngx_INFO, "starting with pending migrations: ",
|
|
list_migrations(schema_state.pending_migrations))
|
|
end
|
|
end
|
|
|
|
schema_state = nil
|
|
|
|
local worker_events, err = kong_global.init_worker_events(kong.configuration)
|
|
if not worker_events then
|
|
stash_init_worker_error("failed to instantiate 'kong.worker_events' " ..
|
|
"module: " .. err)
|
|
return
|
|
end
|
|
kong.worker_events = worker_events
|
|
|
|
local cluster_events, err = kong_global.init_cluster_events(kong.configuration, kong.db)
|
|
if not cluster_events then
|
|
stash_init_worker_error("failed to instantiate 'kong.cluster_events' " ..
|
|
"module: " .. err)
|
|
return
|
|
end
|
|
kong.cluster_events = cluster_events
|
|
|
|
local cache, err = kong_global.init_cache(kong.configuration, cluster_events, worker_events)
|
|
if not cache then
|
|
stash_init_worker_error("failed to instantiate 'kong.cache' module: " ..
|
|
err)
|
|
return
|
|
end
|
|
kong.cache = cache
|
|
|
|
local core_cache, err = kong_global.init_core_cache(kong.configuration, cluster_events, worker_events)
|
|
if not core_cache then
|
|
stash_init_worker_error("failed to instantiate 'kong.core_cache' module: " ..
|
|
err)
|
|
return
|
|
end
|
|
kong.core_cache = core_cache
|
|
|
|
kong.db:set_events_handler(worker_events)
|
|
|
|
if kong.configuration.admin_gui_listeners then
|
|
kong.cache:invalidate_local(constants.ADMIN_GUI_KCONFIG_CACHE_KEY)
|
|
end
|
|
|
|
if kong.clustering then
|
|
local using_dedicated = kong.configuration.dedicated_config_processing
|
|
|
|
-- CP needs to support both v1 and v2 sync
|
|
-- v1 sync is only enabled for DP if v2 sync is unavailble
|
|
kong.clustering:init_worker()
|
|
|
|
-- see is_dp_worker_process() in clustering/utils.lua
|
|
if using_dedicated and process.type() == "privileged agent" then
|
|
return
|
|
end
|
|
end
|
|
|
|
kong.vault.init_worker()
|
|
|
|
kong.timing = kong_global.init_timing()
|
|
kong.timing.init_worker(kong.configuration.request_debug)
|
|
|
|
if is_dbless(kong.configuration) then
|
|
-- databases in LMDB need to be explicitly created, otherwise `get`
|
|
-- operations will return error instead of `nil`. This ensures the default
|
|
-- namespace always exists in the
|
|
local t = lmdb_txn.begin(1)
|
|
t:db_open(true)
|
|
ok, err = t:commit()
|
|
if not ok then
|
|
stash_init_worker_error("failed to create and open LMDB database: " .. err)
|
|
return
|
|
end
|
|
|
|
if not has_declarative_config(kong.configuration) and
|
|
declarative.get_current_hash() ~= nil then
|
|
-- if there is no declarative config set and a config is present in LMDB,
|
|
-- just build the router and plugins iterator
|
|
ngx_log(ngx_INFO, "found persisted lmdb config, loading...")
|
|
local ok, err = declarative_init_build()
|
|
if not ok then
|
|
stash_init_worker_error("failed to initialize declarative config: " .. err)
|
|
return
|
|
end
|
|
|
|
elseif declarative_entities then
|
|
|
|
ok, err = load_declarative_config(kong.configuration,
|
|
declarative_entities,
|
|
declarative_meta,
|
|
declarative_hash)
|
|
|
|
declarative_entities = nil
|
|
declarative_meta = nil
|
|
declarative_hash = nil
|
|
|
|
if not ok then
|
|
stash_init_worker_error("failed to load declarative config file: " .. err)
|
|
return
|
|
end
|
|
|
|
else
|
|
-- stream does not need to load declarative config again, just build
|
|
-- the router and plugins iterator
|
|
local ok, err = declarative_init_build()
|
|
if not ok then
|
|
stash_init_worker_error("failed to initialize declarative config: " .. err)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local is_not_control_plane = not is_control_plane(kong.configuration)
|
|
if is_not_control_plane then
|
|
ok, err = execute_cache_warmup(kong.configuration)
|
|
if not ok then
|
|
ngx_log(ngx_ERR, "failed to warm up the DB cache: ", err)
|
|
end
|
|
end
|
|
|
|
ok, err = runloop.update_plugins_iterator()
|
|
if not ok then
|
|
stash_init_worker_error("failed to build the plugins iterator: " .. err)
|
|
return
|
|
end
|
|
|
|
if is_not_control_plane then
|
|
ok, err = runloop.update_router()
|
|
if not ok then
|
|
stash_init_worker_error("failed to build the router: " .. err)
|
|
return
|
|
end
|
|
end
|
|
|
|
runloop.init_worker.before()
|
|
|
|
-- run plugins init_worker context
|
|
local plugins_iterator = runloop.get_plugins_iterator()
|
|
local errors = execute_init_worker_plugins_iterator(plugins_iterator, ctx)
|
|
if errors then
|
|
for _, e in ipairs(errors) do
|
|
local err = 'failed to execute the "init_worker" ' ..
|
|
'handler for plugin "' .. e.plugin ..'": ' .. e.err
|
|
stash_init_worker_error(err)
|
|
end
|
|
end
|
|
|
|
if is_not_control_plane then
|
|
plugin_servers.start()
|
|
end
|
|
|
|
-- rpc and sync
|
|
if is_http_module then
|
|
|
|
-- init rpc connection
|
|
if kong.rpc then
|
|
kong.rpc:init_worker()
|
|
end
|
|
|
|
-- init sync
|
|
-- should run after rpc init successfully
|
|
if kong.sync then
|
|
kong.sync:init_worker()
|
|
end
|
|
end
|
|
|
|
ok, err = wasm.init_worker()
|
|
if not ok then
|
|
err = "wasm nginx worker initialization failed: " .. tostring(err)
|
|
stash_init_worker_error(err)
|
|
return
|
|
end
|
|
|
|
plugins_iterator:configure(ctx)
|
|
end
|
|
|
|
|
|
function Kong.exit_worker()
|
|
if process.type() ~= "privileged agent" and not is_control_plane(kong.configuration) then
|
|
plugin_servers.stop()
|
|
end
|
|
end
|
|
|
|
|
|
function Kong.ssl_certificate()
|
|
-- Note: ctx here is for a connection (not for a single request)
|
|
local ctx = get_ctx_table(fetch_table(CTX_NS, CTX_NARR, CTX_NREC))
|
|
|
|
ctx.KONG_PHASE = PHASES.certificate
|
|
|
|
log_init_worker_errors(ctx)
|
|
|
|
-- this is the first phase to run on an HTTPS request
|
|
ctx.workspace = kong.default_workspace
|
|
|
|
certificate.execute()
|
|
local plugins_iterator = runloop.get_updated_plugins_iterator()
|
|
execute_global_plugins_iterator(plugins_iterator, "certificate", ctx)
|
|
|
|
-- TODO: do we want to keep connection context?
|
|
kong.table.clear(ngx.ctx)
|
|
end
|
|
|
|
function Kong.ssl_client_hello()
|
|
local ctx = get_ctx_table(fetch_table(CTX_NS, CTX_NARR, CTX_NREC))
|
|
ctx.KONG_PHASE = PHASES.client_hello
|
|
end
|
|
|
|
function Kong.preread()
|
|
local ctx = get_ctx_table(fetch_table(CTX_NS, CTX_NARR, CTX_NREC))
|
|
if not ctx.KONG_PROCESSING_START then
|
|
ctx.KONG_PROCESSING_START = get_start_time_ms()
|
|
end
|
|
|
|
if not ctx.KONG_PREREAD_START then
|
|
ctx.KONG_PREREAD_START = get_now_ms()
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.preread
|
|
|
|
log_init_worker_errors(ctx)
|
|
|
|
local preread_terminate = runloop.preread.before(ctx)
|
|
|
|
-- if proxying to a second layer TLS terminator is required
|
|
-- abort further execution and return back to Nginx
|
|
if preread_terminate then
|
|
return
|
|
end
|
|
|
|
local plugins_iterator = runloop.get_updated_plugins_iterator()
|
|
execute_collecting_plugins_iterator(plugins_iterator, "preread", ctx)
|
|
|
|
if ctx.delayed_response then
|
|
ctx.KONG_PREREAD_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_PREREAD_TIME = ctx.KONG_PREREAD_ENDED_AT - ctx.KONG_PREREAD_START
|
|
ctx.KONG_RESPONSE_LATENCY = ctx.KONG_PREREAD_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
|
|
return flush_delayed_response(ctx)
|
|
end
|
|
|
|
ctx.delay_response = nil
|
|
|
|
if not ctx.service then
|
|
ctx.KONG_PREREAD_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_PREREAD_TIME = ctx.KONG_PREREAD_ENDED_AT - ctx.KONG_PREREAD_START
|
|
ctx.KONG_RESPONSE_LATENCY = ctx.KONG_PREREAD_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
|
|
ngx_log(ngx_WARN, "no Service found with those values")
|
|
return ngx.exit(503)
|
|
end
|
|
|
|
runloop.preread.after(ctx)
|
|
|
|
ctx.KONG_PREREAD_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_PREREAD_TIME = ctx.KONG_PREREAD_ENDED_AT - ctx.KONG_PREREAD_START
|
|
|
|
-- we intent to proxy, though balancer may fail on that
|
|
ctx.KONG_PROXIED = true
|
|
end
|
|
|
|
|
|
function Kong.rewrite()
|
|
local proxy_mode = var.kong_proxy_mode
|
|
if proxy_mode == "grpc" or proxy_mode == "unbuffered" then
|
|
kong_resty_ctx.apply_ref() -- if kong_proxy_mode is gRPC/unbuffered, this is executing
|
|
local ctx = ngx.ctx -- after an internal redirect. Restore (and restash)
|
|
kong_resty_ctx.stash_ref(ctx) -- context to avoid re-executing phases
|
|
|
|
ctx.KONG_REWRITE_ENDED_AT = get_now_ms()
|
|
ctx.KONG_REWRITE_TIME = ctx.KONG_REWRITE_ENDED_AT - ctx.KONG_REWRITE_START
|
|
|
|
return
|
|
end
|
|
|
|
local is_https = var.https == "on"
|
|
local ctx
|
|
if is_https then
|
|
ctx = ngx.ctx
|
|
else
|
|
ctx = get_ctx_table(fetch_table(CTX_NS, CTX_NARR, CTX_NREC))
|
|
end
|
|
|
|
if not ctx.KONG_PROCESSING_START then
|
|
ctx.KONG_PROCESSING_START = get_start_time_ms()
|
|
end
|
|
|
|
if not ctx.KONG_REWRITE_START then
|
|
ctx.KONG_REWRITE_START = get_now_ms()
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.rewrite
|
|
local has_timing
|
|
|
|
req_dyn_hook_run_hook("timing:auth", "auth")
|
|
|
|
if req_dyn_hook_is_group_enabled("timing") then
|
|
ctx.has_timing = true
|
|
has_timing = true
|
|
end
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:rewrite")
|
|
end
|
|
|
|
kong_resty_ctx.stash_ref(ctx)
|
|
|
|
if not is_https then
|
|
log_init_worker_errors(ctx)
|
|
end
|
|
|
|
runloop.rewrite.before(ctx)
|
|
|
|
if not ctx.workspace then
|
|
ctx.workspace = kong.default_workspace
|
|
end
|
|
|
|
-- On HTTPS requests, the plugins iterator is already updated in the ssl_certificate phase
|
|
local plugins_iterator
|
|
if is_https then
|
|
plugins_iterator = runloop.get_plugins_iterator()
|
|
else
|
|
plugins_iterator = runloop.get_updated_plugins_iterator()
|
|
end
|
|
|
|
execute_global_plugins_iterator(plugins_iterator, "rewrite", ctx)
|
|
|
|
ctx.KONG_REWRITE_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_REWRITE_TIME = ctx.KONG_REWRITE_ENDED_AT - ctx.KONG_REWRITE_START
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:rewrite")
|
|
end
|
|
end
|
|
|
|
|
|
function Kong.access()
|
|
local ctx = ngx.ctx
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:access")
|
|
end
|
|
|
|
if not ctx.KONG_ACCESS_START then
|
|
ctx.KONG_ACCESS_START = get_now_ms()
|
|
|
|
if ctx.KONG_REWRITE_START and not ctx.KONG_REWRITE_ENDED_AT then
|
|
ctx.KONG_REWRITE_ENDED_AT = ctx.KONG_ACCESS_START
|
|
ctx.KONG_REWRITE_TIME = ctx.KONG_REWRITE_ENDED_AT - ctx.KONG_REWRITE_START
|
|
end
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.access
|
|
|
|
runloop.access.before(ctx)
|
|
|
|
local plugins_iterator = runloop.get_plugins_iterator()
|
|
|
|
execute_collecting_plugins_iterator(plugins_iterator, "access", ctx)
|
|
|
|
if ctx.delayed_response then
|
|
ctx.KONG_ACCESS_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT - ctx.KONG_ACCESS_START
|
|
ctx.KONG_RESPONSE_LATENCY = ctx.KONG_ACCESS_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:access")
|
|
end
|
|
|
|
return flush_delayed_response(ctx)
|
|
end
|
|
|
|
ctx.delay_response = nil
|
|
|
|
if not ctx.service then
|
|
ctx.KONG_ACCESS_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT - ctx.KONG_ACCESS_START
|
|
ctx.KONG_RESPONSE_LATENCY = ctx.KONG_ACCESS_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
|
|
ctx.buffered_proxying = nil
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:access")
|
|
end
|
|
|
|
return kong.response.error(503, "no Service found with those values")
|
|
end
|
|
|
|
runloop.wasm_attach(ctx)
|
|
runloop.access.after(ctx)
|
|
|
|
ctx.KONG_ACCESS_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT - ctx.KONG_ACCESS_START
|
|
|
|
-- we intent to proxy, though balancer may fail on that
|
|
ctx.KONG_PROXIED = true
|
|
|
|
|
|
if ctx.buffered_proxying then
|
|
local upgrade = var.upstream_upgrade or ""
|
|
if upgrade == "" then
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:access")
|
|
end
|
|
|
|
return Kong.response()
|
|
end
|
|
|
|
ngx_log(ngx_NOTICE, "response buffering was turned off: connection upgrade (", upgrade, ")")
|
|
|
|
ctx.buffered_proxying = nil
|
|
end
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:access")
|
|
end
|
|
end
|
|
|
|
|
|
function Kong.balancer()
|
|
local ctx = ngx.ctx
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:balancer")
|
|
end
|
|
|
|
-- This may be called multiple times, and no yielding here!
|
|
local now_ms = get_now_ms()
|
|
local now_ns = time_ns()
|
|
|
|
if not ctx.KONG_BALANCER_START then
|
|
ctx.KONG_BALANCER_START = now_ms
|
|
|
|
if is_stream_module then
|
|
if ctx.KONG_PREREAD_START and not ctx.KONG_PREREAD_ENDED_AT then
|
|
ctx.KONG_PREREAD_ENDED_AT = ctx.KONG_BALANCER_START
|
|
ctx.KONG_PREREAD_TIME = ctx.KONG_PREREAD_ENDED_AT -
|
|
ctx.KONG_PREREAD_START
|
|
end
|
|
|
|
else
|
|
if ctx.KONG_REWRITE_START and not ctx.KONG_REWRITE_ENDED_AT then
|
|
ctx.KONG_REWRITE_ENDED_AT = ctx.KONG_ACCESS_START or
|
|
ctx.KONG_BALANCER_START
|
|
ctx.KONG_REWRITE_TIME = ctx.KONG_REWRITE_ENDED_AT -
|
|
ctx.KONG_REWRITE_START
|
|
end
|
|
|
|
if ctx.KONG_ACCESS_START and not ctx.KONG_ACCESS_ENDED_AT then
|
|
ctx.KONG_ACCESS_ENDED_AT = ctx.KONG_BALANCER_START
|
|
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT -
|
|
ctx.KONG_ACCESS_START
|
|
end
|
|
end
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.balancer
|
|
|
|
local balancer_data = ctx.balancer_data
|
|
local tries = balancer_data.tries
|
|
local try_count = balancer_data.try_count
|
|
local current_try = table_new(0, 4)
|
|
|
|
try_count = try_count + 1
|
|
balancer_data.try_count = try_count
|
|
tries[try_count] = current_try
|
|
|
|
current_try.balancer_start = now_ms
|
|
current_try.balancer_start_ns = now_ns
|
|
|
|
if try_count > 1 then
|
|
-- only call balancer on retry, first one is done in `runloop.access.after`
|
|
-- which runs in the ACCESS context and hence has less limitations than
|
|
-- this BALANCER context where the retries are executed
|
|
|
|
-- record failure data
|
|
local previous_try = tries[try_count - 1]
|
|
previous_try.state, previous_try.code = get_last_failure()
|
|
|
|
-- Report HTTP status for health checks
|
|
local balancer_instance = balancer_data.balancer
|
|
if balancer_instance then
|
|
if previous_try.state == "failed" then
|
|
if previous_try.code == 504 then
|
|
balancer_instance.report_timeout(balancer_data.balancer_handle)
|
|
else
|
|
balancer_instance.report_tcp_failure(balancer_data.balancer_handle)
|
|
end
|
|
|
|
else
|
|
balancer_instance.report_http_status(balancer_data.balancer_handle,
|
|
previous_try.code)
|
|
end
|
|
end
|
|
|
|
local ok, err, errcode = balancer.execute(balancer_data, ctx)
|
|
if not ok then
|
|
ngx_log(ngx_ERR, "failed to retry the dns/balancer resolver for ",
|
|
tostring(balancer_data.host), "' with: ", tostring(err))
|
|
|
|
ctx.KONG_BALANCER_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_BALANCER_START
|
|
ctx.KONG_PROXY_LATENCY = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:balancer")
|
|
end
|
|
|
|
return ngx.exit(errcode)
|
|
end
|
|
|
|
if is_http_module then
|
|
ok, err = balancer.set_host_header(balancer_data, var.upstream_scheme, var.upstream_host, true)
|
|
if not ok then
|
|
ngx_log(ngx_ERR, "failed to set balancer Host header: ", err)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:balancer")
|
|
end
|
|
|
|
return ngx.exit(500)
|
|
end
|
|
end
|
|
|
|
else
|
|
-- first try, so set the max number of retries
|
|
local retries = balancer_data.retries
|
|
if retries > 0 then
|
|
set_more_tries(retries)
|
|
end
|
|
end
|
|
|
|
local balancer_data_ip = balancer_data.ip
|
|
local balancer_data_port = balancer_data.port
|
|
|
|
local pool
|
|
if UPSTREAM_KEEPALIVE_POOL_SIZE > 0 and is_http_module then
|
|
if balancer_data.scheme == "https" then
|
|
-- upstream_host is SNI
|
|
local service = ctx.service
|
|
if service then
|
|
pool = fmt("%s|%s|%s|%s|%s|%s|%s",
|
|
balancer_data_ip,
|
|
balancer_data_port,
|
|
var.upstream_host,
|
|
service.tls_verify and "1" or "0",
|
|
service.tls_verify_depth or "",
|
|
service.ca_certificates and concat(service.ca_certificates, ",") or "",
|
|
service.client_certificate and service.client_certificate.id or "")
|
|
|
|
else
|
|
pool = balancer_data_ip .. "|" .. balancer_data_port .. "|" .. var.upstream_host
|
|
end
|
|
|
|
else
|
|
pool = balancer_data_ip .. "|" .. balancer_data_port
|
|
end
|
|
end
|
|
|
|
current_try.ip = balancer_data_ip
|
|
current_try.port = balancer_data_port
|
|
|
|
-- set the targets as resolved
|
|
ngx_log(ngx_DEBUG, "setting address (try ", try_count, "): ",
|
|
balancer_data_ip, ":", balancer_data_port)
|
|
local ok, err = set_current_peer(balancer_data_ip, balancer_data_port, pool)
|
|
if not ok then
|
|
ngx_log(ngx_ERR, "failed to set the current peer (address: ",
|
|
tostring(balancer_data_ip), " port: ", tostring(balancer_data_port),
|
|
"): ", tostring(err))
|
|
|
|
ctx.KONG_BALANCER_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_BALANCER_START
|
|
ctx.KONG_PROXY_LATENCY = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:balancer")
|
|
end
|
|
|
|
return ngx.exit(500)
|
|
end
|
|
|
|
ok, err = set_timeouts(balancer_data.connect_timeout / 1000,
|
|
balancer_data.send_timeout / 1000,
|
|
balancer_data.read_timeout / 1000)
|
|
if not ok then
|
|
ngx_log(ngx_ERR, "could not set upstream timeouts: ", err)
|
|
end
|
|
|
|
if pool then
|
|
ok, err = enable_keepalive(UPSTREAM_KEEPALIVE_IDLE_TIMEOUT, UPSTREAM_KEEPALIVE_MAX_REQUESTS)
|
|
if not ok then
|
|
ngx_log(ngx_ERR, "could not enable connection keepalive: ", err)
|
|
end
|
|
|
|
ngx_log(ngx_DEBUG, "enabled connection keepalive (pool=", pool,
|
|
", pool_size=", UPSTREAM_KEEPALIVE_POOL_SIZE,
|
|
", idle_timeout=", UPSTREAM_KEEPALIVE_IDLE_TIMEOUT,
|
|
", max_requests=", UPSTREAM_KEEPALIVE_MAX_REQUESTS, ")")
|
|
end
|
|
|
|
-- record overall latency
|
|
ctx.KONG_BALANCER_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_BALANCER_START
|
|
|
|
-- record try-latency
|
|
local try_latency = ctx.KONG_BALANCER_ENDED_AT - current_try.balancer_start
|
|
current_try.balancer_latency = try_latency
|
|
current_try.balancer_latency_ns = time_ns() - current_try.balancer_start_ns
|
|
|
|
-- time spent in Kong before sending the request to upstream
|
|
-- start_time() is kept in seconds with millisecond resolution.
|
|
ctx.KONG_PROXY_LATENCY = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:balancer")
|
|
end
|
|
end
|
|
|
|
|
|
do
|
|
local HTTP_METHODS = {
|
|
GET = ngx.HTTP_GET,
|
|
HEAD = ngx.HTTP_HEAD,
|
|
PUT = ngx.HTTP_PUT,
|
|
POST = ngx.HTTP_POST,
|
|
DELETE = ngx.HTTP_DELETE,
|
|
OPTIONS = ngx.HTTP_OPTIONS,
|
|
MKCOL = ngx.HTTP_MKCOL,
|
|
COPY = ngx.HTTP_COPY,
|
|
MOVE = ngx.HTTP_MOVE,
|
|
PROPFIND = ngx.HTTP_PROPFIND,
|
|
PROPPATCH = ngx.HTTP_PROPPATCH,
|
|
LOCK = ngx.HTTP_LOCK,
|
|
UNLOCK = ngx.HTTP_UNLOCK,
|
|
PATCH = ngx.HTTP_PATCH,
|
|
TRACE = ngx.HTTP_TRACE,
|
|
}
|
|
|
|
function Kong.response()
|
|
local ctx = ngx.ctx
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:response")
|
|
end
|
|
|
|
local plugins_iterator = runloop.get_plugins_iterator()
|
|
|
|
-- buffered proxying (that also executes the balancer)
|
|
ngx.req.read_body()
|
|
|
|
local options = {
|
|
always_forward_body = true,
|
|
share_all_vars = true,
|
|
method = HTTP_METHODS[ngx.req.get_method()],
|
|
ctx = ctx,
|
|
}
|
|
|
|
local res = ngx.location.capture("/kong_buffered_http", options)
|
|
if res.truncated and options.method ~= ngx.HTTP_HEAD then
|
|
ctx.KONG_PHASE = PHASES.error
|
|
ngx.status = res.status or 502
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:response")
|
|
end
|
|
|
|
return kong_error_handlers(ctx)
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.response
|
|
|
|
local status = res.status
|
|
local headers = res.header
|
|
local body = res.body
|
|
|
|
ctx.buffered_status = status
|
|
ctx.buffered_headers = headers
|
|
ctx.buffered_body = body
|
|
|
|
-- fake response phase (this runs after the balancer)
|
|
if not ctx.KONG_RESPONSE_START then
|
|
ctx.KONG_RESPONSE_START = get_now_ms()
|
|
|
|
if ctx.KONG_BALANCER_START and not ctx.KONG_BALANCER_ENDED_AT then
|
|
ctx.KONG_BALANCER_ENDED_AT = ctx.KONG_RESPONSE_START
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT -
|
|
ctx.KONG_BALANCER_START
|
|
end
|
|
end
|
|
|
|
if not ctx.KONG_WAITING_TIME then
|
|
ctx.KONG_WAITING_TIME = ctx.KONG_RESPONSE_START -
|
|
(ctx.KONG_BALANCER_ENDED_AT or ctx.KONG_ACCESS_ENDED_AT)
|
|
end
|
|
|
|
if not ctx.KONG_PROXY_LATENCY then
|
|
ctx.KONG_PROXY_LATENCY = ctx.KONG_RESPONSE_START - ctx.KONG_PROCESSING_START
|
|
end
|
|
|
|
if not ctx.KONG_UPSTREAM_DNS_TIME and ctx.KONG_UPSTREAM_DNS_END_AT and ctx.KONG_UPSTREAM_DNS_START then
|
|
ctx.KONG_UPSTREAM_DNS_TIME = ctx.KONG_UPSTREAM_DNS_END_AT - ctx.KONG_UPSTREAM_DNS_START
|
|
else
|
|
ctx.KONG_UPSTREAM_DNS_TIME = 0
|
|
end
|
|
|
|
kong.response.set_status(status)
|
|
kong.response.set_headers(headers)
|
|
|
|
execute_collected_plugins_iterator(plugins_iterator, "response", ctx)
|
|
|
|
ctx.KONG_RESPONSE_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_RESPONSE_TIME = ctx.KONG_RESPONSE_ENDED_AT - ctx.KONG_RESPONSE_START
|
|
|
|
-- buffered response
|
|
ngx.print(body)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:response")
|
|
end
|
|
|
|
-- jump over the balancer to header_filter
|
|
ngx.exit(status)
|
|
end
|
|
end
|
|
|
|
|
|
function Kong.header_filter()
|
|
local ctx = ngx.ctx
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:header_filter")
|
|
end
|
|
|
|
if not ctx.KONG_PROCESSING_START then
|
|
ctx.KONG_PROCESSING_START = get_start_time_ms()
|
|
end
|
|
|
|
if not ctx.workspace then
|
|
ctx.workspace = kong.default_workspace
|
|
end
|
|
|
|
if not ctx.KONG_HEADER_FILTER_START then
|
|
ctx.KONG_HEADER_FILTER_START = get_now_ms()
|
|
|
|
if ctx.KONG_REWRITE_START and not ctx.KONG_REWRITE_ENDED_AT then
|
|
ctx.KONG_REWRITE_ENDED_AT = ctx.KONG_BALANCER_START or
|
|
ctx.KONG_ACCESS_START or
|
|
ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START
|
|
ctx.KONG_REWRITE_TIME = ctx.KONG_REWRITE_ENDED_AT -
|
|
ctx.KONG_REWRITE_START
|
|
end
|
|
|
|
if ctx.KONG_ACCESS_START and not ctx.KONG_ACCESS_ENDED_AT then
|
|
ctx.KONG_ACCESS_ENDED_AT = ctx.KONG_BALANCER_START or
|
|
ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START
|
|
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT -
|
|
ctx.KONG_ACCESS_START
|
|
end
|
|
|
|
if ctx.KONG_BALANCER_START and not ctx.KONG_BALANCER_ENDED_AT then
|
|
ctx.KONG_BALANCER_ENDED_AT = ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT -
|
|
ctx.KONG_BALANCER_START
|
|
end
|
|
|
|
if ctx.KONG_RESPONSE_START and not ctx.KONG_RESPONSE_ENDED_AT then
|
|
ctx.KONG_RESPONSE_ENDED_AT = ctx.KONG_HEADER_FILTER_START
|
|
ctx.KONG_RESPONSE_TIME = ctx.KONG_RESPONSE_ENDED_AT -
|
|
ctx.KONG_RESPONSE_START
|
|
end
|
|
end
|
|
|
|
if ctx.KONG_PROXIED then
|
|
if not ctx.KONG_WAITING_TIME then
|
|
ctx.KONG_WAITING_TIME = (ctx.KONG_RESPONSE_START or ctx.KONG_HEADER_FILTER_START) -
|
|
(ctx.KONG_BALANCER_ENDED_AT or ctx.KONG_ACCESS_ENDED_AT)
|
|
end
|
|
|
|
if not ctx.KONG_PROXY_LATENCY then
|
|
ctx.KONG_PROXY_LATENCY = (ctx.KONG_RESPONSE_START or ctx.KONG_HEADER_FILTER_START) -
|
|
ctx.KONG_PROCESSING_START
|
|
end
|
|
|
|
elseif not ctx.KONG_RESPONSE_LATENCY then
|
|
ctx.KONG_RESPONSE_LATENCY = (ctx.KONG_RESPONSE_START or ctx.KONG_HEADER_FILTER_START) -
|
|
ctx.KONG_PROCESSING_START
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.header_filter
|
|
|
|
runloop.header_filter.before(ctx)
|
|
local plugins_iterator = runloop.get_plugins_iterator()
|
|
execute_collected_plugins_iterator(plugins_iterator, "header_filter", ctx)
|
|
runloop.header_filter.after(ctx)
|
|
|
|
ctx.KONG_HEADER_FILTER_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_HEADER_FILTER_TIME = ctx.KONG_HEADER_FILTER_ENDED_AT - ctx.KONG_HEADER_FILTER_START
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:header_filter")
|
|
end
|
|
end
|
|
|
|
|
|
function Kong.body_filter()
|
|
local ctx = ngx.ctx
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:body_filter")
|
|
end
|
|
|
|
if not ctx.KONG_BODY_FILTER_START then
|
|
ctx.KONG_BODY_FILTER_START = get_now_ms()
|
|
|
|
if ctx.KONG_REWRITE_START and not ctx.KONG_REWRITE_ENDED_AT then
|
|
ctx.KONG_REWRITE_ENDED_AT = ctx.KONG_ACCESS_START or
|
|
ctx.KONG_BALANCER_START or
|
|
ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START or
|
|
ctx.KONG_BODY_FILTER_START
|
|
ctx.KONG_REWRITE_TIME = ctx.KONG_REWRITE_ENDED_AT -
|
|
ctx.KONG_REWRITE_START
|
|
end
|
|
|
|
if ctx.KONG_ACCESS_START and not ctx.KONG_ACCESS_ENDED_AT then
|
|
ctx.KONG_ACCESS_ENDED_AT = ctx.KONG_BALANCER_START or
|
|
ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START or
|
|
ctx.KONG_BODY_FILTER_START
|
|
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT -
|
|
ctx.KONG_ACCESS_START
|
|
end
|
|
|
|
if ctx.KONG_BALANCER_START and not ctx.KONG_BALANCER_ENDED_AT then
|
|
ctx.KONG_BALANCER_ENDED_AT = ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START or
|
|
ctx.KONG_BODY_FILTER_START
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT -
|
|
ctx.KONG_BALANCER_START
|
|
end
|
|
|
|
if ctx.KONG_RESPONSE_START and not ctx.KONG_RESPONSE_ENDED_AT then
|
|
ctx.KONG_RESPONSE_ENDED_AT = ctx.KONG_HEADER_FILTER_START or
|
|
ctx.KONG_BODY_FILTER_START
|
|
ctx.KONG_RESPONSE_TIME = ctx.KONG_RESPONSE_ENDED_AT -
|
|
ctx.KONG_RESPONSE_START
|
|
end
|
|
|
|
if ctx.KONG_HEADER_FILTER_START and not ctx.KONG_HEADER_FILTER_ENDED_AT then
|
|
ctx.KONG_HEADER_FILTER_ENDED_AT = ctx.KONG_BODY_FILTER_START
|
|
ctx.KONG_HEADER_FILTER_TIME = ctx.KONG_HEADER_FILTER_ENDED_AT -
|
|
ctx.KONG_HEADER_FILTER_START
|
|
end
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.body_filter
|
|
|
|
if ctx.response_body then
|
|
arg[1] = ctx.response_body
|
|
arg[2] = true
|
|
end
|
|
|
|
local plugins_iterator = runloop.get_plugins_iterator()
|
|
execute_collected_plugins_iterator(plugins_iterator, "body_filter", ctx)
|
|
|
|
if not arg[2] then
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:body_filter")
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
ctx.KONG_BODY_FILTER_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_BODY_FILTER_ENDED_AT_NS = time_ns()
|
|
ctx.KONG_BODY_FILTER_TIME = ctx.KONG_BODY_FILTER_ENDED_AT - ctx.KONG_BODY_FILTER_START
|
|
|
|
if ctx.KONG_PROXIED then
|
|
-- time spent receiving the response ((response +) header_filter + body_filter)
|
|
-- we could use $upstream_response_time but we need to distinguish the waiting time
|
|
-- from the receiving time in our logging plugins (especially ALF serializer).
|
|
ctx.KONG_RECEIVE_TIME = ctx.KONG_BODY_FILTER_ENDED_AT - (ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START or
|
|
ctx.KONG_BALANCER_ENDED_AT or
|
|
ctx.KONG_BALANCER_START or
|
|
ctx.KONG_ACCESS_ENDED_AT)
|
|
end
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:body_filter")
|
|
end
|
|
end
|
|
|
|
|
|
function Kong.log()
|
|
local ctx = ngx.ctx
|
|
local has_timing = ctx.has_timing
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "before:log")
|
|
end
|
|
|
|
if not ctx.KONG_LOG_START then
|
|
ctx.KONG_LOG_START = get_now_ms()
|
|
ctx.KONG_LOG_START_NS = time_ns()
|
|
if is_stream_module then
|
|
if not ctx.KONG_PROCESSING_START then
|
|
ctx.KONG_PROCESSING_START = get_start_time_ms()
|
|
end
|
|
|
|
if ctx.KONG_PREREAD_START and not ctx.KONG_PREREAD_ENDED_AT then
|
|
ctx.KONG_PREREAD_ENDED_AT = ctx.KONG_LOG_START
|
|
ctx.KONG_PREREAD_TIME = ctx.KONG_PREREAD_ENDED_AT -
|
|
ctx.KONG_PREREAD_START
|
|
end
|
|
|
|
if ctx.KONG_BALANCER_START and not ctx.KONG_BALANCER_ENDED_AT then
|
|
ctx.KONG_BALANCER_ENDED_AT = ctx.KONG_LOG_START
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT -
|
|
ctx.KONG_BALANCER_START
|
|
end
|
|
|
|
if ctx.KONG_PROXIED then
|
|
if not ctx.KONG_PROXY_LATENCY then
|
|
ctx.KONG_PROXY_LATENCY = ctx.KONG_LOG_START -
|
|
ctx.KONG_PROCESSING_START
|
|
end
|
|
|
|
elseif not ctx.KONG_RESPONSE_LATENCY then
|
|
ctx.KONG_RESPONSE_LATENCY = ctx.KONG_LOG_START -
|
|
ctx.KONG_PROCESSING_START
|
|
end
|
|
|
|
else
|
|
if ctx.KONG_REWRITE_START and not ctx.KONG_REWRITE_ENDED_AT then
|
|
ctx.KONG_REWRITE_ENDED_AT = ctx.KONG_ACCESS_START or
|
|
ctx.KONG_BALANCER_START or
|
|
ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START or
|
|
ctx.BODY_FILTER_START or
|
|
ctx.KONG_LOG_START
|
|
ctx.KONG_REWRITE_TIME = ctx.KONG_REWRITE_ENDED_AT -
|
|
ctx.KONG_REWRITE_START
|
|
end
|
|
|
|
if ctx.KONG_ACCESS_START and not ctx.KONG_ACCESS_ENDED_AT then
|
|
ctx.KONG_ACCESS_ENDED_AT = ctx.KONG_BALANCER_START or
|
|
ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START or
|
|
ctx.BODY_FILTER_START or
|
|
ctx.KONG_LOG_START
|
|
ctx.KONG_ACCESS_TIME = ctx.KONG_ACCESS_ENDED_AT -
|
|
ctx.KONG_ACCESS_START
|
|
end
|
|
|
|
if ctx.KONG_BALANCER_START and not ctx.KONG_BALANCER_ENDED_AT then
|
|
ctx.KONG_BALANCER_ENDED_AT = ctx.KONG_RESPONSE_START or
|
|
ctx.KONG_HEADER_FILTER_START or
|
|
ctx.BODY_FILTER_START or
|
|
ctx.KONG_LOG_START
|
|
ctx.KONG_BALANCER_TIME = ctx.KONG_BALANCER_ENDED_AT -
|
|
ctx.KONG_BALANCER_START
|
|
end
|
|
|
|
if ctx.KONG_HEADER_FILTER_START and not ctx.KONG_HEADER_FILTER_ENDED_AT then
|
|
ctx.KONG_HEADER_FILTER_ENDED_AT = ctx.BODY_FILTER_START or
|
|
ctx.KONG_LOG_START
|
|
ctx.KONG_HEADER_FILTER_TIME = ctx.KONG_HEADER_FILTER_ENDED_AT -
|
|
ctx.KONG_HEADER_FILTER_START
|
|
end
|
|
|
|
if ctx.KONG_BODY_FILTER_START and not ctx.KONG_BODY_FILTER_ENDED_AT then
|
|
ctx.KONG_BODY_FILTER_ENDED_AT = ctx.KONG_LOG_START
|
|
ctx.KONG_BODY_FILTER_ENDED_AT_NS = ctx.KONG_LOG_START_NS
|
|
ctx.KONG_BODY_FILTER_TIME = ctx.KONG_BODY_FILTER_ENDED_AT -
|
|
ctx.KONG_BODY_FILTER_START
|
|
end
|
|
|
|
if ctx.KONG_PROXIED and not ctx.KONG_WAITING_TIME then
|
|
ctx.KONG_WAITING_TIME = ctx.KONG_LOG_START -
|
|
(ctx.KONG_BALANCER_ENDED_AT or ctx.KONG_ACCESS_ENDED_AT)
|
|
end
|
|
end
|
|
end
|
|
|
|
ctx.KONG_PHASE = PHASES.log
|
|
|
|
runloop.log.before(ctx)
|
|
local plugins_iterator = runloop.get_plugins_iterator()
|
|
execute_collected_plugins_iterator(plugins_iterator, "log", ctx)
|
|
plugins_iterator.release(ctx)
|
|
runloop.log.after(ctx)
|
|
|
|
if has_timing then
|
|
req_dyn_hook_run_hook("timing", "after:log")
|
|
end
|
|
|
|
release_table(CTX_NS, ctx)
|
|
|
|
-- this is not used for now, but perhaps we need it later?
|
|
--ctx.KONG_LOG_ENDED_AT = get_now_ms()
|
|
--ctx.KONG_LOG_TIME = ctx.KONG_LOG_ENDED_AT - ctx.KONG_LOG_START
|
|
end
|
|
|
|
|
|
function Kong.handle_error()
|
|
kong_resty_ctx.apply_ref()
|
|
|
|
local ctx = ngx.ctx
|
|
ctx.KONG_PHASE = PHASES.error
|
|
ctx.KONG_UNEXPECTED = true
|
|
|
|
log_init_worker_errors(ctx)
|
|
|
|
return kong_error_handlers(ctx)
|
|
end
|
|
|
|
|
|
local function serve_content(module)
|
|
local ctx = ngx.ctx
|
|
ctx.KONG_PROCESSING_START = get_start_time_ms()
|
|
ctx.KONG_ADMIN_CONTENT_START = ctx.KONG_ADMIN_CONTENT_START or get_now_ms()
|
|
ctx.KONG_PHASE = PHASES.admin_api
|
|
|
|
log_init_worker_errors(ctx)
|
|
|
|
ngx.header["Access-Control-Allow-Origin"] = ngx.req.get_headers()["Origin"] or "*"
|
|
|
|
lapis.serve(module)
|
|
|
|
ctx.KONG_ADMIN_CONTENT_ENDED_AT = get_updated_now_ms()
|
|
ctx.KONG_ADMIN_CONTENT_TIME = ctx.KONG_ADMIN_CONTENT_ENDED_AT - ctx.KONG_ADMIN_CONTENT_START
|
|
ctx.KONG_ADMIN_LATENCY = ctx.KONG_ADMIN_CONTENT_ENDED_AT - ctx.KONG_PROCESSING_START
|
|
end
|
|
|
|
|
|
function Kong.admin_content()
|
|
kong.worker_events.poll()
|
|
|
|
local ctx = ngx.ctx
|
|
if not ctx.workspace then
|
|
ctx.workspace = kong.default_workspace
|
|
end
|
|
|
|
return serve_content("kong.api")
|
|
end
|
|
|
|
|
|
function Kong.admin_header_filter()
|
|
local ctx = ngx.ctx
|
|
|
|
if not ctx.KONG_PROCESSING_START then
|
|
ctx.KONG_PROCESSING_START = get_start_time_ms()
|
|
end
|
|
|
|
if not ctx.KONG_ADMIN_HEADER_FILTER_START then
|
|
ctx.KONG_ADMIN_HEADER_FILTER_START = get_now_ms()
|
|
|
|
if ctx.KONG_ADMIN_CONTENT_START and not ctx.KONG_ADMIN_CONTENT_ENDED_AT then
|
|
ctx.KONG_ADMIN_CONTENT_ENDED_AT = ctx.KONG_ADMIN_HEADER_FILTER_START
|
|
ctx.KONG_ADMIN_CONTENT_TIME = ctx.KONG_ADMIN_CONTENT_ENDED_AT - ctx.KONG_ADMIN_CONTENT_START
|
|
end
|
|
|
|
if not ctx.KONG_ADMIN_LATENCY then
|
|
ctx.KONG_ADMIN_LATENCY = ctx.KONG_ADMIN_HEADER_FILTER_START - ctx.KONG_PROCESSING_START
|
|
end
|
|
end
|
|
|
|
local enabled_headers = kong.configuration.enabled_headers
|
|
local headers = constants.HEADERS
|
|
|
|
if enabled_headers[headers.ADMIN_LATENCY] then
|
|
header[headers.ADMIN_LATENCY] = ctx.KONG_ADMIN_LATENCY
|
|
end
|
|
|
|
if enabled_headers[headers.SERVER] then
|
|
header[headers.SERVER] = meta._SERVER_TOKENS
|
|
|
|
else
|
|
header[headers.SERVER] = nil
|
|
end
|
|
|
|
-- this is not used for now, but perhaps we need it later?
|
|
--ctx.KONG_ADMIN_HEADER_FILTER_ENDED_AT = get_now_ms()
|
|
--ctx.KONG_ADMIN_HEADER_FILTER_TIME = ctx.KONG_ADMIN_HEADER_FILTER_ENDED_AT - ctx.KONG_ADMIN_HEADER_FILTER_START
|
|
end
|
|
|
|
function Kong.admin_gui_kconfig_content()
|
|
local content, err = kong.cache:get(
|
|
constants.ADMIN_GUI_KCONFIG_CACHE_KEY,
|
|
nil,
|
|
admin_gui.generate_kconfig,
|
|
kong.configuration
|
|
)
|
|
if err then
|
|
kong.log.err("error occurred while retrieving admin gui config `kconfig.js` from cache", err)
|
|
kong.response.exit(500, { message = "An unexpected error occurred" })
|
|
else
|
|
ngx.say(content)
|
|
end
|
|
end
|
|
|
|
function Kong.admin_gui_log()
|
|
if kong.configuration.anonymous_reports then
|
|
reports.admin_gui_log(ngx.ctx)
|
|
end
|
|
end
|
|
|
|
function Kong.status_content()
|
|
return serve_content("kong.status")
|
|
end
|
|
|
|
|
|
Kong.status_header_filter = Kong.admin_header_filter
|
|
|
|
|
|
function Kong.serve_cluster_listener()
|
|
local ctx = ngx.ctx
|
|
|
|
log_init_worker_errors(ctx)
|
|
|
|
ctx.KONG_PHASE = PHASES.cluster_listener
|
|
|
|
return kong.clustering:handle_cp_websocket()
|
|
end
|
|
|
|
|
|
function Kong.stream_api()
|
|
stream_api.handle()
|
|
end
|
|
|
|
|
|
function Kong.serve_cluster_rpc_listener()
|
|
local ctx = ngx.ctx
|
|
|
|
log_init_worker_errors(ctx)
|
|
|
|
ctx.KONG_PHASE = PHASES.cluster_listener
|
|
|
|
return kong.rpc:handle_websocket()
|
|
end
|
|
|
|
|
|
do
|
|
local events = require "kong.runloop.events"
|
|
Kong.stream_config_listener = events.stream_reconfigure_listener
|
|
end
|
|
|
|
|
|
return Kong
|