Skip to content

Commit

Permalink
feat(core) new rewrite_by_lua handler for plugins
Browse files Browse the repository at this point in the history
Signed-off-by: Thibault Charbonnier <[email protected]>

This handler exposes the Nginx rewrite phase. This handler is called for
all loaded plugins (since the rewrite phase is executed prior to API
matching).

From Kong#2354
  • Loading branch information
subnetmarco authored and thibaultcha committed Apr 29, 2017
1 parent 72d0138 commit d89343e
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 10 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@

### Added

:fireworks: Plugins can implement a new `rewrite_by_lua` handler to execute
code in the Nginx rewrite phase. This phase is executed prior to matching a
registered Kong API, and prior to any authentication plugin. As such, plugins
implementing this phase don't have to be configured via the Admin API to be
executed. Enabled plugins (loaded via the `custom_plugins` Kong configuration
value) will execute their `rewrite_by_lua` handler for each request.
[#2354](https://github.com/Mashape/kong/pull/2354)
- Ability for the client to chose whether the upstream request (Kong <->
upstream) should contain a trailing slash in its URI. Prior to this change,
Kong 0.10 would unconditionally append a trailing slash to all upstream
Expand Down
9 changes: 9 additions & 0 deletions kong/core/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ return {
certificate.execute()
end
},
rewrite = {
before = function()
ngx.ctx.KONG_REWRITE_START = get_now()
end,
after = function ()
local ctx = ngx.ctx
ctx.KONG_REWRITE_TIME = get_now() - ctx.KONG_REWRITE_START -- time spent in Kong's rewrite_by_lua
end
},
access = {
before = function()
if not router then
Expand Down
11 changes: 8 additions & 3 deletions kong/core/plugins_iterator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ local function iter_plugins_for_req(loaded_plugins, access_or_cert_ctx)
local function get_next()
i = i + 1
local plugin = loaded_plugins[i]
if plugin and ctx.api then
local api = ctx.api
if plugin then
-- load the plugin configuration in early phases
if access_or_cert_ctx then

Expand All @@ -75,15 +76,19 @@ local function iter_plugins_for_req(loaded_plugins, access_or_cert_ctx)
-- Search API and Consumer specific, or consumer specific
local consumer_id = (ctx.authenticated_consumer or empty).id
if consumer_id and plugin.schema and not plugin.schema.no_consumer then
plugin_configuration = load_plugin_configuration(ctx.api.id, consumer_id, plugin.name)
if api then
plugin_configuration = load_plugin_configuration(api.id, consumer_id, plugin.name)
end
if not plugin_configuration then
plugin_configuration = load_plugin_configuration(nil, consumer_id, plugin.name)
end
end

if not plugin_configuration then
-- Search API specific, or global
plugin_configuration = load_plugin_configuration(ctx.api.id, nil, plugin.name)
if api then
plugin_configuration = load_plugin_configuration(api.id, nil, plugin.name)
end
if not plugin_configuration then
plugin_configuration = load_plugin_configuration(nil, nil, plugin.name)
end
Expand Down
13 changes: 13 additions & 0 deletions kong/kong.lua
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,19 @@ function Kong.balancer()
end
end

function Kong.rewrite()
core.rewrite.before()

-- we're just using the iterator, as in this rewrite phase no consumer nor
-- api will have been identified, hence we'll just be executing the global
-- plugins
for plugin, plugin_conf in plugins_iterator(singletons.loaded_plugins, true) do
plugin.handler:rewrite(plugin_conf)
end

core.rewrite.after()
end

function Kong.access()
core.access.before()

Expand Down
19 changes: 13 additions & 6 deletions kong/plugins/base_plugin.lua
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
local Object = require "kong.vendor.classic"
local BasePlugin = Object:extend()

local ngx_log = ngx.log
local DEBUG = ngx.DEBUG

function BasePlugin:new(name)
self._name = name
end

function BasePlugin:init_worker()
ngx.log(ngx.DEBUG, " executing plugin \""..self._name.."\": init_worker")
ngx_log(DEBUG, "executing plugin \"", self._name, "\": init_worker")
end

function BasePlugin:certificate()
ngx.log(ngx.DEBUG, " executing plugin \""..self._name.."\": certificate")
ngx_log(DEBUG, "executing plugin \"", self._name, "\": certificate")
end

function BasePlugin:rewrite()
ngx_log(DEBUG, "executing plugin \"", self._name, "\": rewrite")
end

function BasePlugin:access()
ngx.log(ngx.DEBUG, " executing plugin \""..self._name.."\": access")
ngx_log(DEBUG, "executing plugin \"", self._name, "\": access")
end

function BasePlugin:header_filter()
ngx.log(ngx.DEBUG, " executing plugin \""..self._name.."\": header_filter")
ngx_log(DEBUG, "executing plugin \"", self._name, "\": header_filter")
end

function BasePlugin:body_filter()
ngx.log(ngx.DEBUG, " executing plugin \""..self._name.."\": body_filter")
ngx_log(DEBUG, "executing plugin \"", self._name, "\": body_filter")
end

function BasePlugin:log()
ngx.log(ngx.DEBUG, " executing plugin \""..self._name.."\": log")
ngx_log(DEBUG, "executing plugin \"", self._name, "\": log")
end

return BasePlugin
3 changes: 2 additions & 1 deletion kong/plugins/log-serializers/basic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ function _M.serialize(ngx)
tries = addr.tries,
latencies = {
kong = (ngx.ctx.KONG_ACCESS_TIME or 0) +
(ngx.ctx.KONG_RECEIVE_TIME or 0),
(ngx.ctx.KONG_RECEIVE_TIME or 0) +
(ngx.ctx.KONG_REWRITE_TIME or 0),
proxy = ngx.ctx.KONG_WAITING_TIME or -1,
request = ngx.var.request_time * 1000
},
Expand Down
4 changes: 4 additions & 0 deletions kong/templates/nginx_kong.lua
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ server {
set $upstream_host nil;
set $upstream_scheme nil;
rewrite_by_lua_block {
kong.rewrite()
}
access_by_lua_block {
kong.access()
}
Expand Down
151 changes: 151 additions & 0 deletions spec/02-integration/05-proxy/10-handler_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
local helpers = require "spec.helpers"

describe("OpenResty phases", function()
describe("rewrite_by_lua", function()
describe("enabled on all APIs", function()
local api_client, proxy_client

setup(function()
-- insert plugin-less api and a global plugin
assert(helpers.dao.apis:insert {
name = "rewrite1",
hosts = { "rewriter1.com" },
upstream_url = "http://mockbin.org"
})
assert(helpers.dao.plugins:insert {
name = "rewriter",
config = {
value = "global plugin",
},
})

assert(helpers.start_kong({
custom_plugins = "rewriter",
}))

api_client = helpers.admin_client()
proxy_client = helpers.proxy_client()
end)

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

it("runs", function()
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
host = "rewriter1.com",
},
})
assert.response(res).has.status(200)
local value = assert.request(res).has.header("rewriter")
assert.equal("global plugin", value)
end)
end)

describe("enabled on a specific APIs", function()
local api_client, proxy_client

setup(function()
-- api specific plugin
local api2 = assert(helpers.dao.apis:insert {
name = "rewrite2",
hosts = { "rewriter2.com" },
upstream_url = "http://mockbin.org"
})
assert(helpers.dao.plugins:insert {
api_id = api2.id,
name = "rewriter",
config = {
value = "api-specific plugin",
},
})

assert(helpers.start_kong({
custom_plugins = "rewriter",
}))

api_client = helpers.admin_client()
proxy_client = helpers.proxy_client()
end)

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

it("doesn't run", function()
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
host = "rewriter2.com",
},
})
assert.response(res).has.status(200)
assert.request(res).has.no.header("rewriter")
end)
end)

describe("enabled on a specific Consumers", function()
local api_client, proxy_client

setup(function()
-- consumer specific plugin
local api3 = assert(helpers.dao.apis:insert {
name = "rewrite3",
hosts = { "rewriter3.com" },
upstream_url = "http://mockbin.org"
})
assert(helpers.dao.plugins:insert {
api_id = api3.id,
name = "key-auth",
})
local consumer3 = assert(helpers.dao.consumers:insert {
username = "test-consumer",
})
assert(helpers.dao.keyauth_credentials:insert {
key = "kong",
consumer_id = consumer3.id
})
assert(helpers.dao.plugins:insert {
consumer_id = consumer3.id,
name = "rewriter",
config = {
value = "consumer-specific plugin",
},
})

assert(helpers.start_kong({
custom_plugins = "rewriter",
}))

api_client = helpers.admin_client()
proxy_client = helpers.proxy_client()
end)

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

it("doesn't run", function()
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
host = "rewriter3.com",
apikey = "kong"
},
})
assert.response(res).has.status(200)
local value = assert.request(res).has.header("x-consumer-username")
assert.equal("test-consumer", value)
assert.request(res).has.no.header("rewriter")
end)
end)
end)
end)
19 changes: 19 additions & 0 deletions spec/fixtures/custom_plugins/kong/plugins/rewriter/handler.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- a plugin fixture to test running of the rewrite phase handler.

local BasePlugin = require "kong.plugins.base_plugin"

local Rewriter = BasePlugin:extend()

Rewriter.PRIORITY = 1000

function Rewriter:new()
Rewriter.super.new(self, "rewriter")
end

function Rewriter:rewrite(conf)
Rewriter.super.access(self)

ngx.req.set_header("rewriter", conf.value)
end

return Rewriter
5 changes: 5 additions & 0 deletions spec/fixtures/custom_plugins/kong/plugins/rewriter/schema.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
return {
fields = {
value = { typ = "string" }
}
}

0 comments on commit d89343e

Please sign in to comment.