Skip to content

Commit

Permalink
feat(core) add ability to inject X-Kong-Upstream-Status header
Browse files Browse the repository at this point in the history
X-Kong-Upstream-Status header returns the status
code returned by the upstream API.
This header is disabled by default and can be enabled
by adding it to `headers` config property in kong.conf.
  • Loading branch information
hbagdi committed May 2, 2018
1 parent da4e36b commit 141c076
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 0 deletions.
1 change: 1 addition & 0 deletions kong/conf_loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ local header_key_to_name = {
[string.lower(headers.SERVER)] = headers.SERVER,
[string.lower(headers.PROXY_LATENCY)] = headers.PROXY_LATENCY,
[string.lower(headers.UPSTREAM_LATENCY)] = headers.UPSTREAM_LATENCY,
[string.lower(headers.UPSTREAM_STATUS)] = headers.UPSTREAM_STATUS,
}

local PREFIX_PATHS = {
Expand Down
1 change: 1 addition & 0 deletions kong/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ return {
HOST_OVERRIDE = "X-Host-Override",
PROXY_LATENCY = "X-Kong-Proxy-Latency",
UPSTREAM_LATENCY = "X-Kong-Upstream-Latency",
UPSTREAM_STATUS = "X-Kong-Upstream-Status",
CONSUMER_ID = "X-Consumer-ID",
CONSUMER_CUSTOM_ID = "X-Consumer-Custom-ID",
CONSUMER_USERNAME = "X-Consumer-Username",
Expand Down
15 changes: 15 additions & 0 deletions kong/runloop/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ local ngx = ngx
local log = ngx.log
local null = ngx.null
local ngx_now = ngx.now
local re_match = ngx.re.match
local unpack = unpack


Expand Down Expand Up @@ -687,12 +688,26 @@ return {
},
header_filter = {
before = function(ctx)
local var = ngx.var
local header = ngx.header

if ctx.KONG_PROXIED then
local now = get_now()
-- time spent waiting for a response from upstream
ctx.KONG_WAITING_TIME = now - ctx.KONG_ACCESS_ENDED_AT
ctx.KONG_HEADER_FILTER_STARTED_AT = now
end

local upstream_status_header = constants.HEADERS.UPSTREAM_STATUS
if singletons.configuration.headers[upstream_status_header] then
local matches, err = re_match(var.upstream_status,
"[0-9]+$", "oj")
if err then
log(ERR, "failed to set ", upstream_status_header, " header: ", err)
else
header[upstream_status_header] = matches[0]
end
end
end,
after = function(ctx)
local header = ngx.header
Expand Down
141 changes: 141 additions & 0 deletions spec/02-integration/05-proxy/14-upstream-status-header_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
local helpers = require "spec.helpers"
local constants = require "kong.constants"

local function setup_db()
local bp = helpers.get_db_utils()
assert(helpers.dao:run_migrations())

local service = assert(bp.services:insert {
host = helpers.mock_upstream_host,
port = helpers.mock_upstream_port,
protocol = helpers.mock_upstream_protocol,
})

assert(bp.routes:insert {
protocols = { "http" },
service = service,
paths = { "/status/200" },
})

local route2 = assert(bp.routes:insert {
protocols = { "http" },
service = service,
paths = { "/status/plugin-changes-200-to-500" },
})

assert(bp.plugins:insert({
name = "dummy",
route_id = route2.id,
config = {
resp_code = 500,
}
}))
end

describe(constants.HEADERS.UPSTREAM_STATUS .. " header", function()
local client

describe("should be same as upstream status code", function()
setup(function()
setup_db()
assert(helpers.start_kong {
headers = "server_tokens,latency_tokens,x-kong-upstream-status",
custom_plugins = "dummy",
nginx_conf = "spec/fixtures/custom_nginx.template",
})
client = helpers.proxy_client()
end)

teardown(function()
if client then
client:close()
end
helpers.stop_kong()
end)

it("when no plugin changes status code", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
host = helpers.mock_upstream_host,
}
})
assert.res_status(200, res)
assert.equal("200", res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)

it("when a plugin changes status code", function()
local res = assert(client:send {
method = "GET",
host = helpers.mock_upstream_host,
path = "/status/plugin-changes-200-to-500",
headers = {
["Host"] = helpers.mock_upstream_host,
}
})
assert.res_status(500, res)
assert.equal("200", res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)
end)

describe("is not injected with default configuration", function()
setup(function()
setup_db()
assert(helpers.start_kong{
nginx_conf = "spec/fixtures/custom_nginx.template",
})
end)

teardown(function()
if client then
client:close()
end
helpers.stop_kong()
end)

it("", function()
local client = helpers.proxy_client()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
host = helpers.mock_upstream_host,
}
})
assert.res_status(200, res)
assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)
end)

describe("is injected with configuration [headers=X-Kong-Upstream-Status]", function()

setup(function()
setup_db()
assert(helpers.start_kong{
nginx_conf = "spec/fixtures/custom_nginx.template",
headers="X-Kong-Upstream-Status",
})
end)

teardown(function()
if client then
client:close()
end
helpers.stop_kong()
end)

it("", function()
local client = helpers.proxy_client()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
host = helpers.mock_upstream_host,
}
})
assert.res_status(200, res)
assert("200", res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)
end)
end)
4 changes: 4 additions & 0 deletions spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ function DummyHandler:header_filter(conf)

ngx.header["Dummy-Plugin"] = conf.resp_header_value

if conf.resp_code then
ngx.status = conf.resp_code
end

if conf.append_body then
ngx.header["Content-Length"] = nil
end
Expand Down
1 change: 1 addition & 0 deletions spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ return {
fields = {
resp_header_value = { type = "string", default = "1" },
append_body = { type = "string" },
resp_code = { type = "number" },
}
}

0 comments on commit 141c076

Please sign in to comment.