├── .gitattributes ├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── dist.ini ├── lib └── resty │ └── ctxdump.lua ├── lua-resty-ctxdump-0.1-0.rockspec ├── t ├── 00-sanity.t └── 01-bug.t └── util └── lua-releng /.gitattributes: -------------------------------------------------------------------------------- 1 | *.t linguist-language=Text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | t/servroot/* 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | perl: 3 | - 5.10 4 | cache: 5 | - apt 6 | - ccache 7 | env: 8 | - V_OPENRESTY=1.9.15.1 9 | install: 10 | - cpanm -v --notest Test::Nginx 11 | before_script: 12 | - sudo apt-get update -q 13 | - sudo apt-get install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev -y 14 | - sudo apt-get install make build-essential lua5.1 -y 15 | - wget https://openresty.org/download/openresty-$V_OPENRESTY.tar.gz 16 | - tar xzf openresty-$V_OPENRESTY.tar.gz 17 | - cd openresty-$V_OPENRESTY && ./configure && make && sudo make install && cd .. 18 | script: 19 | - make test 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OPENRESTY_PREFIX=/usr/local/openresty 2 | 3 | PREFIX ?= /usr/local 4 | LUA_INCLUDE_DIR ?= $(PREFIX)/include 5 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) 6 | INSTALL ?= install 7 | 8 | .PHONY: all test install 9 | 10 | all: ; 11 | 12 | install: all 13 | $(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty/ 14 | $(INSTALL) lib/resty/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty/ 15 | 16 | test: all 17 | sudo cp lib/resty/ctxdump.lua $(OPENRESTY_PREFIX)/lualib/resty 18 | PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t/ 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | lua-resty-ctxdump - stash and apply the ngx.ctx, avoiding being destoried after Nginx internal redirect happens. 5 | 6 | ![Build Status](https://travis-ci.org/tokers/lua-resty-ctxdump.svg?branch=master) 7 | 8 | Table of Contents 9 | ================= 10 | 11 | * [Name](#name) 12 | * [Status](#status) 13 | * [Synopsis](#synopsis) 14 | * [Methods](#methods) 15 | * [stash_ngx_ctx](#stash_ngx_ctx) 16 | * [apply_ngx_ctx](#apply_ngx_ctx) 17 | * [Author](#author) 18 | * [Copyright and License](#copyright-and-license) 19 | * [See Also](#see-also) 20 | 21 | Status 22 | ====== 23 | 24 | Probably production ready in most cases, though not yet proven in the wild. Please check the issues list and let me know if you have any problems / questions. 25 | 26 | Synopsis 27 | ======== 28 | 29 | ```lua 30 | 31 | location /t1 { 32 | set $ctx_ref ""; 33 | content_by_lua_block { 34 | local ctxdump = require "resty.ctxdump" 35 | ngx.ctx = { 36 | Date = "Wed May 3 15:18:04 CST 2017", 37 | Site = "unknown" 38 | } 39 | ngx.var.ctx_ref = ctxdump.stash_ngx_ctx() 40 | ngx.exec("/t2") 41 | } 42 | } 43 | 44 | location /t2 { 45 | internal; 46 | content_by_lua_block { 47 | local ctxdump = require "resty.ctxdump" 48 | ngx.ctx = ctxdump.apply_ngx_ctx(ngx.var.ctx_ref) 49 | ngx.say("Date: " .. ngx.ctx["Date"] .. " Site: " .. ngx.ctx["Site"]) 50 | } 51 | } 52 | 53 | ``` 54 | 55 | Methods 56 | ======= 57 | 58 | stash_ngx_ctx 59 | ------------- 60 | 61 | **syntax:** *ref = stash_ngx_ctx()*
62 | **phase:** *init_worker_by_lua\*, set_by_lua\*, rewrite_by_lua\*, access_by_lua\*, 63 | content_by_lua\*, header_filter_by_lua\*, body_filter_by_lua\*, log_by_lua\*, 64 | ngx.timer.\*, balancer_by_lua\* 65 | 66 | Reference the `ngx.ctx`, returns an anchor(a new reference maintained by lua-resty-ctxdump). 67 | 68 | Note: `stash_ngx_ctx` and `apply_ngx_ctx` must be called in pairs, otherwise memory leak will happen! See [apply_ngx_ctx](#apply_ngx_ctx). 69 | 70 | apply_ngx_ctx 71 | ------------- 72 | 73 | **syntax:** *old_ngx_ctx = apply_ngx_ctx(ref)*
74 | **phase:** *init_worker_by_lua\*, set_by_lua\*, rewrite_by_lua\*, access_by_lua\*, 75 | content_by_lua\*, header_filter_by_lua\*, body_filter_by_lua\*, log_by_lua\*, 76 | ngx.timer.\*, balancer_by_lua\* 77 | 78 | fetch the old `ngx.ctx` with the anchor returns from `stash_ngx_ctx `. After that, the anchor will be out of work. 79 | 80 | Note: `stash_ngx_ctx` and `apply_ngx_ctx ` must be called in pairs, otherwise memory leak will happen! See [stash_ngx_ctx](#stash_ngx_ctx). 81 | 82 | 83 | Author 84 | ====== 85 | 86 | Alex Zhang(张超) tokers@apache.org, @api7.ai. 87 | 88 | 89 | Copyright and License 90 | ===================== 91 | 92 | The bundle itself is licensed under the 2-clause BSD license. 93 | 94 | Copyright (c) 2017-2021, Alex Zhang. 95 | 96 | This module is licensed under the terms of the BSD license. 97 | 98 | Redistribution and use in source and binary forms, with or without 99 | modification, are permitted provided that the following conditions are 100 | met: 101 | 102 | * Redistributions of source code must retain the above copyright notice, this 103 | list of conditions and the following disclaimer. 104 | * Redistributions in binary form must reproduce the above copyright notice, this 105 | list of conditions and the following disclaimer in the documentation and/or 106 | other materials provided with the distribution. 107 | 108 | 109 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 110 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 111 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 112 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 113 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 114 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 115 | 116 | See Also 117 | ======== 118 | 119 | * upyun-resty: https://github.com/upyun/upyun-resty 120 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = lua-resty-ctxdump 2 | abstract = Stash and apply the old ngx.ctx for avoiding being destoried after Nginx internal redirect happens. 3 | version = 0.03 4 | author = Alex Zhang(张超) zchao1995@gmail.com, UPYUN Inc. 5 | is_original = yes 6 | license = 2bsd 7 | lib_dir = lib 8 | repo_link = https://github.com/tokers/lua-resty-ctxdump 9 | -------------------------------------------------------------------------------- /lib/resty/ctxdump.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) UPYUN, Inc. 2 | -- Copyright (C) Alex Zhang 3 | 4 | local tonumber = tonumber 5 | local _M = { _VERSION = "0.03" } 6 | local memo = {} 7 | local FREE_LIST_REF = 0 8 | 9 | 10 | local function ref_in_table(tb, key) 11 | if key == nil then 12 | return -1 13 | end 14 | 15 | local ref = tb[FREE_LIST_REF] 16 | if ref and ref ~= FREE_LIST_REF then 17 | tb[FREE_LIST_REF] = tb[ref] 18 | else 19 | ref = #tb + 1 20 | end 21 | 22 | tb[ref] = key 23 | 24 | return ref 25 | end 26 | 27 | 28 | function _M.stash_ngx_ctx() 29 | local ctx_ref = ref_in_table(memo, ngx.ctx) 30 | return ctx_ref 31 | end 32 | 33 | 34 | function _M.apply_ngx_ctx(ref) 35 | ref = tonumber(ref) 36 | if not ref or ref <= FREE_LIST_REF then 37 | return nil, "bad ref value" 38 | end 39 | 40 | local old_ngx_ctx = memo[ref] 41 | 42 | -- dereference 43 | memo[ref] = memo[FREE_LIST_REF] 44 | memo[FREE_LIST_REF] = ref 45 | 46 | return old_ngx_ctx 47 | end 48 | 49 | 50 | return _M 51 | -------------------------------------------------------------------------------- /lua-resty-ctxdump-0.1-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-resty-ctxdump" 2 | version = "0.1-0" 3 | source = { 4 | url = "git://github.com/tokers/lua-resty-ctxdump", 5 | tag = "v0.1" 6 | } 7 | 8 | description = { 9 | summary = "Stash and apply the ngx.ctx, avoiding being destoried after Nginx internal redirect happens", 10 | homepage = "https://github.com/tokers/lua-resty-ctxdump", 11 | license = "2-clause BSD license", 12 | maintainer = "Chao Zhang " 13 | } 14 | 15 | build = { 16 | type = "builtin", 17 | modules = { 18 | ["resty.ctxdump"] = "lib/resty/ctxdump.lua" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /t/00-sanity.t: -------------------------------------------------------------------------------- 1 | use lib 'lib'; 2 | use Test::Nginx::Socket 'no_plan'; 3 | 4 | repeat_each(2); 5 | run_tests(); 6 | 7 | __DATA__ 8 | 9 | === TEST 1: stash and apply the old ngx.ctx 10 | 11 | --- config 12 | 13 | location = /t1 { 14 | set $ctx_ref ""; 15 | content_by_lua_block { 16 | local ctxdump = require "resty.ctxdump" 17 | ngx.ctx = { 18 | today = "wednesday", 19 | launch = "steak", 20 | drink = "wine", 21 | } 22 | 23 | ngx.var.ctx_ref = ctxdump.stash_ngx_ctx() 24 | ngx.exec("/t2") 25 | } 26 | } 27 | 28 | location = /t2 { 29 | content_by_lua_block { 30 | local ctxdump = require "resty.ctxdump" 31 | ngx.ctx = ctxdump.apply_ngx_ctx(ngx.var.ctx_ref) 32 | ngx.var.ctx_ref = "" 33 | ngx.say("today ", ngx.ctx["today"], " launch ", ngx.ctx["launch"], 34 | " drink ", ngx.ctx["drink"]) 35 | } 36 | } 37 | 38 | --- request 39 | GET /t1 40 | 41 | --- error_code: 200 42 | 43 | --- response_body 44 | today wednesday launch steak drink wine 45 | -------------------------------------------------------------------------------- /t/01-bug.t: -------------------------------------------------------------------------------- 1 | use lib 'lib'; 2 | use Test::Nginx::Socket 'no_plan'; 3 | 4 | repeat_each(2); 5 | run_tests(); 6 | 7 | __DATA__ 8 | 9 | === TEST 1: memo will be swell. 10 | 11 | --- config 12 | 13 | location = /t1 { 14 | set $ctx_ref ""; 15 | content_by_lua_block { 16 | local ctxdump = require "resty.ctxdump" 17 | ngx.ctx = { 18 | today = "wednesday", 19 | launch = "steak", 20 | drink = "wine", 21 | } 22 | 23 | ngx.var.ctx_ref = ctxdump.stash_ngx_ctx() 24 | ngx.exec("/t2") 25 | } 26 | } 27 | 28 | location = /t2 { 29 | content_by_lua_block { 30 | local ctxdump = require "resty.ctxdump" 31 | ngx.ctx = ctxdump.apply_ngx_ctx(ngx.var.ctx_ref) 32 | ngx.say("ref is ", ngx.var.ctx_ref) 33 | ngx.var.ctx_ref = "" 34 | } 35 | } 36 | 37 | --- request 38 | GET /t1 39 | 40 | --- error_code: 200 41 | 42 | --- response_body 43 | ref is 1 44 | 45 | --- request 46 | GET /t1 47 | 48 | --- error_code: 200 49 | 50 | --- response_body 51 | ref is 1 52 | -------------------------------------------------------------------------------- /util/lua-releng: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub file_contains ($$); 7 | 8 | my $version; 9 | for my $file (map glob, qw{ *.lua lib/*.lua lib/*/*.lua lib/*/*/*.lua lib/*/*/*/*.lua lib/*/*/*/*/*.lua }) { 10 | # Check the sanity of each .lua file 11 | open my $in, $file or 12 | die "ERROR: Can't open $file for reading: $!\n"; 13 | my $found_ver; 14 | while (<$in>) { 15 | my ($ver, $skipping); 16 | if (/(?x) (?:_VERSION) \s* = .*? ([\d\.]*\d+) (.*? SKIP)?/) { 17 | my $orig_ver = $ver = $1; 18 | $found_ver = 1; 19 | # $skipping = $2; 20 | $ver =~ s{^(\d+)\.(\d{3})(\d{3})$}{join '.', int($1), int($2), int($3)}e; 21 | warn "$file: $orig_ver ($ver)\n"; 22 | } elsif (/(?x) (?:_VERSION) \s* = \s* ([a-zA-Z_]\S*)/) { 23 | warn "$file: $1\n"; 24 | $found_ver = 1; 25 | last; 26 | } 27 | if ($ver and $version and !$skipping) { 28 | if ($version ne $ver) { 29 | # die "$file: $ver != $version\n"; 30 | } 31 | } elsif ($ver and !$version) { 32 | $version = $ver; 33 | } 34 | } 35 | if (!$found_ver) { 36 | warn "WARNING: No \"_VERSION\" or \"version\" field found in `$file`.\n"; 37 | } 38 | close $in; 39 | print "Checking use of Lua global variables in file $file ...\n"; 40 | my $output = `luac -p -l $file | grep ETGLOBAL | grep -vE 'require|type|tostring|error|ngx\$|ndk|jit|setmetatable|getmetatable|string|table|io|os|print|tonumber|math|pcall|xpcall|unpack|pairs|ipairs|assert|module|package|coroutine|[gs]etfenv|next|select|rawset|rawget|debug'`; 41 | if ($output) { 42 | print $output; 43 | exit 1; 44 | } 45 | #file_contains($file, "attempt to write to undeclared variable"); 46 | system("grep -H -n -E --color '.{120}' $file"); 47 | } 48 | 49 | sub file_contains ($$) { 50 | my ($file, $regex) = @_; 51 | open my $in, $file 52 | or die "Cannot open $file fo reading: $!\n"; 53 | my $content = do { local $/; <$in> }; 54 | close $in; 55 | #print "$content"; 56 | return scalar ($content =~ /$regex/); 57 | } 58 | 59 | if (-d 't') { 60 | for my $file (map glob, qw{ t/*.t t/*/*.t t/*/*/*.t }) { 61 | system(qq{grep -H -n --color -E '\\--- ?(ONLY|LAST)' $file}); 62 | } 63 | } 64 | --------------------------------------------------------------------------------