diff --git a/kong/globalpatches.lua b/kong/globalpatches.lua index 61ca747fd4d2..5b0712e071d1 100644 --- a/kong/globalpatches.lua +++ b/kong/globalpatches.lua @@ -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 @@ -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() + ngx.ctx.body_read = true + end + end end end diff --git a/kong/pdk/request.lua b/kong/pdk/request.lua index fbd55a741944..c96449f81462 100644 --- a/kong/pdk/request.lua +++ b/kong/pdk/request.lua @@ -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" @@ -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 end @@ -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 @@ -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 @@ -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 @@ -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) 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 @@ -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 if not content_type then return nil, "missing content type" end diff --git a/kong/pdk/response.lua b/kong/pdk/response.lua index 844d5c7d1390..bb6bee29fc6b 100644 --- a/kong/pdk/response.lua +++ b/kong/pdk/response.lua @@ -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 diff --git a/kong/pdk/service/request.lua b/kong/pdk/service/request.lua index f583d390fa14..9c01c237bc15 100644 --- a/kong/pdk/service/request.lua +++ b/kong/pdk/service/request.lua @@ -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" diff --git a/kong/pdk/service/response.lua b/kong/pdk/service/response.lua index 5a6621abf543..3161041c0e9f 100644 --- a/kong/pdk/service/response.lua +++ b/kong/pdk/service/response.lua @@ -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) 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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/kong/plugins/aws-lambda/request-util.lua b/kong/plugins/aws-lambda/request-util.lua index 9e761afc1959..fe59f786c983 100644 --- a/kong/plugins/aws-lambda/request-util.lua +++ b/kong/plugins/aws-lambda/request-util.lua @@ -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 diff --git a/kong/plugins/basic-auth/access.lua b/kong/plugins/basic-auth/access.lua index d989738d5cbe..45570e234367 100644 --- a/kong/plugins/basic-auth/access.lua +++ b/kong/plugins/basic-auth/access.lua @@ -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") @@ -158,21 +158,22 @@ end local function do_authentication(conf) local www_authenticate = "Basic realm=\"" .. conf.realm .. "\"" + local 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 diff --git a/kong/plugins/cors/handler.lua b/kong/plugins/cors/handler.lua index 3d62be388181..4f2a023b87f3 100644 --- a/kong/plugins/cors/handler.lua +++ b/kong/plugins/cors/handler.lua @@ -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 @@ -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 @@ -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 @@ -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) @@ -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 @@ -207,8 +208,8 @@ 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 @@ -216,7 +217,7 @@ function CorsHandler:access(conf) 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 @@ -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 @@ -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", diff --git a/kong/plugins/grpc-web/handler.lua b/kong/plugins/grpc-web/handler.lua index 8159fb5c196d..437f92f119a0 100644 --- a/kong/plugins/grpc-web/handler.lua +++ b/kong/plugins/grpc-web/handler.lua @@ -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 @@ -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 diff --git a/kong/plugins/hmac-auth/access.lua b/kong/plugins/hmac-auth/access.lua index e3a2828ddf68..cd4acaedbbc7 100644 --- a/kong/plugins/hmac-auth/access.lua +++ b/kong/plugins/hmac-auth/access.lua @@ -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 @@ -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 @@ -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 == "" @@ -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 @@ -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", diff --git a/kong/plugins/ldap-auth/access.lua b/kong/plugins/ldap-auth/access.lua index a2b5db0817bd..824aace960eb 100644 --- a/kong/plugins/ldap-auth/access.lua +++ b/kong/plugins/ldap-auth/access.lua @@ -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 diff --git a/kong/plugins/oauth2/access.lua b/kong/plugins/oauth2/access.lua index 04eecf657f21..8b1dcb5ea95e 100644 --- a/kong/plugins/oauth2/access.lua +++ b/kong/plugins/oauth2/access.lua @@ -877,7 +877,7 @@ local function parse_access_token(conf) parameters[ACCESS_TOKEN] = nil kong.service.request.set_query(parameters) - local content_type = kong.request.get_header("content-type") + local content_type = ngx.var.http_content_type local is_form_post = content_type and string_find(content_type, "application/x-www-form-urlencoded", 1, true) diff --git a/kong/plugins/request-transformer/access.lua b/kong/plugins/request-transformer/access.lua index 9b3216a233e4..9ab374841f5f 100644 --- a/kong/plugins/request-transformer/access.lua +++ b/kong/plugins/request-transformer/access.lua @@ -8,7 +8,7 @@ local table_insert = table.insert local get_uri_args = kong.request.get_query local set_uri_args = kong.service.request.set_query local clear_header = kong.service.request.clear_header -local get_header = kong.request.get_header +local var = ngx.var local set_header = kong.service.request.set_header local get_headers = kong.request.get_headers local set_headers = kong.service.request.set_headers @@ -31,7 +31,6 @@ local template_cache = setmetatable( {}, { __mode = "k" }) local DEBUG = ngx.DEBUG local CONTENT_LENGTH = "content-length" -local CONTENT_TYPE = "content-type" local HOST = "host" local JSON, MULTI, ENCODED = "json", "multi_part", "form_encoded" local EMPTY = require("kong.tools.table").EMPTY @@ -422,7 +421,8 @@ local function transform_multipart_body(conf, body, content_length, content_type end local function transform_body(conf, template_env) - local content_type_value = get_header(CONTENT_TYPE) + -- content-type is a builtin single value header, use var to fetch is faster. + local content_type_value = var.http_content_type local content_type = get_content_type(content_type_value) if content_type == nil or #conf.rename.body < 1 and #conf.remove.body < 1 and #conf.replace.body < 1 and @@ -456,7 +456,8 @@ local function transform_method(conf) if conf.http_method then set_method(conf.http_method:upper()) if conf.http_method == "GET" or conf.http_method == "HEAD" or conf.http_method == "TRACE" then - local content_type_value = get_header(CONTENT_TYPE) + -- content-type is a builtin single value header, use var to fetch is faster. + local content_type_value = var.http_content_type local content_type = get_content_type(content_type_value) if content_type == ENCODED then -- Also put the body into querystring diff --git a/kong/runloop/handler.lua b/kong/runloop/handler.lua index 37efc7f8bb78..9d6573917d09 100644 --- a/kong/runloop/handler.lua +++ b/kong/runloop/handler.lua @@ -1166,7 +1166,8 @@ return { ctx.scheme = var.scheme ctx.request_uri = var.request_uri - + ctx.host = var.host + -- trace router local span = instrumentation.router() -- create the balancer span "in advance" so its ID is available @@ -1208,7 +1209,7 @@ return { req_dyn_hook_run_hook("timing", "workspace_id:got", ctx.workspace) end - local host = var.host + local host = ctx.host local port = ctx.host_port or tonumber(var.server_port, 10) local route = match_t.route diff --git a/t/01-pdk/08-response/02-get_header.t b/t/01-pdk/08-response/02-get_header.t index fd565ec4d7a7..c6bec909981a 100644 --- a/t/01-pdk/08-response/02-get_header.t +++ b/t/01-pdk/08-response/02-get_header.t @@ -116,7 +116,7 @@ X-Missing: nil -=== TEST 4: response.get_header() returns nil when response header does not fit in default max_headers +=== TEST 4: response.get_header() returns existing header will not limited by get_headers() default max_headers configuration --- http_config eval: $t::Util::HttpConfig --- config location = /t { @@ -145,7 +145,7 @@ X-Missing: nil --- request GET /t --- response_body chop -accept header value: nil +accept header value: string --- no_error_log [error]