1384 lines
39 KiB
Lua
1384 lines
39 KiB
Lua
local helpers = require "spec.helpers"
|
|
local strategies = require("kong.plugins.proxy-cache.strategies")
|
|
|
|
local function get(client, host)
|
|
return assert(client:get("/get", {
|
|
headers = {
|
|
host = host,
|
|
},
|
|
}))
|
|
end
|
|
|
|
--local TIMEOUT = 10 -- default timeout for non-memory strategies
|
|
|
|
-- use wait_until spec helper only on async strategies
|
|
--local function strategy_wait_until(strategy, func, timeout)
|
|
-- if strategies.DELAY_STRATEGY_STORE[strategy] then
|
|
-- helpers.wait_until(func, timeout)
|
|
-- end
|
|
--end
|
|
|
|
|
|
do
|
|
local policy = "memory"
|
|
describe("proxy-cache access with policy: " .. policy, function()
|
|
local client, admin_client
|
|
--local cache_key
|
|
local policy_config = { dictionary_name = "kong", }
|
|
|
|
local strategy = strategies({
|
|
strategy_name = policy,
|
|
strategy_opts = policy_config,
|
|
})
|
|
|
|
setup(function()
|
|
|
|
local bp = helpers.get_db_utils(nil, nil, {"proxy-cache"})
|
|
strategy:flush(true)
|
|
|
|
local route1 = assert(bp.routes:insert {
|
|
hosts = { "route-1.test" },
|
|
})
|
|
local route2 = assert(bp.routes:insert {
|
|
hosts = { "route-2.test" },
|
|
})
|
|
assert(bp.routes:insert {
|
|
hosts = { "route-3.test" },
|
|
})
|
|
assert(bp.routes:insert {
|
|
hosts = { "route-4.test" },
|
|
})
|
|
local route5 = assert(bp.routes:insert {
|
|
hosts = { "route-5.test" },
|
|
})
|
|
local route6 = assert(bp.routes:insert {
|
|
hosts = { "route-6.test" },
|
|
})
|
|
local route7 = assert(bp.routes:insert {
|
|
hosts = { "route-7.test" },
|
|
})
|
|
local route8 = assert(bp.routes:insert {
|
|
hosts = { "route-8.test" },
|
|
})
|
|
local route9 = assert(bp.routes:insert {
|
|
hosts = { "route-9.test" },
|
|
})
|
|
local route10 = assert(bp.routes:insert {
|
|
hosts = { "route-10.test" },
|
|
})
|
|
local route11 = assert(bp.routes:insert {
|
|
hosts = { "route-11.test" },
|
|
})
|
|
local route12 = assert(bp.routes:insert {
|
|
hosts = { "route-12.test" },
|
|
})
|
|
local route13 = assert(bp.routes:insert {
|
|
hosts = { "route-13.test" },
|
|
})
|
|
local route14 = assert(bp.routes:insert {
|
|
hosts = { "route-14.test" },
|
|
})
|
|
local route15 = assert(bp.routes:insert {
|
|
hosts = { "route-15.test" },
|
|
})
|
|
local route16 = assert(bp.routes:insert {
|
|
hosts = { "route-16.test" },
|
|
})
|
|
local route17 = assert(bp.routes:insert {
|
|
hosts = { "route-17.test" },
|
|
})
|
|
local route18 = assert(bp.routes:insert {
|
|
hosts = { "route-18.test" },
|
|
})
|
|
local route19 = assert(bp.routes:insert {
|
|
hosts = { "route-19.test" },
|
|
})
|
|
local route20 = assert(bp.routes:insert {
|
|
hosts = { "route-20.test" },
|
|
})
|
|
local route21 = assert(bp.routes:insert {
|
|
hosts = { "route-21.test" },
|
|
})
|
|
local route22 = assert(bp.routes:insert({
|
|
hosts = { "route-22.test" },
|
|
}))
|
|
local route23 = assert(bp.routes:insert({
|
|
hosts = { "route-23.test" },
|
|
}))
|
|
local route24 = assert(bp.routes:insert({
|
|
hosts = { "route-24.test" },
|
|
}))
|
|
|
|
local consumer1 = assert(bp.consumers:insert {
|
|
username = "bob",
|
|
})
|
|
assert(bp.keyauth_credentials:insert {
|
|
key = "bob",
|
|
consumer = { id = consumer1.id },
|
|
})
|
|
local consumer2 = assert(bp.consumers:insert {
|
|
username = "alice",
|
|
})
|
|
assert(bp.keyauth_credentials:insert {
|
|
key = "alice",
|
|
consumer = { id = consumer2.id },
|
|
})
|
|
assert(bp.plugins:insert {
|
|
name = "key-auth",
|
|
route = { id = route5.id },
|
|
config = {},
|
|
})
|
|
assert(bp.plugins:insert {
|
|
name = "key-auth",
|
|
route = { id = route13.id },
|
|
config = {},
|
|
})
|
|
assert(bp.plugins:insert {
|
|
name = "key-auth",
|
|
route = { id = route14.id },
|
|
config = {},
|
|
})
|
|
assert(bp.plugins:insert {
|
|
name = "key-auth",
|
|
route = { id = route15.id },
|
|
config = {},
|
|
})
|
|
assert(bp.plugins:insert {
|
|
name = "key-auth",
|
|
route = { id = route16.id },
|
|
config = {},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route1.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route2.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
},
|
|
})
|
|
|
|
-- global plugin for routes 3 and 4
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route5.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route6.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
cache_ttl = 2,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route7.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
cache_control = true,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route8.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
cache_control = true,
|
|
storage_ttl = 600,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route9.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
cache_ttl = 2,
|
|
storage_ttl = 60,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route10.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/html; charset=utf-8", "application/json" },
|
|
response_code = { 200, 417 },
|
|
request_method = { "GET", "HEAD", "POST" },
|
|
[policy] = policy_config,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route11.id },
|
|
config = {
|
|
strategy = policy,
|
|
[policy] = policy_config,
|
|
content_type = { "text/plain", "application/json" },
|
|
response_code = { 200 },
|
|
request_method = { "GET", "HEAD", "POST" },
|
|
vary_headers = {"foo"}
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route12.id },
|
|
config = {
|
|
strategy = policy,
|
|
[policy] = policy_config,
|
|
content_type = { "text/plain", "application/json" },
|
|
response_code = { 200 },
|
|
request_method = { "GET", "HEAD", "POST" },
|
|
vary_query_params = {"foo"}
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route17.id },
|
|
config = {
|
|
strategy = policy,
|
|
[policy] = policy_config,
|
|
content_type = { "*/*" },
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route18.id },
|
|
config = {
|
|
strategy = policy,
|
|
[policy] = policy_config,
|
|
content_type = { "application/xml; charset=UTF-8" },
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route19.id },
|
|
config = {
|
|
strategy = policy,
|
|
[policy] = policy_config,
|
|
content_type = { "application/xml;" }, -- invalid content_type
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route20.id },
|
|
config = {
|
|
strategy = policy,
|
|
response_code = {404},
|
|
ignore_uri_case = true,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route21.id },
|
|
config = {
|
|
strategy = policy,
|
|
response_code = {404},
|
|
ignore_uri_case = false,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route22.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
response_headers = {
|
|
age = false,
|
|
["X-Cache-Status"] = false,
|
|
["X-Cache-Key"] = false
|
|
},
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route23.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
response_headers = {
|
|
age = true,
|
|
["X-Cache-Status"] = true,
|
|
["X-Cache-Key"] = true
|
|
},
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
name = "proxy-cache",
|
|
route = { id = route24.id },
|
|
config = {
|
|
strategy = policy,
|
|
content_type = { "text/plain", "application/json" },
|
|
[policy] = policy_config,
|
|
-- leave reponse_header to default values
|
|
},
|
|
})
|
|
|
|
assert(helpers.start_kong({
|
|
plugins = "bundled",
|
|
nginx_conf = "spec/fixtures/custom_nginx.template",
|
|
}))
|
|
end)
|
|
|
|
|
|
before_each(function()
|
|
if client then
|
|
client:close()
|
|
end
|
|
if admin_client then
|
|
admin_client:close()
|
|
end
|
|
client = helpers.proxy_client()
|
|
admin_client = helpers.admin_client()
|
|
end)
|
|
|
|
|
|
teardown(function()
|
|
if client then
|
|
client:close()
|
|
end
|
|
|
|
if admin_client then
|
|
admin_client:close()
|
|
end
|
|
|
|
helpers.stop_kong()
|
|
end)
|
|
|
|
it("caches a simple request", function()
|
|
local res = assert(get(client, "route-1.test"))
|
|
|
|
local body1 = assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
-- cache key is a sha256sum of the prefix uuid, method, and $request
|
|
local cache_key1 = res.headers["X-Cache-Key"]
|
|
assert.matches("^[%w%d]+$", cache_key1)
|
|
assert.equals(64, #cache_key1)
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key1) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
local res = assert(get(client, "route-1.test"))
|
|
|
|
local body2 = assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
local cache_key2 = res.headers["X-Cache-Key"]
|
|
assert.same(cache_key1, cache_key2)
|
|
|
|
-- assert that response bodies are identical
|
|
assert.same(body1, body2)
|
|
|
|
-- examine this cache key against another plugin's cache key for the same req
|
|
--cache_key = cache_key1
|
|
end)
|
|
it("No X-Cache* neither age headers on the response without debug header in the query", function()
|
|
local res = assert(get(client, "route-22.test"))
|
|
assert.res_status(200, res)
|
|
assert.is_nil(res.headers["X-Cache-Status"])
|
|
res = assert(get(client, "route-22.test"))
|
|
assert.res_status(200, res)
|
|
assert.is_nil(res.headers["X-Cache-Status"])
|
|
assert.is_nil(res.headers["X-Cache-Key"])
|
|
assert.is_nil(res.headers["Age"])
|
|
res = assert(client:get("/get", {
|
|
headers = {
|
|
Host = "route-22.test",
|
|
["kong-debug"] = 1,
|
|
},
|
|
}))
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
assert.is_not_nil(res.headers["Age"])
|
|
assert.is_not_nil(res.headers["X-Cache-Key"])
|
|
end)
|
|
|
|
it("response_headers headers on the response when configured", function()
|
|
-- Initial query to set cache
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
Host = "route-23.test",
|
|
},
|
|
}))
|
|
-- Cache should be Miss
|
|
assert.res_status(200, res)
|
|
assert.is_same("Miss", res.headers["X-Cache-Status"])
|
|
assert.is_not_nil(res.headers["X-Cache-Key"])
|
|
assert.is_nil(res.headers["Age"])
|
|
-- Cache should be HIT
|
|
res = assert(client:get("/get", {
|
|
headers = {
|
|
Host = "route-23.test",
|
|
},
|
|
}))
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
-- response_headers are configured
|
|
assert.is_not_nil(res.headers["Age"])
|
|
assert.is_not_nil(res.headers["X-Cache-Key"])
|
|
end)
|
|
|
|
it("response_headers headers on the response when set to default", function()
|
|
-- Initial query to set cache
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
Host = "route-24.test",
|
|
},
|
|
}))
|
|
-- Cache should be Miss
|
|
assert.res_status(200, res)
|
|
assert.is_same("Miss", res.headers["X-Cache-Status"])
|
|
assert.is_not_nil(res.headers["X-Cache-Key"])
|
|
assert.is_nil(res.headers["Age"])
|
|
res = assert(client:get("/get", {
|
|
headers = {
|
|
Host = "route-24.test",
|
|
},
|
|
}))
|
|
-- Cache should be Hit
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
-- response_headers are on by default
|
|
assert.is_not_nil(res.headers["Age"])
|
|
assert.is_not_nil(res.headers["X-Cache-Key"])
|
|
end)
|
|
|
|
it("respects cache ttl", function()
|
|
local res = assert(get(client, "route-6.test"))
|
|
|
|
--local cache_key2 = res.headers["X-Cache-Key"]
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key2) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(get(client, "route-6.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- if strategy is local, it's enough to simply use a sleep
|
|
if strategies.LOCAL_DATA_STRATEGIES[policy] then
|
|
ngx.sleep(3)
|
|
end
|
|
|
|
-- wait until the strategy expires the object for the given
|
|
-- cache key
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) == nil
|
|
--end, TIMEOUT)
|
|
|
|
-- and go through the cycle again
|
|
res = assert(get(client, "route-6.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
--cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(get(client, "route-6.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
-- examine the behavior of keeping cache in memory for longer than ttl
|
|
res = assert(get(client, "route-9.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
--cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(get(client, "route-9.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
-- if strategy is local, it's enough to simply use a sleep
|
|
if strategies.LOCAL_DATA_STRATEGIES[policy] then
|
|
ngx.sleep(3)
|
|
end
|
|
|
|
-- give ourselves time to expire
|
|
-- as storage_ttl > cache_ttl, the object still remains in storage
|
|
-- in an expired state
|
|
--strategy_wait_until(policy, function()
|
|
-- local obj = strategy:fetch(cache_key)
|
|
-- return ngx.time() - obj.timestamp > obj.ttl
|
|
--end, TIMEOUT)
|
|
|
|
-- and go through the cycle again
|
|
res = assert(get(client, "route-9.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Refresh", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(get(client, "route-9.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("respects cache ttl via cache control", function()
|
|
local res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
-- if strategy is local, it's enough to simply use a sleep
|
|
if strategies.LOCAL_DATA_STRATEGIES[policy] then
|
|
ngx.sleep(3)
|
|
end
|
|
|
|
-- give ourselves time to expire
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) == nil
|
|
--end, TIMEOUT)
|
|
|
|
-- and go through the cycle again
|
|
res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
-- assert that max-age=0 never results in caching
|
|
res = assert(client:get("/cache/0", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/cache/0", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("public not present in Cache-Control, but max-age is", function()
|
|
-- httpbin's /cache endpoint always sets "Cache-Control: public"
|
|
-- necessary to set it manually using /response-headers instead
|
|
local res = assert(client:get("/response-headers?Cache-Control=max-age%3D604800", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("Cache-Control contains s-maxage only", function()
|
|
local res = assert(client:get("/response-headers?Cache-Control=s-maxage%3D604800", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("Expires present, Cache-Control absent", function()
|
|
local httpdate = ngx.escape_uri(os.date("!%a, %d %b %Y %X %Z", os.time()+5000))
|
|
local res = assert(client:get("/response-headers", {
|
|
query = "Expires=" .. httpdate,
|
|
headers = {
|
|
host = "route-7.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
describe("respects cache-control", function()
|
|
it("min-fresh", function()
|
|
-- bypass via unsatisfied min-fresh
|
|
local res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
["Cache-Control"] = "min-fresh=30",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Refresh", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("max-age", function()
|
|
local res = assert(client:get("/cache/10", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
["Cache-Control"] = "max-age=2",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/cache/10", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
["Cache-Control"] = "max-age=2",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- if strategy is local, it's enough to simply use a sleep
|
|
if strategies.LOCAL_DATA_STRATEGIES[policy] then
|
|
ngx.sleep(3)
|
|
end
|
|
|
|
-- wait until max-age
|
|
--strategy_wait_until(policy, function()
|
|
-- local obj = strategy:fetch(cache_key)
|
|
-- return ngx.time() - obj.timestamp > 2
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/cache/10", {
|
|
headers = {
|
|
host = "route-7.test",
|
|
["Cache-Control"] = "max-age=2",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Refresh", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("max-stale", function()
|
|
local res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-8.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-8.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
-- if strategy is local, it's enough to simply use a sleep
|
|
if strategies.LOCAL_DATA_STRATEGIES[policy] then
|
|
ngx.sleep(4)
|
|
end
|
|
|
|
-- wait for longer than max-stale below
|
|
--strategy_wait_until(policy, function()
|
|
-- local obj = strategy:fetch(cache_key)
|
|
-- return ngx.time() - obj.timestamp - obj.ttl > 2
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/cache/2", {
|
|
headers = {
|
|
host = "route-8.test",
|
|
["Cache-Control"] = "max-stale=1",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Refresh", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("only-if-cached", function()
|
|
local res = assert(client:get("/get?not=here", {
|
|
headers = {
|
|
host = "route-8.test",
|
|
["Cache-Control"] = "only-if-cached",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(504, res)
|
|
end)
|
|
end)
|
|
|
|
it("caches a streaming request", function()
|
|
local res = assert(client:get("/stream/3", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
local body1 = assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
assert.is_nil(res.headers["Content-Length"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/stream/3", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
local body2 = assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
assert.same(body1, body2)
|
|
end)
|
|
|
|
it("uses a separate cache key for the same consumer between routes", function()
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-13.test",
|
|
apikey = "bob",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
local cache_key1 = res.headers["X-Cache-Key"]
|
|
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-14.test",
|
|
apikey = "bob",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
local cache_key2 = res.headers["X-Cache-Key"]
|
|
|
|
assert.not_equal(cache_key1, cache_key2)
|
|
end)
|
|
|
|
it("uses a separate cache key for the same consumer between routes/services", function()
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-15.test",
|
|
apikey = "bob",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
local cache_key1 = res.headers["X-Cache-Key"]
|
|
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-16.test",
|
|
apikey = "bob",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
local cache_key2 = res.headers["X-Cache-Key"]
|
|
|
|
assert.not_equal(cache_key1, cache_key2)
|
|
end)
|
|
|
|
it("uses an separate cache key between routes-specific and a global plugin", function()
|
|
local res = assert(get(client, "route-3.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
local cache_key1 = res.headers["X-Cache-Key"]
|
|
assert.matches("^[%w%d]+$", cache_key1)
|
|
assert.equals(64, #cache_key1)
|
|
|
|
res = assert(get(client, "route-4.test"))
|
|
|
|
assert.res_status(200, res)
|
|
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
local cache_key2 = res.headers["X-Cache-Key"]
|
|
assert.not_same(cache_key1, cache_key2)
|
|
end)
|
|
|
|
it("differentiates caches between instances", function()
|
|
local res = assert(get(client, "route-2.test"))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
local cache_key1 = res.headers["X-Cache-Key"]
|
|
assert.matches("^[%w%d]+$", cache_key1)
|
|
assert.equals(64, #cache_key1)
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key1) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(get(client, "route-2.test"))
|
|
|
|
local cache_key2 = res.headers["X-Cache-Key"]
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
assert.same(cache_key1, cache_key2)
|
|
end)
|
|
|
|
it("uses request params as part of the cache key", function()
|
|
local res = assert(client:get("/get?a=b&b=c", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/get?a=c", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/get?b=c&a=b", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/get?a&b", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/get?a&b", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("can focus only in a subset of the query arguments", function()
|
|
local res = assert(client:get("/get?foo=b&b=c", {
|
|
headers = {
|
|
host = "route-12.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/get?b=d&foo=b", {
|
|
headers = {
|
|
host = "route-12.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("uses headers if instructed to do so", function()
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-11.test",
|
|
foo = "bar",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-11.test",
|
|
foo = "bar",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-11.test",
|
|
foo = "baz",
|
|
}
|
|
}))
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
describe("handles authenticated routes", function()
|
|
it("by ignoring cache if the request is unauthenticated", function()
|
|
local res = assert(get(client, "route-5.test"))
|
|
|
|
assert.res_status(401, res)
|
|
assert.is_nil(res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("by maintaining a separate cache per consumer", function()
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-5.test",
|
|
apikey = "bob",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-5.test",
|
|
apikey = "bob",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
local res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-5.test",
|
|
apikey = "alice",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/get", {
|
|
headers = {
|
|
host = "route-5.test",
|
|
apikey = "alice",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
|
|
end)
|
|
end)
|
|
|
|
describe("bypasses cache for uncacheable requests: ", function()
|
|
it("request method", function()
|
|
local res = assert(client:post("/post", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
["Content-Type"] = "application/json",
|
|
},
|
|
{
|
|
foo = "bar",
|
|
},
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
end)
|
|
end)
|
|
|
|
describe("bypasses cache for uncacheable responses:", function()
|
|
it("response status", function()
|
|
local res = assert(client:get("/status/418", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
},
|
|
}))
|
|
|
|
assert.res_status(418, res)
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("response content type", function()
|
|
local res = assert(client:get("/xml", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
},
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
end)
|
|
end)
|
|
|
|
describe("caches non-default", function()
|
|
it("request methods", function()
|
|
local res = assert(client:post("/post", {
|
|
headers = {
|
|
host = "route-10.test",
|
|
["Content-Type"] = "application/json",
|
|
},
|
|
{
|
|
foo = "bar",
|
|
},
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
--local cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:post("/post", {
|
|
headers = {
|
|
host = "route-10.test",
|
|
["Content-Type"] = "application/json",
|
|
},
|
|
{
|
|
foo = "bar",
|
|
},
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("response status", function()
|
|
local res = assert(client:get("/status/417", {
|
|
headers = {
|
|
host = "route-10.test",
|
|
},
|
|
}))
|
|
|
|
assert.res_status(417, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
res = assert(client:get("/status/417", {
|
|
headers = {
|
|
host = "route-10.test",
|
|
},
|
|
}))
|
|
|
|
assert.res_status(417, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
end)
|
|
|
|
describe("displays Kong core headers:", function()
|
|
it("X-Kong-Proxy-Latency", function()
|
|
local res = assert(client:get("/get?show-me=proxy-latency", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
assert.matches("^%d+$", res.headers["X-Kong-Proxy-Latency"])
|
|
|
|
res = assert(client:get("/get?show-me=proxy-latency", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
assert.matches("^%d+$", res.headers["X-Kong-Proxy-Latency"])
|
|
end)
|
|
|
|
it("X-Kong-Upstream-Latency", function()
|
|
local res = assert(client:get("/get?show-me=upstream-latency", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
assert.matches("^%d+$", res.headers["X-Kong-Upstream-Latency"])
|
|
--cache_key = res.headers["X-Cache-Key"]
|
|
|
|
-- wait until the underlying strategy converges
|
|
--strategy_wait_until(policy, function()
|
|
-- return strategy:fetch(cache_key) ~= nil
|
|
--end, TIMEOUT)
|
|
|
|
res = assert(client:get("/get?show-me=upstream-latency", {
|
|
headers = {
|
|
host = "route-1.test",
|
|
}
|
|
}))
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
assert.matches("^%d+$", res.headers["X-Kong-Upstream-Latency"])
|
|
end)
|
|
end)
|
|
|
|
describe("content-type", function()
|
|
it("should cache a request with wildcard content_type(*/*)", function()
|
|
local request = {
|
|
method = "GET",
|
|
path = "/xml",
|
|
headers = {
|
|
host = "route-17.test",
|
|
},
|
|
}
|
|
|
|
local res = assert(client:send(request))
|
|
assert.res_status(200, res)
|
|
assert.same("application/xml", res.headers["Content-Type"])
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
local res = assert(client:send(request))
|
|
assert.res_status(200, res)
|
|
assert.same("application/xml", res.headers["Content-Type"])
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("should not cache a request while parameter is not match", function()
|
|
local res = assert(client:send {
|
|
method = "GET",
|
|
path = "/xml",
|
|
headers = {
|
|
host = "route-18.test",
|
|
},
|
|
})
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("application/xml", res.headers["Content-Type"])
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
|
|
it("should not cause error while upstream returns a invalid content type", function()
|
|
local res = assert(client:send {
|
|
method = "GET",
|
|
path = "/response-headers?Content-Type=application/xml;",
|
|
headers = {
|
|
host = "route-18.test",
|
|
},
|
|
})
|
|
|
|
assert.res_status(200, res)
|
|
assert.same("application/xml;", res.headers["Content-Type"])
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
end)
|
|
|
|
it("should not cause error while config.content_type has invalid element", function()
|
|
local res, err = client:send {
|
|
method = "GET",
|
|
path = "/xml",
|
|
headers = {
|
|
host = "route-19.test",
|
|
},
|
|
}
|
|
|
|
assert.is_nil(err)
|
|
assert.res_status(200, res)
|
|
assert.same("application/xml", res.headers["Content-Type"])
|
|
assert.same("Bypass", res.headers["X-Cache-Status"])
|
|
end)
|
|
end)
|
|
|
|
it("ignore uri case in cache_key", function()
|
|
local res = assert(client:send {
|
|
method = "GET",
|
|
path = "/ignore-case/kong",
|
|
headers = {
|
|
host = "route-20.test",
|
|
},
|
|
})
|
|
|
|
local body1 = assert.res_status(404, res)
|
|
assert.same("Miss", res.headers["X-Cache-Status"])
|
|
|
|
local cache_key1 = res.headers["X-Cache-Key"]
|
|
assert.matches("^[%w%d]+$", cache_key1)
|
|
assert.equals(64, #cache_key1)
|
|
|
|
local res = client:send {
|
|
method = "GET",
|
|
path = "/ignore-case/KONG",
|
|
headers = {
|
|
host = "route-20.test",
|
|
},
|
|
}
|
|
|
|
local body2 = assert.res_status(404, res)
|
|
assert.same("Hit", res.headers["X-Cache-Status"])
|
|
local cache_key2 = res.headers["X-Cache-Key"]
|
|
assert.same(cache_key1, cache_key2)
|
|
|
|
assert.same(body1, body2)
|
|
end)
|
|
|
|
it("acknowledge uri case in cache_key", function()
|
|
local res = assert(client:send {
|
|
method = "GET",
|
|
path = "/acknowledge-case/kong",
|
|
headers = {
|
|
host = "route-21.test",
|
|
},
|
|
})
|
|
|
|
assert.res_status(404, res)
|
|
local x_cache_status = assert.response(res).has_header("X-Cache-Status")
|
|
assert.same("Miss", x_cache_status)
|
|
|
|
local cache_key1 = res.headers["X-Cache-Key"]
|
|
assert.matches("^[%w%d]+$", cache_key1)
|
|
assert.equals(64, #cache_key1)
|
|
|
|
res = assert(client:send {
|
|
method = "GET",
|
|
path = "/acknowledge-case/KONG",
|
|
headers = {
|
|
host = "route-21.test",
|
|
},
|
|
})
|
|
|
|
x_cache_status = assert.response(res).has_header("X-Cache-Status")
|
|
local cache_key2 = assert.response(res).has_header("X-Cache-Key")
|
|
assert.same("Miss", x_cache_status)
|
|
assert.not_same(cache_key1, cache_key2)
|
|
end)
|
|
|
|
end)
|
|
end
|