Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pdk refine with optional cache to improvement performance #13981

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions kong/globalpatches.lua
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ return function(options)
local get_uri_args = ngx.req.get_uri_args
local get_post_args = ngx.req.get_post_args
local decode_args = ngx.decode_args
local read_req_body = ngx.req.read_body

local DEFAULT_MAX_REQ_HEADERS = 100
local DEFAULT_MAX_RESP_HEADERS = 100
Expand Down Expand Up @@ -232,6 +233,15 @@ return function(options)
return decode_args_real(str, max_args or MAX_DECODE_ARGS, ...)
end
-- ]

-- READ REQUEST BODY [
_G.ngx.req.read_body = function()
-- for the same request, only one `read_body` call is needed
if not ngx.ctx.body_read then
read_req_body()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ngx.req.read_body only need to be called once in a request lifetime. Although multiple calls in the same request
don't re-read data to buffer multiple times, there are still some extra cost of check logic. So we could just avoid it to gain better performance.

ngx.ctx.body_read = true
end
end
end
end

Expand Down
17 changes: 7 additions & 10 deletions kong/pdk/request.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ local function new(self)
local MIN_PORT = 1
local MAX_PORT = 65535

local CONTENT_TYPE = "Content-Type"

local CONTENT_TYPE_POST = "application/x-www-form-urlencoded"
local CONTENT_TYPE_JSON = "application/json"
local CONTENT_TYPE_FORM_DATA = "multipart/form-data"
Expand Down Expand Up @@ -98,7 +96,7 @@ local function new(self)
function _REQUEST.get_scheme()
check_phase(PHASES.request)

return var.scheme
return ngx.ctx.scheme or var.scheme
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctx is cheaper then var, so use ctx if exists, like what we do in router handling: local req_scheme = ctx and ctx.scheme or var.scheme

end


Expand All @@ -116,7 +114,7 @@ local function new(self)
function _REQUEST.get_host()
check_phase(PHASES.request)

return var.host
return ngx.ctx.host or var.host
end


Expand Down Expand Up @@ -437,7 +435,7 @@ local function new(self)
function _REQUEST.get_raw_path()
check_phase(PHASES.request)

local uri = var.request_uri or ""
local uri = ngx.ctx.request_uri or var.request_uri or ""
local s = find(uri, "?", 2, true)
return s and sub(uri, 1, s - 1) or uri
end
Expand All @@ -456,7 +454,7 @@ local function new(self)
-- kong.request.get_path_with_query() -- "/v1/movies?movie=foo"
function _REQUEST.get_path_with_query()
check_phase(PHASES.request)
return var.request_uri or ""
return ngx.ctx.request_uri or var.request_uri or ""
end


Expand Down Expand Up @@ -619,14 +617,14 @@ local function new(self)
-- kong.request.get_header("Host") -- "foo.com"
-- kong.request.get_header("x-custom-header") -- "bla"
-- kong.request.get_header("X-Another") -- "foo bar"
function _REQUEST.get_header(name)
function _REQUEST.get_header(name, ctx)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http_get_header supports ctx param to cache headers, expose it to get_header API so we could use cache.

check_phase(PHASES.request)

if type(name) ~= "string" then
error("header name must be a string", 2)
end

return http_get_header(name)
return http_get_header(name, ctx)
end


Expand Down Expand Up @@ -820,8 +818,7 @@ local function new(self)
-- body.age -- "42"
function _REQUEST.get_body(mimetype, max_args, max_allowed_file_size)
check_phase(before_content)

local content_type = mimetype or _REQUEST.get_header(CONTENT_TYPE)
local content_type = mimetype or ngx.var.http_content_type
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Content-Type" is a built-in single value header, which means ngx.var.http_content_type always returns the same value of get_header("Content-Type"), and fetching single header by ngx.var is quicker than get_header(which may use get_headers()["Content-Type"] to get the single header).

if not content_type then
return nil, "missing content type"
end
Expand Down
2 changes: 1 addition & 1 deletion kong/pdk/response.lua
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ local function new(self, major_version)
error("header name must be a string", 2)
end

local header_value = _RESPONSE.get_headers()[name]
local header_value = ngx.header[name]
if type(header_value) == "table" then
return header_value[1]
end
Expand Down
2 changes: 1 addition & 1 deletion kong/pdk/service/request.lua
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ local function new(self)
error("mime must be a string", 2)
end
if not mime then
mime = ngx.req.get_headers()[CONTENT_TYPE]
mime = ngx_var.http_content_type
if not mime then
return nil, "content type was neither explicitly given " ..
"as an argument or received as a header"
Expand Down
14 changes: 8 additions & 6 deletions kong/pdk/service/response.lua
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,11 @@ local function new(pdk, major_version)
-- -- or `access` phase prior calling this function.
--
-- local body = kong.service.response.get_raw_body()
function response.get_raw_body()
function response.get_raw_body(buffered_proxying)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a minor refinement so that if we could reduce the reads of ngx.ctx.buffered_proxying by passing it by param

check_phase(header_body_log)
local ctx = ngx.ctx
if not ctx.buffered_proxying then
local buffered = buffered_proxying or ctx.buffered_proxying
if not buffered then
error("service body is only available with buffered proxying " ..
"(see: kong.service.request.enable_buffering function)", 2)
end
Expand All @@ -313,7 +314,8 @@ local function new(pdk, major_version)
-- local body = kong.service.response.get_body()
function response.get_body(mimetype, max_args)
check_phase(header_body_log)
if not ngx.ctx.buffered_proxying then
local bufferred_proxying = ngx.ctx.buffered_proxying
if not bufferred_proxying then
error("service body is only available with buffered proxying " ..
"(see: kong.service.request.enable_buffering function)", 2)
end
Expand Down Expand Up @@ -344,7 +346,7 @@ local function new(pdk, major_version)
end
end

local body = response.get_raw_body()
local body = response.get_raw_body(bufferred_proxying)
local pargs, err = ngx.decode_args(body, max_args or MAX_POST_ARGS_DEFAULT)
if not pargs then
return nil, err, CONTENT_TYPE_POST
Expand All @@ -353,7 +355,7 @@ local function new(pdk, major_version)
return pargs, nil, CONTENT_TYPE_POST

elseif find(content_type_lower, CONTENT_TYPE_JSON, 1, true) == 1 then
local body = response.get_raw_body()
local body = response.get_raw_body(bufferred_proxying)
local json = cjson.decode_with_array_mt(body)
if type(json) ~= "table" then
return nil, "invalid json body", CONTENT_TYPE_JSON
Expand All @@ -362,7 +364,7 @@ local function new(pdk, major_version)
return json, nil, CONTENT_TYPE_JSON

elseif find(content_type_lower, CONTENT_TYPE_FORM_DATA, 1, true) == 1 then
local body = response.get_raw_body()
local body = response.get_raw_body(bufferred_proxying)

local parts = multipart(body, content_type)
if not parts then
Expand Down
2 changes: 1 addition & 1 deletion kong/plugins/aws-lambda/request-util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ local function build_request_payload(conf)
end

if conf.forward_request_body then
local content_type = kong.request.get_header("content-type")
local content_type = ngx.var.http_content_type
local body_raw = read_request_body(conf.skip_large_bodies)
local body_args, err = kong.request.get_body()
if err and err:match("content type") then
Expand Down
11 changes: 6 additions & 5 deletions kong/plugins/basic-auth/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ local _M = {}
-- @param {table} conf Plugin config
-- @return {string} public_key
-- @return {string} private_key
local function retrieve_credentials(header_name, conf)
local function retrieve_credentials(header_name, conf, ctx)
local username, password
local authorization_header = kong.request.get_header(header_name)
local authorization_header = kong.request.get_header(header_name, ctx)

if authorization_header then
local iterator, iter_err = re_gmatch(authorization_header, "\\s*[Bb]asic\\s*(.+)", "oj")
Expand Down Expand Up @@ -158,21 +158,22 @@ end

local function do_authentication(conf)
local www_authenticate = "Basic realm=\"" .. conf.realm .. "\""
local ctx = {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a local ctx here which is cheaper thanngx.ctx, and the cache is only fresh in the local ctx lifetime, not in the request lifetime, so we don't need ngx.ctx


-- If both headers are missing, return 401
if not (kong.request.get_header("authorization") or kong.request.get_header("proxy-authorization")) then
if not (kong.request.get_header("authorization", ctx) or kong.request.get_header("proxy-authorization", ctx)) then
return false, unauthorized("Unauthorized", www_authenticate)
end

local credential
local given_username, given_password = retrieve_credentials("proxy-authorization", conf)
local given_username, given_password = retrieve_credentials("proxy-authorization", conf, ctx)
if given_username and given_password then
credential = load_credential_from_db(given_username)
end

-- Try with the authorization header
if not credential then
given_username, given_password = retrieve_credentials("authorization", conf)
given_username, given_password = retrieve_credentials("authorization", conf, ctx)
if given_username and given_password then
credential = load_credential_from_db(given_username)
else
Expand Down
26 changes: 14 additions & 12 deletions kong/plugins/cors/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ local function add_vary_header(header_filter)
end
end

local function configure_origin(conf, header_filter)
local function configure_origin(conf, header_filter, ctx)
local n_origins = conf.origins ~= nil and #conf.origins or 0
local set_header = kong.response.set_header

Expand Down Expand Up @@ -89,7 +89,7 @@ local function configure_origin(conf, header_filter)
end
end

local req_origin = kong.request.get_header("origin")
local req_origin = kong.request.get_header("origin", ctx)
if req_origin then
local cached_domains = config_cache[conf]
if not cached_domains then
Expand Down Expand Up @@ -167,7 +167,7 @@ local function configure_origin(conf, header_filter)
end


local function configure_credentials(conf, allow_all, header_filter)
local function configure_credentials(conf, allow_all, header_filter, ctx)
local set_header = kong.response.set_header

if not conf.credentials then
Expand All @@ -181,7 +181,7 @@ local function configure_credentials(conf, allow_all, header_filter)

-- Access-Control-Allow-Origin is '*', must change it because ACAC cannot
-- be 'true' if ACAO is '*'.
local req_origin = kong.request.get_header("origin")
local req_origin = kong.request.get_header("origin", ctx)
if req_origin then
add_vary_header(header_filter)
set_header("Access-Control-Allow-Origin", req_origin)
Expand All @@ -191,9 +191,10 @@ end


function CorsHandler:access(conf)
local ctx = {}
if kong.request.get_method() ~= "OPTIONS"
or not kong.request.get_header("Origin")
or not kong.request.get_header("Access-Control-Request-Method")
or not kong.request.get_header("Origin", ctx)
or not kong.request.get_header("Access-Control-Request-Method", ctx)
then
return
end
Expand All @@ -207,16 +208,16 @@ function CorsHandler:access(conf)
return
end

local allow_all = configure_origin(conf, false)
configure_credentials(conf, allow_all, false)
local allow_all = configure_origin(conf, false, ctx)
configure_credentials(conf, allow_all, false, ctx)

local set_header = kong.response.set_header

if conf.headers and #conf.headers > 0 then
set_header("Access-Control-Allow-Headers", concat(conf.headers, ","))

else
local acrh = kong.request.get_header("Access-Control-Request-Headers")
local acrh = kong.request.get_header("Access-Control-Request-Headers", ctx)
if acrh then
set_header("Access-Control-Allow-Headers", acrh)
else
Expand All @@ -234,7 +235,7 @@ function CorsHandler:access(conf)
end

if conf.private_network and
kong.request.get_header("Access-Control-Request-Private-Network") == 'true' then
kong.request.get_header("Access-Control-Request-Private-Network", ctx) == 'true' then
set_header("Access-Control-Allow-Private-Network", 'true')
end

Expand All @@ -247,8 +248,9 @@ function CorsHandler:header_filter(conf)
return
end

local allow_all = configure_origin(conf, true)
configure_credentials(conf, allow_all, true)
local ctx = {}
local allow_all = configure_origin(conf, true, ctx)
configure_credentials(conf, allow_all, true, ctx)

if conf.exposed_headers and #conf.exposed_headers > 0 then
kong.response.set_header("Access-Control-Expose-Headers",
Expand Down
3 changes: 1 addition & 2 deletions kong/plugins/grpc-web/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ local ngx_arg = ngx.arg
local ngx_var = ngx.var

local kong_request_get_path = kong.request.get_path
local kong_request_get_header = kong.request.get_header
local kong_request_get_method = kong.request.get_method
local kong_request_get_raw_body = kong.request.get_raw_body
local kong_response_exit = kong.response.exit
Expand Down Expand Up @@ -51,7 +50,7 @@ function grpc_web:access(conf)
end

local dec, err = deco.new(
kong_request_get_header("Content-Type"),
ngx.var.http_content_type,
uri, conf.proto)

if not dec then
Expand Down
20 changes: 11 additions & 9 deletions kong/plugins/hmac-auth/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ local function create_hash(request_uri, hmac_params)
local hmac_headers = hmac_params.hmac_headers

local count = #hmac_headers
local ctx = {}
for i = 1, count do
local header = hmac_headers[i]
local header_value = kong.request.get_header(header)
local header_value = kong.request.get_header(header, ctx)

if not header_value then
if header == "@request-target" then
Expand Down Expand Up @@ -199,8 +200,8 @@ local function load_credential(username)
end


local function validate_clock_skew(date_header_name, allowed_clock_skew)
local date = kong_request.get_header(date_header_name)
local function validate_clock_skew(date_header_name, allowed_clock_skew, ctx)
local date = kong_request.get_header(date_header_name, ctx)
if not date then
return false
end
Expand All @@ -219,14 +220,14 @@ local function validate_clock_skew(date_header_name, allowed_clock_skew)
end


local function validate_body()
local function validate_body(ctx)
local body, err = kong_request.get_raw_body()
if err then
kong.log.debug(err)
return false
end

local digest_received = kong_request.get_header(DIGEST)
local digest_received = kong_request.get_header(DIGEST, ctx)
if not digest_received then
-- if there is no digest and no body, it is ok
return body == ""
Expand Down Expand Up @@ -281,8 +282,9 @@ end


local function do_authentication(conf)
local authorization = kong_request.get_header(AUTHORIZATION)
local proxy_authorization = kong_request.get_header(PROXY_AUTHORIZATION)
local ctx = {}
local authorization = kong_request.get_header(AUTHORIZATION, ctx)
local proxy_authorization = kong_request.get_header(PROXY_AUTHORIZATION, ctx)
local www_auth_content = conf.realm and fmt('hmac realm="%s"', conf.realm) or 'hmac'

-- If both headers are missing, return 401
Expand All @@ -291,8 +293,8 @@ local function do_authentication(conf)
end

-- validate clock skew
if not (validate_clock_skew(X_DATE, conf.clock_skew) or
validate_clock_skew(DATE, conf.clock_skew)) then
if not (validate_clock_skew(X_DATE, conf.clock_skew, ctx) or
validate_clock_skew(DATE, conf.clock_skew, ctx)) then
return false, unauthorized(
"HMAC signature cannot be verified, a valid date or " ..
"x-date header is required for HMAC Authentication",
Expand Down
5 changes: 3 additions & 2 deletions kong/plugins/ldap-auth/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,9 @@ local function unauthorized(message, authorization_scheme)
end

local function do_authentication(conf)
local authorization_value = kong.request.get_header(AUTHORIZATION)
local proxy_authorization_value = kong.request.get_header(PROXY_AUTHORIZATION)
local ctx = {}
local authorization_value = kong.request.get_header(AUTHORIZATION, ctx)
local proxy_authorization_value = kong.request.get_header(PROXY_AUTHORIZATION, ctx)

local scheme = conf.header_type
if scheme == "ldap" then
Expand Down
Loading
Loading