-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #708 from Mashape/refactor/resolver
[refactor/resolver] lighter kong.lua
- Loading branch information
Showing
30 changed files
with
428 additions
and
352 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
-- Kong core | ||
-- | ||
-- This consists of events than need to | ||
-- be ran at the very beginning and very end of the lua-nginx-module contexts. | ||
-- It mainly carries information related to a request from one context to the next one, | ||
-- through the `ngx.ctx` table. | ||
-- | ||
-- In the `access_by_lua` phase, it is responsible for retrieving the API being proxied by | ||
-- a Consumer. Then it is responsible for loading the plugins to execute on this request. | ||
-- | ||
-- In other phases, we create different variables and timers. | ||
-- Variables: | ||
-- `plugins_to_execute`: an array of plugin to be executed for this request. | ||
-- Timers: | ||
-- `KONG_<CONTEXT_NAME>_STARTED_AT`: time at which a given context is started to be executed by all Kong plugins. | ||
-- `KONG_<CONTEXT_NAME>_ENDED_AT`: time at which all plugins have been executed by Kong for this context. | ||
-- `KONG_<CONTEXT_NAME>_TIME`: time taken by Kong to execute all the plugins for this context | ||
-- | ||
-- @see https://github.com/openresty/lua-nginx-module#ngxctx | ||
|
||
local utils = require "kong.tools.utils" | ||
local reports = require "kong.core.reports" | ||
local stringy = require "stringy" | ||
local resolver = require "kong.core.resolver" | ||
local constants = require "kong.constants" | ||
local certificate = require "kong.core.certificate" | ||
|
||
local table_insert = table.insert | ||
local math_floor = math.floor | ||
|
||
local MULT = 10^3 | ||
local function round(num) | ||
return math_floor(num * MULT + 0.5) / MULT | ||
end | ||
|
||
return { | ||
init_worker = function() | ||
reports.init_worker() | ||
end, | ||
certificate = function() | ||
ngx.ctx.api = certificate.execute() | ||
end, | ||
access = { | ||
before = function() | ||
ngx.ctx.KONG_ACCESS_START = ngx.now() | ||
ngx.ctx.api, ngx.ctx.upstream_url = resolver.execute() | ||
end, | ||
-- Only executed if the `resolver` module found an API and allows nginx to proxy it. | ||
after = function() | ||
local now = ngx.now() | ||
ngx.ctx.KONG_ACCESS_TIME = now - ngx.ctx.KONG_ACCESS_START | ||
ngx.ctx.KONG_ACCESS_ENDED_AT = now | ||
ngx.ctx.KONG_PROXIED = true | ||
|
||
-- Append any querystring parameters modified during plugins execution | ||
local upstream_url = unpack(stringy.split(ngx.ctx.upstream_url, "?")) | ||
if utils.table_size(ngx.req.get_uri_args()) > 0 then | ||
upstream_url = upstream_url.."?"..ngx.encode_args(ngx.req.get_uri_args()) | ||
end | ||
|
||
-- Set the `$upstream_url` variable for the `proxy_pass` nginx's directive. | ||
ngx.var.upstream_url = upstream_url | ||
end | ||
}, | ||
header_filter = { | ||
before = function() | ||
if ngx.ctx.KONG_PROXIED then | ||
ngx.ctx.KONG_HEADER_FILTER_STARTED_AT = ngx.now() | ||
end | ||
end, | ||
after = function() | ||
if ngx.ctx.KONG_PROXIED then | ||
local now = ngx.now() | ||
local proxy_started_at = ngx.ctx.KONG_ACCESS_ENDED_AT | ||
local proxy_ended_at = ngx.ctx.KONG_HEADER_FILTER_STARTED_AT | ||
local upstream_response_time = round(proxy_ended_at - proxy_started_at) | ||
local proxy_time = round(now - ngx.req.start_time() - upstream_response_time) | ||
|
||
ngx.ctx.KONG_HEADER_FILTER_TIME = now - ngx.ctx.KONG_HEADER_FILTER_STARTED_AT | ||
ngx.header[constants.HEADERS.UPSTREAM_LATENCY] = upstream_response_time * 1000 -- ms | ||
ngx.header[constants.HEADERS.PROXY_LATENCY] = proxy_time * 1000 -- ms | ||
ngx.header["Via"] = constants.NAME.."/"..constants.VERSION | ||
else | ||
ngx.header["Server"] = constants.NAME.."/"..constants.VERSION | ||
end | ||
end | ||
}, | ||
-- `body_filter_by_lua` can be executed mutiple times depending on the size of the | ||
-- response body. | ||
-- To compute the time spent in Kong, we keep an array of size n, | ||
-- n being the number of times the directive ran: | ||
-- starts = {4312, 5423, 4532} | ||
-- ends = {4320, 5430, 4550} | ||
-- time = 8 + 7 + 18 = 33 = total time spent in `body_filter` in all plugins | ||
body_filter = { | ||
before = function() | ||
if ngx.ctx.KONG_BODY_FILTER_STARTS == nil then | ||
ngx.ctx.KONG_BODY_FILTER_STARTS = {} | ||
ngx.ctx.KONG_BODY_FILTER_EDINGS = {} | ||
end | ||
table_insert(ngx.ctx.KONG_BODY_FILTER_STARTS, ngx.now()) | ||
end, | ||
after = function() | ||
table_insert(ngx.ctx.KONG_BODY_FILTER_EDINGS, ngx.now()) | ||
|
||
if ngx.arg[2] then | ||
-- compute time spent in Kong's body_filters | ||
local total_time = 0 | ||
for i in ipairs(ngx.ctx.KONG_BODY_FILTER_EDINGS) do | ||
total_time = total_time + (ngx.ctx.KONG_BODY_FILTER_EDINGS[i] - ngx.ctx.KONG_BODY_FILTER_STARTS[i]) | ||
end | ||
ngx.ctx.KONG_BODY_FILTER_TIME = total_time | ||
ngx.ctx.KONG_BODY_FILTER_STARTS = nil | ||
ngx.ctx.KONG_BODY_FILTER_EDINGS = nil | ||
end | ||
end | ||
}, | ||
log = function() | ||
reports.log() | ||
end | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
local cache = require "kong.tools.database_cache" | ||
local constants = require "kong.constants" | ||
local responses = require "kong.tools.responses" | ||
|
||
local table_remove = table.remove | ||
local table_insert = table.insert | ||
local ipairs = ipairs | ||
|
||
--- Load the configuration for a plugin entry in the DB. | ||
-- Given an API, a Consumer and a plugin name, retrieve the plugin's configuration if it exists. | ||
-- Results are cached in ngx.dict | ||
-- @param[type=string] api_id ID of the API being proxied. | ||
-- @param[type=string] consumer_id ID of the Consumer making the request (if any). | ||
-- @param[type=stirng] plugin_name Name of the plugin being tested for. | ||
-- @treturn table Plugin retrieved from the cache or database. | ||
local function load_plugin_configuration(api_id, consumer_id, plugin_name) | ||
local cache_key = cache.plugin_key(plugin_name, api_id, consumer_id) | ||
|
||
local plugin = cache.get_or_set(cache_key, function() | ||
local rows, err = dao.plugins:find_by_keys { | ||
api_id = api_id, | ||
consumer_id = consumer_id ~= nil and consumer_id or constants.DATABASE_NULL_ID, | ||
name = plugin_name | ||
} | ||
if err then | ||
return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) | ||
end | ||
|
||
if #rows > 0 then | ||
return table_remove(rows, 1) | ||
else | ||
-- insert a cached value to not trigger too many DB queries. | ||
-- for now, this will lock the cache for the expiraiton duration. | ||
return {null = true} | ||
end | ||
end) | ||
|
||
if plugin ~= nil and plugin.enabled then | ||
return plugin.config or {} | ||
end | ||
end | ||
|
||
local function load_plugins_for_req(loaded_plugins) | ||
if ngx.ctx.plugins_for_request == nil then | ||
local t = {} | ||
-- Build an array of plugins that must be executed for this particular request. | ||
-- A plugin is considered to be executed if there is a row in the DB which contains: | ||
-- 1. the API id (contained in ngx.ctx.api.id, retrived by the core resolver) | ||
-- 2. a Consumer id, in which case it overrides any previous plugin found in 1. | ||
-- this use case will be treated once the authentication plugins have run (access phase). | ||
-- Such a row will contain a `config` value, which is a table. | ||
if ngx.ctx.api ~= nil then | ||
for _, plugin in ipairs(loaded_plugins) do | ||
local plugin_configuration = load_plugin_configuration(ngx.ctx.api.id, nil, plugin.name) | ||
if plugin_configuration ~= nil then | ||
table_insert(t, {plugin, plugin_configuration}) | ||
end | ||
end | ||
end | ||
|
||
ngx.ctx.plugins_for_request = t | ||
end | ||
end | ||
|
||
--- Plugins for request iterator. | ||
-- Iterate over the plugin loaded for a request, stored in `ngx.ctx.plugins_for_request`. | ||
-- @param[type=string] context_name Name of the current nginx context. We don't use `ngx.get_phase()` simply because we can avoid it. | ||
-- @treturn function iterator | ||
local function iter_plugins_for_req(loaded_plugins, context_name) | ||
-- In case previous contexts did not run, we need to handle | ||
-- the case when plugins have not been fetched for a given request. | ||
-- This will simply make it so the look gets skipped if no API is set in the context | ||
load_plugins_for_req(loaded_plugins) | ||
|
||
local i = 0 | ||
|
||
-- Iterate on plugins to execute for this request until | ||
-- a plugin with a handler for the given context is found. | ||
local function get_next() | ||
i = i + 1 | ||
local p = ngx.ctx.plugins_for_request[i] | ||
if p == nil then | ||
return | ||
end | ||
|
||
local plugin, plugin_configuration = p[1], p[2] | ||
if plugin.handler[context_name] == nil then | ||
ngx.log(ngx.DEBUG, "No handler for "..context_name.." phase on "..plugin.name.." plugin") | ||
return get_next() | ||
end | ||
|
||
return plugin, plugin_configuration | ||
end | ||
|
||
return function() | ||
local plugin, plugin_configuration = get_next() | ||
|
||
-- Check if any Consumer was authenticated during the access phase. | ||
-- If so, retrieve the configuration for this Consumer which overrides | ||
-- the API-wide configuration. | ||
if plugin ~= nil and context_name == "access" then | ||
local consumer_id = ngx.ctx.authenticated_credential and ngx.ctx.authenticated_credential.consumer_id or nil | ||
if consumer_id ~= nil then | ||
local consumer_plugin_configuration = load_plugin_configuration(ngx.ctx.api.id, consumer_id, plugin.name) | ||
if consumer_plugin_configuration ~= nil then | ||
-- This Consumer has a special configuration when this plugin gets executed. | ||
-- Override this plugin's configuration for this request. | ||
plugin_configuration = consumer_plugin_configuration | ||
ngx.ctx.plugins_for_request[i][2] = consumer_plugin_configuration | ||
end | ||
end | ||
end | ||
|
||
return plugin, plugin_configuration | ||
end | ||
end | ||
|
||
return iter_plugins_for_req |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.