Skip to content

Commit

Permalink
feat(core) add X-Kong-Upstream-Status header
Browse files Browse the repository at this point in the history
While Kong returns the same status code as
that of the upstream when it proxies a request,
it is useful to record the real status code
returned by the upstream.

This can be used for monitoring Kong
itself if it is returning the same response
as the upstream.
  • Loading branch information
hbagdi committed Feb 28, 2018
1 parent 637532e commit 8cb77bd
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 3 deletions.
1 change: 1 addition & 0 deletions kong/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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
7 changes: 7 additions & 0 deletions kong/core/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -519,11 +519,18 @@ return {
},
header_filter = {
before = function(ctx)
local var = ngx.var
local header = ngx.header

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

if singletons.configuration.server_tokens then
header[constants.HEADERS.UPSTREAM_STATUS] = var.upstream_status
end
end,
after = function(ctx)
local header = ngx.header
Expand Down
12 changes: 9 additions & 3 deletions spec/02-integration/05-proxy/12-server_tokens_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ describe("Server Tokens", function()

teardown(helpers.stop_kong)

it("should return Kong 'Via' header but not change the 'Server' header when request was proxied", function()
it("should return Kong 'Via'and " .. constants.HEADERS.UPSTREAM_STATUS ..
" headers but not change the 'Server' header when request was proxied", function()
local res = assert(client:send {
method = "GET",
path = "/get",
Expand All @@ -95,9 +96,11 @@ describe("Server Tokens", function()
assert.res_status(200, res)
assert.not_equal(default_server_header, res.headers["server"])
assert.equal(default_server_header, res.headers["via"])
assert.equal('200', res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)

it("should return Kong 'Server' header but not the Kong 'Via' header when no API matched (no proxy)", function()
it("should return Kong 'Server' header but not the Kong 'Via' and " ..
constants.HEADERS.UPSTREAM_STATUS .. " headers when no API matched (no proxy)", function()
local res = assert(client:send {
method = "GET",
path = "/get",
Expand All @@ -109,6 +112,7 @@ describe("Server Tokens", function()
assert.res_status(404, res)
assert.equal(default_server_header, res.headers["server"])
assert.is_nil(res.headers["via"])
assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)

end)
Expand All @@ -122,7 +126,8 @@ describe("Server Tokens", function()

teardown(helpers.stop_kong)

it("should not return Kong 'Via' header but it should forward the 'Server' header when request was proxied", function()
it("should not return Kong 'Via' or " .. constants.HEADERS.UPSTREAM_STATUS ..
" header but it should forward the 'Server' header when request was proxied", function()
local res = assert(client:send {
method = "GET",
path = "/get",
Expand All @@ -134,6 +139,7 @@ describe("Server Tokens", function()
assert.res_status(200, res)
assert.response(res).has.header "server"
assert.response(res).has_not.header "via"
assert.is_nil(res.headers[constants.HEADERS.UPSTREAM_STATUS])
assert.not_equal(default_server_header, res.headers["server"])
end)

Expand Down
61 changes: 61 additions & 0 deletions spec/02-integration/05-proxy/13-upstream-status-header_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
local helpers = require "spec.helpers"
local constants = require "kong.constants"


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

setup(function()
assert(helpers.dao.apis:insert {
name = "upstream-header-1",
hosts = { "upstream-header-1.com" },
upstream_url = helpers.mock_upstream_url,
})
local api2 = assert(helpers.dao.apis:insert {
name = "upstream-header-2",
hosts = { "upstream-header-2.com" },
upstream_url = helpers.mock_upstream_url,
})
assert(helpers.dao.plugins:insert {
name = "force-error",
api_id = api2.id,
})
assert(helpers.start_kong({
nginx_conf = "spec/fixtures/custom_nginx.template",
custom_plugins = "force-error",
}))
client = helpers.proxy_client()
end)

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

it("should be same as upstream staus code", function()
local res = assert(client:send {
method = "GET",
path = "/",
headers = {
host = "upstream-header-1.com",
}
})

assert.res_status(200, res)
assert.equal('200', res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)

it("should be same as upstream staus code even if plugin changes status code", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = {
["Host"] = "upstream-header-2.com",
}
})
assert.res_status(500, res)
assert.equal('200', res.headers[constants.HEADERS.UPSTREAM_STATUS])
end)
end)
19 changes: 19 additions & 0 deletions spec/fixtures/custom_plugins/kong/plugins/force-error/handler.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
local BasePlugin = require "kong.plugins.base_plugin"


local ForceError = BasePlugin:extend()


ForceError.PRIORITY = 1000


function ForceError:new()
ForceError.super.new(self, "force-error")
end

function ForceError:header_filter()
ForceError.super.access(self)
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
end

return ForceError
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
return {
fields = {
}
}

0 comments on commit 8cb77bd

Please sign in to comment.