Skip to content

Commit

Permalink
[resty.ctx] library for sharing ctx to subrequests
Browse files Browse the repository at this point in the history
this is needed to share ngx.ctx to subrequests or internal redirects

quite similar to https://github.com/tokers/lua-resty-ctxdump
implementation based on suggestions from openresty/lua-nginx-module#1057
  • Loading branch information
mikz committed Jan 4, 2018
1 parent 0486db5 commit 19b3a9b
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
84 changes: 84 additions & 0 deletions gateway/src/resty/ctx.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
--- resty.ctx
-- Module for sharing ngx.ctx to subrequests.
-- @module resty.ctx

local ffi = require 'ffi'
local debug = require 'debug'
local base = require "resty.core.base"

-- to get FFI definitions
require 'resty.core.ctx'

local registry = debug.getregistry()
local getfenv = getfenv
local C = ffi.C
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local error = error
local tonumber = tonumber

local _M = {
}

--- Return ctx reference number
-- @raise no request found, no request ctx found
-- @treturn int
function _M.ref()
local r = getfenv(0).__ngx_req

if not r then
return error("no request found")
end

local _ = ngx.ctx -- load context

local ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref(r)

if ctx_ref == FFI_NO_REQ_CTX then
return error("no request ctx found")
end

-- The context should not be garbage collected until all the subrequests are completed.
-- That includes internal redirects and post action.

return ctx_ref
end

_M.var = 'ctx_ref'

--- Store ctx reference in ngx.var
-- @tparam ?string var variable name, defaults to ctx_ref
function _M.stash(var)
ngx.var[var or _M.var] = _M.ref()
end

local function get_ctx(ref)
local r = getfenv(0).__ngx_req

if not r then
return error("no request found")
end

local ctx_ref = tonumber(ref)
if not ctx_ref then
return
end

return registry.ngx_lua_ctx_tables[ctx_ref] or error("no request ctx found")
end

--- Apply stored ctx to the current request
-- @tparam ?string var variable name, defaults to ctx_ref
-- @raise no request found
-- @treturn table
function _M.apply(var)
local ctx = get_ctx(ngx.var[var or _M.var])

-- this will actually store the reference again
-- so each request that gets the context applied will hold own reference
-- this is a very safe way to ensure it is not GC'd or released by another requests
ngx.ctx = ctx

return ctx
end

return _M
132 changes: 132 additions & 0 deletions t/resty-ctx.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use lib 't';
use Test::APIcast 'no_plan';

run_tests();

__DATA__
=== TEST 1: get context reference
get context reference number
--- http_config
lua_package_path "$TEST_NGINX_LUA_PATH";
--- config
location = /t {
content_by_lua_block {
ngx.say(require('resty.ctx').ref())
}
}
--- request
GET /t
--- response_body
1
--- no_error_log
[error]
=== TEST 2: stash context reference
--- http_config
lua_package_path "$TEST_NGINX_LUA_PATH";
--- config
set $ctx_ref -1;
location = /t {
rewrite_by_lua_block {
require('resty.ctx').stash()
}
content_by_lua_block {
ngx.say(ngx.var.ctx_ref)
}
}
--- request
GET /t
--- response_body
1
--- no_error_log
[error]
=== TEST 3: apply context reference
--- http_config
lua_package_path "$TEST_NGINX_LUA_PATH";
--- config
set $ctx_ref -1;
location = /t {
rewrite_by_lua_block {
require('resty.ctx').stash()
ngx.ctx.foo = 'bar'
}
content_by_lua_block {
ngx.exec('@redirect')
}
}
location @redirect {
internal;
rewrite_by_lua_block {
require('resty.ctx').apply()
ngx.say(ngx.ctx.foo)
}
}
--- request
GET /t
--- response_body
bar
--- no_error_log
[error]
=== TEST 4: context is not garbage collected
--- http_config
lua_package_path "$TEST_NGINX_LUA_PATH";
--- config
set $ctx_ref -1;
location = /t {
rewrite_by_lua_block {
require('resty.ctx').stash()
ngx.ctx.foo = 'bar'
}
content_by_lua_block {
ngx.exec('@redirect')
}
}
location @redirect {
internal;
rewrite_by_lua_block {
collectgarbage()
require('resty.ctx').apply()
}
post_action @out_of_band;
content_by_lua_block {
collectgarbage()
ngx.say(ngx.ctx.foo)
}
}
location @out_of_band {
rewrite_by_lua_block {
collectgarbage()
require('resty.ctx').apply()
}
content_by_lua_block {
collectgarbage()
ngx.say(ngx.ctx.foo)
}
}
--- request
GET /t
--- response_body
bar

0 comments on commit 19b3a9b

Please sign in to comment.