Skip to content

Commit

Permalink
Merge pull request Kong#42 from Mashape/feature/cache
Browse files Browse the repository at this point in the history
Feature/cache
  • Loading branch information
subnetmarco committed Mar 2, 2015
2 parents 2e05e88 + b4940c1 commit d3e5835
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 32 deletions.
4 changes: 4 additions & 0 deletions config.default/kong.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ databases_available:
timeout: 1000
keyspace: kong
keepalive: 60000

# Cache configuration
cache:
expiration: 5 # In seconds
2 changes: 2 additions & 0 deletions config.default/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ http {
resolver 8.8.8.8;
charset UTF-8;

lua_shared_dict cache 512m;

init_by_lua "kong = require 'kong'; kong.init()";

server {
Expand Down
13 changes: 13 additions & 0 deletions spec/unit/utils_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ local cjson = require "cjson"

describe("Utils #utils", function()

describe("Cache", function()
it("should return a valid API cache key", function()
assert.are.equal("apis/httpbin.org", utils.cache_api_key("httpbin.org"))
end)
it("should return a valid PLUGIN cache key", function()
assert.are.equal("plugins/authentication/api123/app123", utils.cache_plugin_key("authentication", "api123", "app123"))
assert.are.equal("plugins/authentication/api123", utils.cache_plugin_key("authentication", "api123"))
end)
it("should return a valid Application cache key", function()
assert.are.equal("applications/username", utils.cache_application_key("username"))
end)
end)

describe("HTTP", function()
describe("GET", function()
it("should return a valid GET response", function()
Expand Down
5 changes: 5 additions & 0 deletions src/kong/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ return {
RATELIMIT_LIMIT = "X-RateLimit-Limit",
RATELIMIT_REMAINING = "X-RateLimit-Remaining"
},
CACHE = {
APIS = "apis",
PLUGINS = "plugins",
APPLICATIONS = "applications"
},
AUTHENTICATION = {
QUERY = "query",
BASIC = "basic",
Expand Down
37 changes: 28 additions & 9 deletions src/kong/core/access.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
local stringy = require "stringy"
local constants = require "kong.constants"
local url = require("socket.url")

local _M = {}

Expand All @@ -16,27 +18,44 @@ local function get_backend_url(api)
return result
end

function get_host_header(val)
local parsed_url = url.parse(val)

local port
if parsed_url.port then
port = parsed_url.port
elseif parsed_url.scheme == "https" then
port = 443
end

return parsed_url.host..(port and ":"..port or "")
end

local function skip_authentication(headers)
-- Skip upload request that expect a 100 Continue response
return headers["expect"] and _M.starts_with(headers["expect"], "100")
end

function _M.execute(conf)
-- Retrieving the API from the Host that has been requested
local apis, err = dao.apis:find_by_keys({public_dns = stringy.split(ngx.var.http_host, ":")[1]})
if err then
ngx.log(ngx.ERR, err.message)
utils.show_error(500)
elseif not apis or #apis == 0 then
utils.not_found("API not found")
end
local host = stringy.strip(stringy.split(ngx.var.http_host, ":")[1])

local api = apis[1]
local api = utils.cache_get_and_set(utils.cache_api_key(host), function()
local apis, err = dao.apis:find_by_keys({public_dns = host})
if err then
ngx.log(ngx.ERR, err.message)
utils.show_error(500)
elseif not apis or #apis == 0 then
utils.not_found("API not found")
end
return apis[1]
end)

-- Setting the backend URL for the proxy_pass directive
ngx.var.backend_url = get_backend_url(api) .. ngx.var.request_uri

-- TODO: Move this away from here
ngx.req.set_header("host", get_host_header(ngx.var.backend_url))

-- There are some requests whose authentication needs to be skipped
if skip_authentication(ngx.req.get_headers()) then
return -- Returning and keeping the Lua code running to the next handler
Expand Down
18 changes: 11 additions & 7 deletions src/kong/plugins/authentication/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,17 @@ function _M.execute(conf)

-- Make sure we are not sending an empty table to find_by_keys
if public_key then
local applications, err = dao.applications:find_by_keys { public_key = public_key }
if err then
ngx.log(ngx.ERR, err.message)
utils.show_error(500)
elseif #applications > 0 then
application = applications[1]
end
application = utils.cache_get_and_set(utils.cache_application_key(public_key), function()
local applications, err = dao.applications:find_by_keys { public_key = public_key }
local result
if err then
ngx.log(ngx.ERR, err.message)
utils.show_error(500)
elseif #applications > 0 then
result = applications[1]
end
return result
end)
end

if not validate_credentials[conf.authentication_type](application, public_key, secret_key) then
Expand Down
64 changes: 64 additions & 0 deletions src/kong/tools/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,70 @@ function _M.create_timer(func, data)
end
end

--
-- Cache utils
--

function _M.cache_set(key, value, exptime)
if exptime == nil then exptime = 0 end -- By default never expire
local cache = ngx.shared.cache
if value then
value = cjson.encode(value)
end
if ngx then
ngx.log(ngx.DEBUG, " saving cache key \""..key.."\": "..value)
end
local succ, err, forcible = cache:set(key, value, exptime)
return succ, err, forcible
end

function _M.cache_get(key)
if ngx then
ngx.log(ngx.DEBUG, " Try to get cache key \""..key.."\"")
end

local cache = ngx.shared.cache
local value, flags = cache:get(key)
if value then
if ngx then
ngx.log(ngx.DEBUG, " Found cache value for key \""..key.."\": "..value)
end
value = cjson.decode(value)
end
return value, flags
end

function _M.cache_delete(key)
local cache = ngx.shared.cache
cache:delete(key)
end

function _M.cache_api_key(host)
return constants.CACHE.APIS.."/"..host
end

function _M.cache_plugin_key(name, api_id, application_id)
return constants.CACHE.PLUGINS.."/"..name.."/"..api_id..(application_id and "/"..application_id or "")
end

function _M.cache_application_key(public_key)
return constants.CACHE.APPLICATIONS.."/"..public_key
end

function _M.cache_get_and_set(key, cb)
local val = _M.cache_get(key)
if not val then
val = cb()
if val then
local succ, err, forcible = _M.cache_set(key, val)
if not succ and ngx then
ngx.log(ngx.ERR, err)
end
end
end
return val
end

--
-- Disk I/O utils
--
Expand Down
38 changes: 22 additions & 16 deletions src/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,31 @@ local plugins = {}
local _M = {}

local function load_plugin_conf(api_id, application_id, plugin_name)
local rows, err = dao.plugins:find_by_keys {
api_id = api_id,
application_id = application_id ~= nil and application_id or constants.DATABASE_NULL_ID,
name = plugin_name
}
local cache_key = utils.cache_plugin_key(plugin_name, api_id, application_id)

local plugin = utils.cache_get_and_set(cache_key, function()
local rows, err = dao.plugins:find_by_keys {
api_id = api_id,
application_id = application_id ~= nil and application_id or constants.DATABASE_NULL_ID,
name = plugin_name
}
if err then
ngx.log(ngx.ERR, err.message)
utils.show_error(500)
end

if err then
ngx.log(ngx.ERR, err.message)
utils.show_error(500)
end
if #rows > 0 then
return table.remove(rows, 1)
else
return {null=true}
end
end)

if #rows > 0 then
local plugin = table.remove(rows, 1)
if plugin.enabled then
return plugin
end
if plugin and not plugin.null and plugin.enabled then
return plugin
else
return nil
end

return nil
end

function _M.init()
Expand Down

0 comments on commit d3e5835

Please sign in to comment.