diff --git a/kong/conf_loader.lua b/kong/conf_loader.lua index cc02df37e45..c9ab8a0e474 100644 --- a/kong/conf_loader.lua +++ b/kong/conf_loader.lua @@ -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 = { diff --git a/kong/constants.lua b/kong/constants.lua index b7a865530fa..c4a4ab865c3 100644 --- a/kong/constants.lua +++ b/kong/constants.lua @@ -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", diff --git a/kong/runloop/handler.lua b/kong/runloop/handler.lua index 7d44bd62c47..18a8354dcff 100644 --- a/kong/runloop/handler.lua +++ b/kong/runloop/handler.lua @@ -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 @@ -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 diff --git a/spec/02-integration/05-proxy/14-upstream-status-header_spec.lua b/spec/02-integration/05-proxy/14-upstream-status-header_spec.lua new file mode 100644 index 00000000000..63b9b614464 --- /dev/null +++ b/spec/02-integration/05-proxy/14-upstream-status-header_spec.lua @@ -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) diff --git a/spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua b/spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua index 855cadb4243..44d54d8ddae 100644 --- a/spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua +++ b/spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua @@ -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 diff --git a/spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua b/spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua index b193b332fe3..4985df3482f 100644 --- a/spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua +++ b/spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua @@ -2,5 +2,6 @@ return { fields = { resp_header_value = { type = "string", default = "1" }, append_body = { type = "string" }, + resp_code = { type = "number" }, } }