Merge commit '36023392a6e3c8fb6aebb46140db759e61da220e' as 'src/deps/src/lua-nginx-module'

This commit is contained in:
Théophile Diot 2023-06-30 15:38:24 -04:00
commit 29d135bdbc
401 changed files with 204720 additions and 0 deletions

View File

@ -0,0 +1 @@
*.t linguist-language=Text

View File

@ -0,0 +1,23 @@
This place is for bug reports and development discussions only. For general questions and
discussions, please join the openresty-en mailing list instead: https://openresty.org/en/community.html
Ensure you have provided the following details while reporting a problem:
* The exact version of the related software, including but not limited to the OpenResty version
(if any), the NGINX core version, the `ngx_lua` module version,
and your operating system version.
* A minimal and standalone test case that others can easily run on their side and
reproduce the issue you are seeing.
* Do not simply say "something is broken" or "something does not work". Always provide
as much details as possible. Always describe the symptoms and your expected results.
* You can (temporarily) enable the nginx debugging logs to see the internal workings
of NGINX in your nginx''s `error.log` file. See http://nginx.org/en/docs/debugging_log.html
The same instructions apply equally well to OpenResty.
* If you are seeing crashes, please provide the full backtrace for the crash. See
https://www.nginx.com/resources/wiki/start/topics/tutorials/debugging/#core-dump
for more details.
Please, do not use Chinese here. This place is considered English only. If you
really want to use Chinese, please join and post to the openresty (Chinese)
mailing list instead. Please see https://openresty.org/en/community.html Thanks for
your cooperation.

View File

@ -0,0 +1,2 @@
I hereby granted the copyright of the changes in this pull request
to the authors of this lua-nginx-module project.

View File

@ -0,0 +1,30 @@
name: "Lint PR"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
main:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Configure which types are allowed.
# Default: https://github.com/commitizen/conventional-commit-types
types: |
bugfix # bug fixes
change # backward incompatible changes
doc # documentation changes including code comments
editor # code editor related configurations
feature # implementing a new feature
optimize # performance optimizations
refactor # code refactoring and other code rearrangement
style # coding style changes
tests # test suite changes

180
src/deps/src/lua-nginx-module/.gitignore vendored Normal file
View File

@ -0,0 +1,180 @@
build/
work/
tags
cscope.*
*.mobi
genmobi.sh
.libs
*.swp
*.slo
*.la
*.swo
*.lo
*~
*.o
print.txt
.rsync
*.tar.gz
dist
build[789]
build
tags
update-readme
*.tmp
test/Makefile
test/blib
test.sh
t.sh
t/t.sh
t/servroot*
test/t/servroot/
releng
reset
*.t_
src/handler.h
src/util.c
src/module.h
src/module.c
src/drizzle.c
src/processor.h
src/handler.c
src/util.h
src/drizzle.h
src/processor.c
src/output.c
src/output.h
libdrizzle
ctags
src/stream.h
nginx
keepalive
reindex
src/keepalive.c
src/keepalive.h
src/checker.h
src/checker.c
src/quoting.h
src/quoting.c
src/module.h
src/module.c
src/util.h
src/util.c
src/processor.h
src/processor.c
src/rds.h
src/utils.h
src/handler.c
src/handler.h
util/bench
*.html
trace.out*
try.sh
src/cache.c
src/cache.h
src/common.h
src/directive.c
src/directive.h
src/consts.[ch]
src/contentby.[ch]
src/pcrefix.[ch]
src/util.c
src/clfactory.c
src/directive.c
src/conf.h
src/setby.h
src/cache.h
src/hook.c
src/util.h
src/hook.h
src/common.h
src/directive.h
src/conf.c
src/setby.c
src/cache.c
src/module.c
src/clfactory.h
src/capturefilter.[ch]
src/contentby.c
pack
b.sh
src/in.[ch]
src/out.[ch]
go
all.sh
src/accessby.[ch]
src/rewriteby.[ch]
src/patch.[ch]
src/ndk.[ch]
src/control.[ch]
src/output.[ch]
src/variable.[ch]
src/string.[ch]
src/misc.[ch]
src/log.[ch]
src/exception.[ch]
src/subrequest.[ch]
src/time.[ch]
src/regex.[ch]
src/ctx.[ch]
src/args.[ch]
src/headers.[ch]
src/script.[ch]
src/filter.[ch]
src/shdict.[ch]
src/body.[ch]
src/uri.[ch]
src/api.[ch]
src/coroutine.[ch]
src/logby.[ch]
src/sleep.[ch]
a.patch
all
build1[0-9]
g
buildroot/
src/headerfilterby.[ch]
*.patch
analyze
tsock
a.c
test.lua
build12
ERRORS
src/bodyfilterby.[ch]
src/tcp.[ch]
src/initby.[ch]
src/initworkerby.[ch]
src/socket.[ch]
src/udp.[ch]
src/method.[ch]
tre
src/phase.[ch]
src/probe.h
src/uthread.[ch]
src/timer.[ch]
src/config.[ch]
src/worker.[ch]
src/certby.[ch]
src/storeby.[ch]
src/fetchby.[ch]
src/ssl.[ch]
src/ocsp.c
src/lex.[ch]
src/balancer.[ch]
src/semaphore.[ch]
*.plist
lua
ttimer
Makefile
tsubreq
tthread
addr2line
hup
theaders
src/ngx_http_lua_autoconf.h
src/autoconf.h
src/filters.c
src/filters.h
src/ringbuf.c
src/ringbuf.h
src/pipe.[ch]

View File

@ -0,0 +1,43 @@
---
pull_request_rules:
- name: warn on conflicts
conditions:
- conflict
actions:
comment:
message: This pull request is now in conflict :(
label:
add:
- conflict
- name: remove conflict label if not needed
conditions:
- -conflict
actions:
label:
remove:
- conflict
- name: add label needs-test-cases
conditions:
- files~=^src/
- -files~=^t/
actions:
label:
add:
- needs-test-cases
- name: remove label needs-test-cases
conditions:
- label=needs-test-cases
- files~=^src/
- files~=^t/
actions:
label:
remove:
- needs-test-cases
- name: add label could-be-merged
conditions:
- "#approved-reviews-by>=2"
- status-success=Travis CI - Pull Request
actions:
label:
add:
- could-be-merged

View File

@ -0,0 +1,146 @@
dist: bionic
branches:
only:
- "master"
os: linux
language: c
compiler:
- gcc
addons:
apt:
packages:
- ack
- axel
- cpanminus
- libtest-base-perl
- libtext-diff-perl
- liburi-perl
- libwww-perl
- libtest-longstring-perl
- liblist-moreutils-perl
- libgd-dev
cache:
directories:
- download-cache
env:
global:
- JOBS=3
- NGX_BUILD_JOBS=$JOBS
- LUAJIT_PREFIX=/opt/luajit21
- LUAJIT_LIB=$LUAJIT_PREFIX/lib
- LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
- LUA_INCLUDE_DIR=$LUAJIT_INC
- PCRE_VER=8.45
- PCRE_PREFIX=/opt/pcre
- PCRE_LIB=$PCRE_PREFIX/lib
- PCRE_INC=$PCRE_PREFIX/include
- OPENSSL_PREFIX=/opt/ssl
- OPENSSL_LIB=$OPENSSL_PREFIX/lib
- OPENSSL_INC=$OPENSSL_PREFIX/include
- LIBDRIZZLE_PREFIX=/opt/drizzle
- LIBDRIZZLE_INC=$LIBDRIZZLE_PREFIX/include/libdrizzle-1.0
- LIBDRIZZLE_LIB=$LIBDRIZZLE_PREFIX/lib
- LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
- DRIZZLE_VER=2011.07.21
- TEST_NGINX_SLEEP=0.006
jobs:
- NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.0l OPENSSL_PATCH_VER=1.1.0d
- NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.1s OPENSSL_PATCH_VER=1.1.1f
services:
- memcached
- redis
- mysql
before_install:
- sudo apt update
- sudo apt install --only-upgrade ca-certificates
- '! grep -n -P ''(?<=.{80}).+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Found C source lines exceeding 80 columns." > /dev/stderr; exit 1)'
- '! grep -n -P ''\t+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Cannot use tabs." > /dev/stderr; exit 1)'
- /usr/bin/env perl $(command -v cpanm) --sudo --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
- pyenv global 2.7
install:
- if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then wget -P download-cache http://openresty.org/download/drizzle7-$DRIZZLE_VER.tar.gz; fi
- if [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache https://downloads.sourceforge.net/project/pcre/pcre/${PCRE_VER}/pcre-${PCRE_VER}.tar.gz; fi
- if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi
- git clone https://github.com/openresty/test-nginx.git
- git clone https://github.com/openresty/openresty.git ../openresty
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
- git clone https://github.com/openresty/openresty-devel-utils.git
- git clone https://github.com/openresty/mockeagain.git
- git clone https://github.com/openresty/lua-cjson.git lua-cjson
- git clone https://github.com/openresty/lua-upstream-nginx-module.git ../lua-upstream-nginx-module
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
- git clone https://github.com/openresty/nginx-eval-module.git ../nginx-eval-module
- git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
- git clone https://github.com/FRiCKLE/ngx_coolkit.git ../coolkit-nginx-module
- git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module
- git clone https://github.com/openresty/drizzle-nginx-module.git ../drizzle-nginx-module
- git clone https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module
- git clone https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module
- git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module
- git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module
- git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
- git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql
- git clone https://github.com/openresty/lua-resty-string.git ../lua-resty-string
- git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
- git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2
before_script:
- mysql -uroot -e 'create database ngx_test; grant all on ngx_test.* to "ngx_test"@"%" identified by "ngx_test"; flush privileges;'
script:
- export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH
- ngx-releng > check.txt || true
- lines=`wc -l check.txt | awk '{print $1}'`; if [ $lines -gt 5 ]; then cat check.txt; exit 1; fi
- sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT
- sudo iptables -I OUTPUT -p tcp --dst 127.0.0.2 --dport 12345 -j DROP
- sudo iptables -I OUTPUT -p udp --dst 127.0.0.2 --dport 12345 -j DROP
- sudo ip route add prohibit 0.0.0.1/32
- cd luajit2/
- make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2' > build.log 2>&1 || (cat build.log && exit 1)
- sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
- cd ..
- tar xzf download-cache/drizzle7-$DRIZZLE_VER.tar.gz && cd drizzle7-$DRIZZLE_VER
- ./configure --prefix=$LIBDRIZZLE_PREFIX --without-server > build.log 2>&1 || (cat build.log && exit 1)
- make libdrizzle-1.0 -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
- sudo make install-libdrizzle-1.0 > build.log 2>&1 || (cat build.log && exit 1)
- cd ../mockeagain/ && make CC=$CC -j$JOBS && cd ..
- cd lua-cjson/ && make -j$JOBS && sudo make install && cd ..
- tar zxf download-cache/pcre-$PCRE_VER.tar.gz
- cd pcre-$PCRE_VER/
- ./configure --prefix=$PCRE_PREFIX --enable-jit --enable-utf --enable-unicode-properties > build.log 2>&1 || (cat build.log && exit 1)
- make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
- sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1)
- cd ..
- tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz
- cd openssl-$OPENSSL_VER/
- patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch
- ./config shared enable-ssl3 enable-ssl3-method -g --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1)
- make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
- sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1)
- cd ..
- export NGX_BUILD_CC=$CC
- sh util/build-without-ssl.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)
- sh util/build-with-dd.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)
- rm -fr buildroot
- sh util/build.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)
- nginx -V
- python3 ./util/nc_server.py &
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
- export LD_PRELOAD=$PWD/mockeagain/mockeagain.so
- export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH
- export TEST_NGINX_RESOLVER=8.8.4.4
- dig +short myip.opendns.com @resolver1.opendns.com || exit 0
- dig +short @$TEST_NGINX_RESOLVER openresty.org || exit 0
- dig +short @$TEST_NGINX_RESOLVER agentzh.org || exit 0
- /usr/bin/env perl $(command -v prove) -I. -Itest-nginx/lib -r t/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,516 @@
ngx_lua_opt_I=
ngx_lua_opt_L=
luajit_ld_opt=
ngx_feature_name=
ngx_feature_run=no
ngx_feature_incs=
ngx_feature_test=
if [ -n "$LUAJIT_INC" -o -n "$LUAJIT_LIB" ]; then
# explicitly set LuaJIT paths
if [ "$NGX_PLATFORM" = win32 ]; then
ngx_feature="LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (win32)"
ngx_feature_path="$LUAJIT_INC"
ngx_lua_opt_I="-I$LUAJIT_INC"
ngx_lua_opt_L="-L$LUAJIT_LIB"
# ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first
SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS"
CC_TEST_FLAGS="$ngx_lua_opt_I $CC_TEST_FLAGS"
SAVED_NGX_TEST_LD_OPT="$NGX_TEST_LD_OPT"
NGX_TEST_LD_OPT="$ngx_lua_opt_L $NGX_TEST_LD_OPT"
# LuaJIT's win32 build uses the library file name lua51.dll.
ngx_feature_libs="$ngx_lua_opt_L -llua51"
. auto/feature
# clean up
CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS"
NGX_TEST_LD_OPT="$SAVED_NGX_TEST_LD_OPT"
else
# attempt to link with -ldl, static linking on Linux requires it.
ngx_feature="LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (specified by the LUAJIT_LIB and LUAJIT_INC env, with -ldl)"
ngx_feature_path="$LUAJIT_INC"
ngx_lua_opt_I="-I$LUAJIT_INC"
ngx_lua_opt_L="-L$LUAJIT_LIB"
luajit_ld_opt="-lm -ldl"
# ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first
SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS"
CC_TEST_FLAGS="$ngx_lua_opt_I $CC_TEST_FLAGS"
SAVED_NGX_TEST_LD_OPT="$NGX_TEST_LD_OPT"
NGX_TEST_LD_OPT="$ngx_lua_opt_L $NGX_TEST_LD_OPT"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R$LUAJIT_LIB $ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt"
else
ngx_feature_libs="$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt"
fi
. auto/feature
# clean up
CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS"
NGX_TEST_LD_OPT="$SAVED_NGX_TEST_LD_OPT"
if [ $ngx_found = no ]; then
# retry without -ldl
ngx_feature="LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (specified by the LUAJIT_LIB and LUAJIT_INC env)"
ngx_feature_path="$LUAJIT_INC"
ngx_lua_opt_I="-I$LUAJIT_INC"
ngx_lua_opt_L="-L$LUAJIT_LIB"
luajit_ld_opt="-lm"
# ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first
SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS"
CC_TEST_FLAGS="$ngx_lua_opt_I $CC_TEST_FLAGS"
SAVED_NGX_TEST_LD_OPT="$NGX_TEST_LD_OPT"
NGX_TEST_LD_OPT="$ngx_lua_opt_L $NGX_TEST_LD_OPT"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R$LUAJIT_LIB $ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt"
else
ngx_feature_libs="$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt"
fi
. auto/feature
# clean up
CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS"
NGX_TEST_LD_OPT="$SAVED_NGX_TEST_LD_OPT"
fi
fi
if [ $ngx_found = no ]; then
cat << END
$0: error: ngx_http_lua_module requires the LuaJIT library, but it could not be found where specified (LUAJIT_LIB=$LUAJIT_LIB, LUAJIT_INC=$LUAJIT_INC).
END
exit 1
fi
case "$NGX_PLATFORM" in
Darwin:*)
case "$NGX_MACHINE" in
amd64 | x86_64 | i386)
echo "adding extra linking options needed by LuaJIT on $NGX_MACHINE"
luajit_ld_opt="$luajit_ld_opt -pagezero_size 10000 -image_base 100000000"
ngx_feature_libs="$ngx_feature_libs -pagezero_size 10000 -image_base 100000000"
;;
*)
;;
esac
;;
*)
;;
esac
else
# auto-discovery
if [ $ngx_found = no ]; then
# FreeBSD with luajit-2.0 from ports collection
ngx_feature="LuaJIT library in /usr/local/"
ngx_feature_path="/usr/local/include/luajit-2.0"
luajit_ld_opt="-lm"
LUAJIT_LIB="/usr/local/lib"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lluajit-5.1 -lm"
else
ngx_feature_libs="-L/usr/local/lib -lluajit-5.1 -lm"
fi
. auto/feature
fi
if [ $ngx_found = no ]; then
# Gentoo with LuaJIT-2.0, try with -ldl
ngx_feature="LuaJIT library in /usr/"
ngx_feature_path="/usr/include/luajit-2.0"
luajit_ld_opt="-lm -ldl"
LUAJIT_LIB="/usr/lib"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/usr/lib -L/usr/lib -lm -lluajit-5.1 -ldl"
else
ngx_feature_libs="-L/usr/lib -lm -lluajit-5.1 -ldl"
fi
. auto/feature
fi
if [ $ngx_found = no ]; then
# Gentoo with LuaJIT 2.0
ngx_feature="LuaJIT library in /usr/"
ngx_feature_path="/usr/include/luajit-2.0"
luajit_ld_opt="-lm"
LUAJIT_LIB="/usr/lib"
if [ $NGX_RPATH = YES ]; then
ngx_feature_libs="-R/usr/lib -L/usr/lib -lm -lluajit-5.1"
else
ngx_feature_libs="-L/usr/lib -lm -lluajit-5.1"
fi
. auto/feature
fi
fi
ngx_module_incs=
ngx_module_libs=
if [ $ngx_found = yes ]; then
# this is a hack to persuade nginx's build system to favor
# the paths set by our user environment
CFLAGS="$ngx_lua_opt_I $CFLAGS"
NGX_LD_OPT="$ngx_lua_opt_L $NGX_LD_OPT"
ngx_module_incs="$ngx_module_incs $ngx_feature_path"
ngx_module_libs="$ngx_module_libs $ngx_feature_libs"
else
cat << END
$0: error: ngx_http_lua_module requires the LuaJIT library.
END
exit 1
fi
# ----------------------------------------
ngx_feature="LuaJIT 2.x"
ngx_feature_run=no
ngx_feature_incs="#include <luajit.h>"
ngx_feature_test="#if !defined(LUAJIT_VERSION_NUM) || LUAJIT_VERSION_NUM < 20000
# error unsupported LuaJIT version
#endif
"
. auto/feature
if [ $ngx_found = no ]; then
cat << END
$0: error: unsupported LuaJIT version; ngx_http_lua_module requires LuaJIT 2.x.
END
exit 1
fi
# ----------------------------------------
ngx_feature="Lua language 5.1"
ngx_feature_run=no
ngx_feature_incs="#include <lua.h>"
ngx_feature_test="#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM != 501
# error unsupported Lua language version
#endif
"
. auto/feature
if [ $ngx_found = no ]; then
cat << END
$0: error: unsupported Lua language version; ngx_http_lua_module requires Lua 5.1.
END
exit 1
fi
# ----------------------------------------
ngx_feature="LuaJIT has FFI"
ngx_feature_libs="$ngx_module_libs"
ngx_feature_run=no
ngx_feature_incs="#include <lualib.h>
#include <lauxlib.h>
#include <assert.h>
"
ngx_feature_test="lua_State *L = luaL_newstate();
assert(L != NULL);
luaopen_ffi(L);
"
. auto/feature
if [ $ngx_found = no ]; then
cat << END
$0: error: unsupported LuaJIT build; ngx_http_lua_module requires LuaJIT with FFI enabled.
END
exit 1
fi
# ----------------------------------------
ngx_addon_name=ngx_http_lua_module
HTTP_LUA_SRCS=" \
$ngx_addon_dir/src/ngx_http_lua_script.c \
$ngx_addon_dir/src/ngx_http_lua_log.c \
$ngx_addon_dir/src/ngx_http_lua_subrequest.c \
$ngx_addon_dir/src/ngx_http_lua_ndk.c \
$ngx_addon_dir/src/ngx_http_lua_control.c \
$ngx_addon_dir/src/ngx_http_lua_time.c \
$ngx_addon_dir/src/ngx_http_lua_misc.c \
$ngx_addon_dir/src/ngx_http_lua_variable.c \
$ngx_addon_dir/src/ngx_http_lua_string.c \
$ngx_addon_dir/src/ngx_http_lua_output.c \
$ngx_addon_dir/src/ngx_http_lua_headers.c \
$ngx_addon_dir/src/ngx_http_lua_req_body.c \
$ngx_addon_dir/src/ngx_http_lua_uri.c \
$ngx_addon_dir/src/ngx_http_lua_args.c \
$ngx_addon_dir/src/ngx_http_lua_ctx.c \
$ngx_addon_dir/src/ngx_http_lua_regex.c \
$ngx_addon_dir/src/ngx_http_lua_module.c \
$ngx_addon_dir/src/ngx_http_lua_headers_out.c \
$ngx_addon_dir/src/ngx_http_lua_headers_in.c \
$ngx_addon_dir/src/ngx_http_lua_directive.c \
$ngx_addon_dir/src/ngx_http_lua_consts.c \
$ngx_addon_dir/src/ngx_http_lua_exception.c \
$ngx_addon_dir/src/ngx_http_lua_util.c \
$ngx_addon_dir/src/ngx_http_lua_cache.c \
$ngx_addon_dir/src/ngx_http_lua_contentby.c \
$ngx_addon_dir/src/ngx_http_lua_server_rewriteby.c \
$ngx_addon_dir/src/ngx_http_lua_rewriteby.c \
$ngx_addon_dir/src/ngx_http_lua_accessby.c \
$ngx_addon_dir/src/ngx_http_lua_setby.c \
$ngx_addon_dir/src/ngx_http_lua_capturefilter.c \
$ngx_addon_dir/src/ngx_http_lua_clfactory.c \
$ngx_addon_dir/src/ngx_http_lua_pcrefix.c \
$ngx_addon_dir/src/ngx_http_lua_headerfilterby.c \
$ngx_addon_dir/src/ngx_http_lua_shdict.c \
$ngx_addon_dir/src/ngx_http_lua_socket_tcp.c \
$ngx_addon_dir/src/ngx_http_lua_api.c \
$ngx_addon_dir/src/ngx_http_lua_logby.c \
$ngx_addon_dir/src/ngx_http_lua_sleep.c \
$ngx_addon_dir/src/ngx_http_lua_semaphore.c\
$ngx_addon_dir/src/ngx_http_lua_coroutine.c \
$ngx_addon_dir/src/ngx_http_lua_bodyfilterby.c \
$ngx_addon_dir/src/ngx_http_lua_initby.c \
$ngx_addon_dir/src/ngx_http_lua_initworkerby.c \
$ngx_addon_dir/src/ngx_http_lua_exitworkerby.c \
$ngx_addon_dir/src/ngx_http_lua_socket_udp.c \
$ngx_addon_dir/src/ngx_http_lua_req_method.c \
$ngx_addon_dir/src/ngx_http_lua_phase.c \
$ngx_addon_dir/src/ngx_http_lua_uthread.c \
$ngx_addon_dir/src/ngx_http_lua_timer.c \
$ngx_addon_dir/src/ngx_http_lua_config.c \
$ngx_addon_dir/src/ngx_http_lua_worker.c \
$ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.c \
$ngx_addon_dir/src/ngx_http_lua_ssl_certby.c \
$ngx_addon_dir/src/ngx_http_lua_ssl_ocsp.c \
$ngx_addon_dir/src/ngx_http_lua_lex.c \
$ngx_addon_dir/src/ngx_http_lua_balancer.c \
$ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.c \
$ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.c \
$ngx_addon_dir/src/ngx_http_lua_ssl.c \
$ngx_addon_dir/src/ngx_http_lua_log_ringbuf.c \
$ngx_addon_dir/src/ngx_http_lua_input_filters.c \
$ngx_addon_dir/src/ngx_http_lua_pipe.c \
$ngx_addon_dir/src/ngx_http_lua_worker_thread.c \
"
HTTP_LUA_DEPS=" \
$ngx_addon_dir/src/ddebug.h \
$ngx_addon_dir/src/ngx_http_lua_autoconf.h \
$ngx_addon_dir/src/ngx_http_lua_script.h \
$ngx_addon_dir/src/ngx_http_lua_log.h \
$ngx_addon_dir/src/ngx_http_lua_subrequest.h \
$ngx_addon_dir/src/ngx_http_lua_ndk.h \
$ngx_addon_dir/src/ngx_http_lua_control.h \
$ngx_addon_dir/src/ngx_http_lua_string.h \
$ngx_addon_dir/src/ngx_http_lua_misc.h \
$ngx_addon_dir/src/ngx_http_lua_output.h \
$ngx_addon_dir/src/ngx_http_lua_headers.h \
$ngx_addon_dir/src/ngx_http_lua_uri.h \
$ngx_addon_dir/src/ngx_http_lua_req_body.h \
$ngx_addon_dir/src/ngx_http_lua_args.h \
$ngx_addon_dir/src/ngx_http_lua_ctx.h \
$ngx_addon_dir/src/ngx_http_lua_common.h \
$ngx_addon_dir/src/ngx_http_lua_directive.h \
$ngx_addon_dir/src/ngx_http_lua_headers_out.h \
$ngx_addon_dir/src/ngx_http_lua_headers_in.h \
$ngx_addon_dir/src/ngx_http_lua_consts.h \
$ngx_addon_dir/src/ngx_http_lua_exception.h \
$ngx_addon_dir/src/ngx_http_lua_util.h \
$ngx_addon_dir/src/ngx_http_lua_cache.h \
$ngx_addon_dir/src/ngx_http_lua_contentby.h \
$ngx_addon_dir/src/ngx_http_lua_server_rewriteby.c \
$ngx_addon_dir/src/ngx_http_lua_rewriteby.h \
$ngx_addon_dir/src/ngx_http_lua_accessby.h \
$ngx_addon_dir/src/ngx_http_lua_setby.h \
$ngx_addon_dir/src/ngx_http_lua_capturefilter.h \
$ngx_addon_dir/src/ngx_http_lua_clfactory.h \
$ngx_addon_dir/src/ngx_http_lua_pcrefix.h \
$ngx_addon_dir/src/ngx_http_lua_headerfilterby.h \
$ngx_addon_dir/src/ngx_http_lua_shdict.h \
$ngx_addon_dir/src/ngx_http_lua_socket_tcp.h \
$ngx_addon_dir/src/api/ngx_http_lua_api.h \
$ngx_addon_dir/src/ngx_http_lua_logby.h \
$ngx_addon_dir/src/ngx_http_lua_sleep.h \
$ngx_addon_dir/src/ngx_http_lua_semaphore.h\
$ngx_addon_dir/src/ngx_http_lua_coroutine.h \
$ngx_addon_dir/src/ngx_http_lua_bodyfilterby.h \
$ngx_addon_dir/src/ngx_http_lua_initby.h \
$ngx_addon_dir/src/ngx_http_lua_initworkerby.h \
$ngx_addon_dir/src/ngx_http_lua_exitworkerby.h \
$ngx_addon_dir/src/ngx_http_lua_socket_udp.h \
$ngx_addon_dir/src/ngx_http_lua_probe.h \
$ngx_addon_dir/src/ngx_http_lua_uthread.h \
$ngx_addon_dir/src/ngx_http_lua_timer.h \
$ngx_addon_dir/src/ngx_http_lua_config.h \
$ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.h \
$ngx_addon_dir/src/ngx_http_lua_ssl_certby.h \
$ngx_addon_dir/src/ngx_http_lua_lex.h \
$ngx_addon_dir/src/ngx_http_lua_balancer.h \
$ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.h \
$ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.h \
$ngx_addon_dir/src/ngx_http_lua_ssl.h \
$ngx_addon_dir/src/ngx_http_lua_log_ringbuf.h \
$ngx_addon_dir/src/ngx_http_lua_input_filters.h \
$ngx_addon_dir/src/ngx_http_lua_pipe.h \
$ngx_addon_dir/src/ngx_http_lua_worker_thread.h \
"
# ----------------------------------------
ngx_feature="export symbols by default (-E)"
ngx_feature_libs="-Wl,-E"
ngx_feature_name=
ngx_feature_run=no
ngx_feature_incs="#include <stdio.h>"
ngx_feature_path=
ngx_feature_test='printf("hello");'
. auto/feature
if [ $ngx_found = yes ]; then
CORE_LIBS="-Wl,-E $CORE_LIBS"
fi
# ----------------------------------------
# for Cygwin
ngx_feature="export symbols by default (--export-all-symbols)"
ngx_feature_libs="-Wl,--export-all-symbols"
ngx_feature_name=
ngx_feature_run=no
ngx_feature_incs="#include <stdio.h>"
ngx_feature_path=
ngx_feature_test='printf("hello");'
. auto/feature
if [ $ngx_found = yes ]; then
CORE_LIBS="-Wl,--export-all-symbols $CORE_LIBS"
fi
# ----------------------------------------
ngx_feature="SO_PASSCRED"
ngx_feature_libs=
ngx_feature_name="NGX_HTTP_LUA_HAVE_SO_PASSCRED"
ngx_feature_run=no
ngx_feature_incs="#include <sys/types.h>
#include <sys/socket.h>"
ngx_feature_path=
ngx_feature_test='setsockopt(1, SOL_SOCKET, SO_PASSCRED, NULL, 0);'
. auto/feature
# ----------------------------------------
ngx_feature="SA_RESTART"
ngx_feature_libs=
ngx_feature_name="NGX_HTTP_LUA_HAVE_SA_RESTART"
ngx_feature_run=no
ngx_feature_incs="#include <signal.h>"
ngx_feature_path=
ngx_feature_test='struct sigaction act;
act.sa_flags |= SA_RESTART;'
. auto/feature
# ----------------------------------------
ngx_feature="malloc_trim"
ngx_feature_libs=
ngx_feature_name="NGX_HTTP_LUA_HAVE_MALLOC_TRIM"
ngx_feature_run=yes
ngx_feature_incs="#include <malloc.h>
#include <stdio.h>"
ngx_feature_test="int rc = malloc_trim((size_t) 0); printf(\"%d\", rc);"
SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS"
CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS"
. auto/feature
CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS"
# ----------------------------------------
ngx_feature="pipe2"
ngx_feature_libs=
ngx_feature_name="NGX_HTTP_LUA_HAVE_PIPE2"
ngx_feature_run=no
ngx_feature_incs="#include <fcntl.h>"
ngx_feature_test="int fd[2]; pipe2(fd, O_CLOEXEC|O_NONBLOCK);"
SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS"
CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS"
. auto/feature
CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS"
# ----------------------------------------
ngx_feature="signalfd"
ngx_feature_libs=
ngx_feature_name="NGX_HTTP_LUA_HAVE_SIGNALFD"
ngx_feature_run=no
ngx_feature_incs="#include <sys/signalfd.h>"
ngx_feature_test="sigset_t set; signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC);"
SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS"
CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS"
. auto/feature
CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS"
# ----------------------------------------
ngx_feature="execvpe"
ngx_feature_libs=
ngx_feature_name="NGX_HTTP_LUA_HAVE_EXECVPE"
ngx_feature_run=no
ngx_feature_incs=
ngx_feature_test='char* argv[] = {"/bin/sh"};execvpe("/bin/sh", argv, NULL);'
SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS"
CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS"
. auto/feature
CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS"
# ----------------------------------------
if [ -n "$ngx_module_link" ]; then
ngx_module_type=HTTP_AUX_FILTER
ngx_module_name=$ngx_addon_name
ngx_module_deps="$HTTP_LUA_DEPS"
ngx_module_srcs="$HTTP_LUA_SRCS"
. auto/module
else
HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES $ngx_addon_name"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $HTTP_LUA_SRCS"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $HTTP_LUA_DEPS"
CORE_INCS="$CORE_INCS $ngx_module_incs"
CORE_LIBS="$CORE_LIBS $ngx_module_libs"
fi
# ----------------------------------------
USE_MD5=YES
USE_SHA1=YES
NGX_DTRACE_PROVIDERS="$NGX_DTRACE_PROVIDERS $ngx_addon_dir/dtrace/ngx_lua_provider.d"
NGX_TAPSET_SRCS="$NGX_TAPSET_SRCS $ngx_addon_dir/tapset/ngx_lua.stp"
CORE_INCS="$CORE_INCS $ngx_addon_dir/src/api"
CFLAGS="$CFLAGS -DNDK_SET_VAR"
echo "/* DO NOT EDIT! This file was automatically generated by config */" > "$ngx_addon_dir/src/ngx_http_lua_autoconf.h"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
provider nginx_lua {
probe http__lua__info(char *s);
/* lua_State *L */
probe http__lua__register__preload__package(void *L, u_char *pkg);
probe http__lua__req__socket__consume__preread(void *r,
u_char *data, size_t len);
/* lua_State *parent, lua_State *child */
probe http__lua__user__coroutine__create(void *r,
void *parent, void *child);
/* lua_State *parent, lua_State *child */
probe http__lua__user__coroutine__resume(void *r,
void *parent, void *child);
/* lua_State *parent, lua_State *child */
probe http__lua__user__coroutine__yield(void *r,
void *parent, void *child);
/* lua_State *L */
probe http__lua__thread__yield(void *r, void *L);
/* ngx_http_lua_socket_tcp_upstream_t *u */
probe http__lua__socket__tcp__send__start(void *r,
void *u, u_char *data, size_t len);
/* ngx_http_lua_socket_tcp_upstream_t *u */
probe http__lua__socket__tcp__receive__done(void *r,
void *u, u_char *data, size_t len);
/* ngx_http_lua_socket_tcp_upstream_t *u */
probe http__lua__socket__tcp__setkeepalive__buf__unread(
void *r, void *u, u_char *data, size_t len);
/* lua_State *creator, lua_State *newthread */
probe http__lua__user__thread__spawn(void *r,
void *creator, void *newthread);
/* lua_State *thread, ngx_http_lua_ctx_t *ctx */
probe http__lua__thread__delete(void *r, void *thread, void *ctx);
/* lua_State *thread */
probe http__lua__run__posted__thread(void *r, void *thread,
int status);
probe http__lua__coroutine__done(void *r, void *co,
int success);
/* lua_State *parent, lua_State *child */
probe http__lua__user__thread__wait(void *parent, void *child);
};
#pragma D attributes Evolving/Evolving/Common provider nginx_lua provider
#pragma D attributes Private/Private/Unknown provider nginx_lua module
#pragma D attributes Private/Private/Unknown provider nginx_lua function
#pragma D attributes Private/Private/Common provider nginx_lua name
#pragma D attributes Evolving/Evolving/Common provider nginx_lua args

View File

@ -0,0 +1,3 @@
test:
prove -Ilib -r t

View File

@ -0,0 +1,138 @@
package RecvUntil;
use strict;
use warnings;
sub recv_until {
my ($pat) = @_;
my $len = length $pat;
my @backtracks;
for (my $i = 1; $i <= $len - 1; $i++) {
my $matched_prefix_len = 1;
while ($matched_prefix_len <= $len - $i - 1) {
#while (1) {
#my $left = $len - $i;
#warn "left: $i: $len: ", $len - 1 - $i, "\n";
#warn "matched_prefix_len: $matched_prefix_len\n";
#while (1) {
my $prefix = substr($pat, 0, $matched_prefix_len);
my $next = substr($pat, $matched_prefix_len, 1);
my $prefix2 = substr($pat, $i, $matched_prefix_len);
my $next2 = substr($pat, $i + $matched_prefix_len, 1);
#warn "$i: global prefix $prefix $next\n";
#warn "$i: local prefix $prefix2 $next2\n";
if ($prefix2 eq $prefix) {
if ($next2 eq $next) {
$matched_prefix_len++;
next;
}
#warn "$matched_prefix_len: $prefix: found match at $i (next $next, next2 $next2)\n";
my $cur_state = $i + $matched_prefix_len;
my $new_state = $matched_prefix_len + 1;
my $matched = substr($pat, 0, $cur_state);
my $chain = $backtracks[$cur_state - 2];
if (!$chain) {
$chain = [];
$backtracks[$cur_state - 2] = $chain;
}
my $found = 0;
for my $rec (@$chain) {
if ($rec->{char} eq $next) {
$found = 1;
if ($rec->{new_state} < $new_state) {
warn "overriding...\n";
$rec->{new_state} = $new_state;
}
}
}
if (!$found) {
warn "on state $cur_state ($matched), if next is '$next', ",
"then backtrack to state $new_state ($prefix$next)\n";
push @$chain, { char => $next, new_state => $new_state };
}
#if ($matched_prefix_len > 1) {
#$i += $matched_prefix_len - 1;
#}
last;
}
last;
}
}
return sub {
my ($txt) = @_;
my $max_state = length $pat;
my $len = length $txt;
my $state = 0;
my $ret = '';
for (my $i = 0; $i < $len; $i++) {
# read the char
my $c = substr($txt, $i, 1);
#warn "$state: read char at $i: $c\n";
#warn "matched: $ret\n";
my $expected = substr($pat, $state, 1);
if ($expected eq $c) {
#warn "matched the char in pattern.\n";
$state++;
if ($state == $max_state) {
last;
}
next;
}
if ($state == 0) {
#warn "did not match the first char in pattern\n";
$ret .= $c;
next;
}
my $old_state;
my $matched;
my $chain = $backtracks[$state - 2];
for my $rec (@$chain) {
if ($rec->{char} eq $c) {
$old_state = $state;
$state = $rec->{new_state};
#warn "matched the char for backtracking to state $state\n";
$matched = 1;
last;
}
}
if (!$matched) {
$ret .= substr($pat, 0, $state);
$state = 0;
redo;
}
$ret .= substr($pat, 0, $old_state + 1 - $state);
next;
}
return $ret;
};
}
1;

View File

@ -0,0 +1,140 @@
# vi:ft=
use 5.10.1;
use Test::Base;
use RecvUntil;
plan tests => 1 * blocks();
run {
my $block = shift;
my $name = $block->name;
my $pat = $block->pat // die "$name: No --- pat found";
my $txt = $block->txt // die "$name: No --- txt found";
my $expected = $block->out // die "$name: No --- out found";
my $it = RecvUntil::recv_until($pat);
is $it->($txt), $expected, "$name: output ok";
};
__DATA__
=== TEST 1:
--- pat: abcabd
--- txt: abcabcabd
--- out: abc
=== TEST 2:
--- pat: aa
--- txt: abcabcaad
--- out: abcabc
=== TEST 3:
--- pat: ab
--- txt: bbcabcaad
--- out: bbc
=== TEST 4:
--- pat: aaa
--- txt: abaabcaaaef
--- out: abaabc
=== TEST 5:
--- pat: aaaaad
--- txt: baaaaaaaaeaaaaaaadf
--- out: baaaaaaaaeaa
=== TEST 6:
--- pat: abacadae
--- txt: a
--- out:
=== TEST 7:
--- pat: abacadae
--- txt: ababacadae
--- out: ab
=== TEST 8:
--- pat: abacadae
--- txt: abacabacadae
--- out: abac
=== TEST 9:
--- pat: abacadae
--- txt: abaabacadae
--- out: aba
=== TEST 10:
--- pat: abacadae
--- txt: abacadabacadae
--- out: abacad
=== TEST 11:
--- pat: abcabdabcabe
--- txt: abcabdabcabdabcabe
--- out: abcabd
=== TEST 12:
--- pat: abcabdabcabe
--- txt: abcabdabcabcabdabcabe
--- out: abcabdabc
=== TEST 13:
--- pat: abcabdabcabe
--- txt: abcabcabdabcabe
--- out: abc
=== TEST 14:
--- pat: abcabdabcabe
--- txt: ababcabdabcabe
--- out: ab
=== TEST 15:
--- pat: abcdef
--- txt: abcabcdef
--- out: abc
=== TEST 16:
--- pat: -- abc
--- txt: ---- abc
--- out: --
=== TEST 17:
--- pat: yz--ababyz
--- txt:
--- out: --
--- SKIP

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_API_H_INCLUDED_
#define _NGX_HTTP_LUA_API_H_INCLUDED_
#include <nginx.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <lua.h>
#include <stdint.h>
/* Public API for other Nginx modules */
#define ngx_http_lua_version 10025
typedef struct ngx_http_lua_co_ctx_s ngx_http_lua_co_ctx_t;
typedef struct {
uint8_t type;
union {
int b; /* boolean */
lua_Number n; /* number */
ngx_str_t s; /* string */
} value;
} ngx_http_lua_value_t;
typedef struct {
int len;
/* this padding hole on 64-bit systems is expected */
u_char *data;
} ngx_http_lua_ffi_str_t;
lua_State *ngx_http_lua_get_global_state(ngx_conf_t *cf);
ngx_http_request_t *ngx_http_lua_get_request(lua_State *L);
ngx_int_t ngx_http_lua_add_package_preload(ngx_conf_t *cf, const char *package,
lua_CFunction func);
ngx_int_t ngx_http_lua_shared_dict_get(ngx_shm_zone_t *shm_zone,
u_char *key_data, size_t key_len, ngx_http_lua_value_t *value);
ngx_shm_zone_t *ngx_http_lua_find_zone(u_char *name_data, size_t name_len);
ngx_shm_zone_t *ngx_http_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,
size_t size, void *tag);
ngx_http_lua_co_ctx_t *ngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r);
void ngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r,
ngx_http_lua_co_ctx_t *coctx);
lua_State *ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx);
void ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets);
int ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r);
#endif /* _NGX_HTTP_LUA_API_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _DDEBUG_H_INCLUDED_
#define _DDEBUG_H_INCLUDED_
#include <ngx_config.h>
#include <nginx.h>
#include <ngx_core.h>
#if defined(DDEBUG) && (DDEBUG)
# if (NGX_HAVE_VARIADIC_MACROS)
# define dd(...) fprintf(stderr, "lua *** %s: ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__)
# else
#include <stdarg.h>
#include <stdio.h>
#include <stdarg.h>
static ngx_inline void
dd(const char *fmt, ...) {
}
# endif
#else
# if (NGX_HAVE_VARIADIC_MACROS)
# define dd(...)
# else
#include <stdarg.h>
static ngx_inline void
dd(const char *fmt, ...) {
}
# endif
#endif
#if defined(DDEBUG) && (DDEBUG)
#define dd_check_read_event_handler(r) \
dd("r->read_event_handler = %s", \
r->read_event_handler == ngx_http_block_reading ? \
"ngx_http_block_reading" : \
r->read_event_handler == ngx_http_test_reading ? \
"ngx_http_test_reading" : \
r->read_event_handler == ngx_http_request_empty_handler ? \
"ngx_http_request_empty_handler" : "UNKNOWN")
#define dd_check_write_event_handler(r) \
dd("r->write_event_handler = %s", \
r->write_event_handler == ngx_http_handler ? \
"ngx_http_handler" : \
r->write_event_handler == ngx_http_core_run_phases ? \
"ngx_http_core_run_phases" : \
r->write_event_handler == ngx_http_request_empty_handler ? \
"ngx_http_request_empty_handler" : "UNKNOWN")
#else
#define dd_check_read_event_handler(r)
#define dd_check_write_event_handler(r)
#endif
#endif /* _DDEBUG_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,399 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include "ngx_http_lua_accessby.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_cache.h"
static ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L,
ngx_http_request_t *r);
ngx_int_t
ngx_http_lua_access_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_loc_conf_t *llcf;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_phase_handler_t tmp, *ph, *cur_ph, *last_ph;
ngx_http_core_main_conf_t *cmcf;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua access handler, uri:\"%V\" c:%ud", &r->uri,
r->main->count);
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
if (!lmcf->postponed_to_access_phase_end) {
lmcf->postponed_to_access_phase_end = 1;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
cur_ph = &ph[r->phase_handler];
/* we should skip the post_access phase handler here too */
last_ph = &ph[cur_ph->next - 2];
dd("ph cur: %d, ph next: %d", (int) r->phase_handler,
(int) (cur_ph->next - 2));
#if 0
if (cur_ph == last_ph) {
dd("XXX our handler is already the last access phase handler");
}
#endif
if (cur_ph < last_ph) {
dd("swapping the contents of cur_ph and last_ph...");
tmp = *cur_ph;
memmove(cur_ph, cur_ph + 1,
(last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t));
*last_ph = tmp;
r->phase_handler--; /* redo the current ph */
return NGX_DECLINED;
}
}
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->access_handler == NULL) {
dd("no access handler found");
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
dd("entered? %d", (int) ctx->entered_access_phase);
if (ctx->entered_access_phase) {
dd("calling wev handler");
rc = ctx->resume_handler(r);
dd("wev handler returns %d", (int) rc);
if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {
return rc;
}
if (rc == NGX_OK) {
if (r->header_sent) {
dd("header already sent");
/* response header was already generated in access_by_lua*,
* so it is no longer safe to proceed to later phases
* which may generate responses again */
if (!ctx->eof) {
dd("eof not yet sent");
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
/* indicate last_buf */);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
}
return NGX_HTTP_OK;
}
return NGX_OK;
}
return NGX_DECLINED;
}
if (ctx->waiting_more_body) {
dd("WAITING MORE BODY");
return NGX_DONE;
}
if (llcf->force_read_body && !ctx->read_body_done) {
r->request_body_in_single_buf = 1;
r->request_body_in_persistent_file = 1;
r->request_body_in_clean_file = 1;
rc = ngx_http_read_client_request_body(r,
ngx_http_lua_generic_phase_post_read);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
if (rc == NGX_AGAIN) {
ctx->waiting_more_body = 1;
return NGX_DONE;
}
}
dd("calling access handler");
return llcf->access_handler(r);
}
ngx_int_t
ngx_http_lua_access_handler_inline(ngx_http_request_t *r)
{
ngx_int_t rc;
lua_State *L;
ngx_http_lua_loc_conf_t *llcf;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
llcf->access_src.value.data,
llcf->access_src.value.len,
&llcf->access_src_ref,
llcf->access_src_key,
(const char *) llcf->access_chunkname);
if (rc != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return ngx_http_lua_access_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_access_handler_file(ngx_http_request_t *r)
{
u_char *script_path;
ngx_int_t rc;
ngx_str_t eval_src;
lua_State *L;
ngx_http_lua_loc_conf_t *llcf;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
/* Eval nginx variables in code path string first */
if (ngx_http_complex_value(r, &llcf->access_src, &eval_src) != NGX_OK) {
return NGX_ERROR;
}
script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
eval_src.len);
if (script_path == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
&llcf->access_src_ref,
llcf->access_src_key);
if (rc != NGX_OK) {
if (rc < NGX_HTTP_SPECIAL_RESPONSE) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return rc;
}
/* make sure we have a valid code chunk */
ngx_http_lua_assert(lua_isfunction(L, -1));
return ngx_http_lua_access_by_chunk(L, r);
}
static ngx_int_t
ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r)
{
int co_ref;
ngx_int_t rc;
ngx_uint_t nreqs;
lua_State *co;
ngx_event_t *rev;
ngx_connection_t *c;
ngx_http_lua_ctx_t *ctx;
ngx_pool_cleanup_t *cln;
ngx_http_lua_loc_conf_t *llcf;
/* {{{ new coroutine to handle request */
co = ngx_http_lua_new_thread(r, L, &co_ref);
if (co == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"lua: failed to create new coroutine "
"to handle request");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* move code closure to new coroutine */
lua_xmove(L, co, 1);
#ifndef OPENRESTY_LUAJIT
/* set closure's env table to new coroutine's globals table */
ngx_http_lua_get_globals_table(co);
lua_setfenv(co, -2);
#endif
/* save nginx request in coroutine globals table */
ngx_http_lua_set_req(co, r);
/* {{{ initialize request context */
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_lua_reset_ctx(r, L, ctx);
ctx->entered_access_phase = 1;
ctx->cur_co_ctx = &ctx->entry_co_ctx;
ctx->cur_co_ctx->co = co;
ctx->cur_co_ctx->co_ref = co_ref;
#ifdef NGX_LUA_USE_ASSERT
ctx->cur_co_ctx->co_top = 1;
#endif
ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
/* }}} */
/* {{{ register nginx pool cleanup hooks */
if (ctx->cleanup == NULL) {
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cln->handler = ngx_http_lua_request_cleanup_handler;
cln->data = ctx;
ctx->cleanup = &cln->handler;
}
/* }}} */
ctx->context = NGX_HTTP_LUA_CONTEXT_ACCESS;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->check_client_abort) {
r->read_event_handler = ngx_http_lua_rd_check_broken_connection;
#if (NGX_HTTP_V2)
if (!r->stream) {
#endif
rev = r->connection->read;
if (!rev->active) {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
return NGX_ERROR;
}
}
#if (NGX_HTTP_V2)
}
#endif
} else {
r->read_event_handler = ngx_http_block_reading;
}
c = r->connection;
nreqs = c->requests;
rc = ngx_http_lua_run_thread(L, r, ctx, 0);
dd("returned %d", (int) rc);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
if (rc == NGX_AGAIN) {
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);
if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {
return rc;
}
if (rc != NGX_OK) {
return NGX_DECLINED;
}
} else if (rc == NGX_DONE) {
ngx_http_lua_finalize_request(r, NGX_DONE);
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);
if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {
return rc;
}
if (rc != NGX_OK) {
return NGX_DECLINED;
}
}
#if 1
if (rc == NGX_OK) {
if (r->header_sent) {
dd("header already sent");
/* response header was already generated in access_by_lua*,
* so it is no longer safe to proceed to later phases
* which may generate responses again */
if (!ctx->eof) {
dd("eof not yet sent");
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
/* indicate last_buf */);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
}
return NGX_HTTP_OK;
}
return NGX_OK;
}
#endif
return NGX_DECLINED;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_
#define _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_access_handler(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_access_handler_inline(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_access_handler_file(ngx_http_request_t *r);
#endif /* _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,344 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_common.h"
#include "api/ngx_http_lua_api.h"
#include "ngx_http_lua_shdict.h"
#include "ngx_http_lua_util.h"
lua_State *
ngx_http_lua_get_global_state(ngx_conf_t *cf)
{
ngx_http_lua_main_conf_t *lmcf;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
return lmcf->lua;
}
ngx_http_request_t *
ngx_http_lua_get_request(lua_State *L)
{
return ngx_http_lua_get_req(L);
}
static ngx_int_t ngx_http_lua_shared_memory_init(ngx_shm_zone_t *shm_zone,
void *data);
ngx_int_t
ngx_http_lua_add_package_preload(ngx_conf_t *cf, const char *package,
lua_CFunction func)
{
lua_State *L;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_preload_hook_t *hook;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
L = lmcf->lua;
if (L) {
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
lua_pushcfunction(L, func);
lua_setfield(L, -2, package);
lua_pop(L, 2);
}
/* we always register preload_hooks since we always create new Lua VMs
* when lua code cache is off. */
if (lmcf->preload_hooks == NULL) {
lmcf->preload_hooks =
ngx_array_create(cf->pool, 4,
sizeof(ngx_http_lua_preload_hook_t));
if (lmcf->preload_hooks == NULL) {
return NGX_ERROR;
}
}
hook = ngx_array_push(lmcf->preload_hooks);
if (hook == NULL) {
return NGX_ERROR;
}
hook->package = (u_char *) package;
hook->loader = func;
return NGX_OK;
}
ngx_shm_zone_t *
ngx_http_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size,
void *tag)
{
ngx_http_lua_main_conf_t *lmcf;
ngx_shm_zone_t **zp;
ngx_shm_zone_t *zone;
ngx_http_lua_shm_zone_ctx_t *ctx;
ngx_int_t n;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
if (lmcf == NULL) {
return NULL;
}
if (lmcf->shm_zones == NULL) {
lmcf->shm_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t));
if (lmcf->shm_zones == NULL) {
return NULL;
}
if (ngx_array_init(lmcf->shm_zones, cf->pool, 2,
sizeof(ngx_shm_zone_t *))
!= NGX_OK)
{
return NULL;
}
}
zone = ngx_shared_memory_add(cf, name, (size_t) size, tag);
if (zone == NULL) {
return NULL;
}
if (zone->data) {
ctx = (ngx_http_lua_shm_zone_ctx_t *) zone->data;
return &ctx->zone;
}
n = sizeof(ngx_http_lua_shm_zone_ctx_t);
ctx = ngx_pcalloc(cf->pool, n);
if (ctx == NULL) {
return NULL;
}
ctx->lmcf = lmcf;
ctx->log = &cf->cycle->new_log;
ctx->cycle = cf->cycle;
ngx_memcpy(&ctx->zone, zone, sizeof(ngx_shm_zone_t));
zp = ngx_array_push(lmcf->shm_zones);
if (zp == NULL) {
return NULL;
}
*zp = zone;
/* set zone init */
zone->init = ngx_http_lua_shared_memory_init;
zone->data = ctx;
lmcf->requires_shm = 1;
return &ctx->zone;
}
static ngx_int_t
ngx_http_lua_shared_memory_init(ngx_shm_zone_t *shm_zone, void *data)
{
ngx_http_lua_shm_zone_ctx_t *octx = data;
ngx_shm_zone_t *ozone;
void *odata;
ngx_int_t rc;
volatile ngx_cycle_t *saved_cycle;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_shm_zone_ctx_t *ctx;
ngx_shm_zone_t *zone;
ctx = (ngx_http_lua_shm_zone_ctx_t *) shm_zone->data;
zone = &ctx->zone;
odata = NULL;
if (octx) {
ozone = &octx->zone;
odata = ozone->data;
}
zone->shm = shm_zone->shm;
#if (nginx_version >= 1009000)
zone->noreuse = shm_zone->noreuse;
#endif
if (zone->init(zone, odata) != NGX_OK) {
return NGX_ERROR;
}
dd("get lmcf");
lmcf = ctx->lmcf;
if (lmcf == NULL) {
return NGX_ERROR;
}
dd("lmcf->lua: %p", lmcf->lua);
lmcf->shm_zones_inited++;
if (lmcf->shm_zones_inited == lmcf->shm_zones->nelts
&& lmcf->init_handler && !ngx_test_config)
{
saved_cycle = ngx_cycle;
ngx_cycle = ctx->cycle;
rc = lmcf->init_handler(ctx->log, lmcf, lmcf->lua);
ngx_cycle = saved_cycle;
if (rc != NGX_OK) {
/* an error happened */
return NGX_ERROR;
}
}
return NGX_OK;
}
ngx_http_lua_co_ctx_t *
ngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r)
{
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
return ctx->cur_co_ctx;
}
void
ngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r, ngx_http_lua_co_ctx_t *coctx)
{
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
coctx->data = r;
ctx->cur_co_ctx = coctx;
}
lua_State *
ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx)
{
return coctx->co;
}
static ngx_int_t
ngx_http_lua_co_ctx_resume(ngx_http_request_t *r)
{
lua_State *vm;
ngx_connection_t *c;
ngx_int_t rc;
ngx_uint_t nreqs;
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return NGX_ERROR;
}
ctx->resume_handler = ngx_http_lua_wev_handler;
c = r->connection;
vm = ngx_http_lua_get_lua_vm(r, ctx);
nreqs = c->requests;
rc = ngx_http_lua_run_thread(vm, r, ctx, ctx->cur_co_ctx->nrets);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua run thread returned %d", rc);
if (rc == NGX_AGAIN) {
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
}
if (rc == NGX_DONE) {
ngx_http_lua_finalize_request(r, NGX_DONE);
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
}
if (ctx->entered_content_phase) {
ngx_http_lua_finalize_request(r, rc);
return NGX_DONE;
}
return rc;
}
void
ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets)
{
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_log_ctx_t *log_ctx;
r = coctx->data;
c = r->connection;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return;
}
if (c->fd != (ngx_socket_t) -1) { /* not a fake connection */
log_ctx = c->log->data;
log_ctx->current_request = r;
}
coctx->nrets = nrets;
coctx->cleanup = NULL;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"lua coctx resume handler: \"%V?%V\"", &r->uri, &r->args);
ctx->cur_co_ctx = coctx;
if (ctx->entered_content_phase) {
(void) ngx_http_lua_co_ctx_resume(r);
} else {
ctx->resume_handler = ngx_http_lua_co_ctx_resume;
ngx_http_core_run_phases(r);
}
ngx_http_run_posted_requests(c);
}
int
ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r)
{
ngx_http_lua_loc_conf_t *llcf;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
return llcf->http10_buffering;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,576 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_args.h"
#include "ngx_http_lua_util.h"
static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L);
static int ngx_http_lua_ngx_req_get_post_args(lua_State *L);
uintptr_t
ngx_http_lua_escape_args(u_char *dst, u_char *src, size_t size)
{
ngx_uint_t n;
static u_char hex[] = "0123456789ABCDEF";
/* %00-%20 %7F*/
static uint32_t escape[] = {
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x00000001, /* 0000 0000 0000 0000 0000 0000 0000 0001 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
};
if (dst == NULL) {
/* find the number of the characters to be escaped */
n = 0;
while (size) {
if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
n++;
}
src++;
size--;
}
return (uintptr_t) n;
}
while (size) {
if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
*dst++ = '%';
*dst++ = hex[*src >> 4];
*dst++ = hex[*src & 0xf];
src++;
} else {
*dst++ = *src++;
}
size--;
}
return (uintptr_t) dst;
}
static int
ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
{
ngx_http_request_t *r;
ngx_str_t args;
const char *msg;
size_t len;
u_char *p;
uintptr_t escape;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting 1 argument but seen %d",
lua_gettop(L));
}
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ngx_http_lua_check_fake_request(L, r);
switch (lua_type(L, 1)) {
case LUA_TNUMBER:
p = (u_char *) lua_tolstring(L, 1, &len);
args.data = ngx_palloc(r->pool, len);
if (args.data == NULL) {
return luaL_error(L, "no memory");
}
ngx_memcpy(args.data, p, len);
args.len = len;
break;
case LUA_TSTRING:
p = (u_char *) lua_tolstring(L, 1, &len);
escape = ngx_http_lua_escape_args(NULL, p, len);
if (escape > 0) {
args.len = len + 2 * escape;
args.data = ngx_palloc(r->pool, args.len);
if (args.data == NULL) {
return NGX_ERROR;
}
ngx_http_lua_escape_args(args.data, p, len);
} else {
args.data = ngx_palloc(r->pool, len);
if (args.data == NULL) {
return luaL_error(L, "no memory");
}
ngx_memcpy(args.data, p, len);
args.len = len;
}
break;
case LUA_TTABLE:
ngx_http_lua_process_args_option(r, L, 1, &args);
dd("args: %.*s", (int) args.len, args.data);
break;
default:
msg = lua_pushfstring(L, "string, number, or table expected, "
"but got %s", luaL_typename(L, 1));
return luaL_argerror(L, 1, msg);
}
dd("args: %.*s", (int) args.len, args.data);
r->args.data = args.data;
r->args.len = args.len;
r->valid_unparsed_uri = 0;
return 0;
}
static int
ngx_http_lua_ngx_req_get_post_args(lua_State *L)
{
ngx_http_request_t *r;
u_char *buf;
int retval;
size_t len;
ngx_chain_t *cl;
u_char *p;
u_char *last;
int n;
int max;
n = lua_gettop(L);
if (n != 0 && n != 1) {
return luaL_error(L, "expecting 0 or 1 arguments but seen %d", n);
}
if (n == 1) {
max = luaL_checkinteger(L, 1);
lua_pop(L, 1);
} else {
max = NGX_HTTP_LUA_MAX_ARGS;
}
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ngx_http_lua_check_fake_request(L, r);
if (r->discard_body) {
lua_createtable(L, 0, 0);
return 1;
}
if (r->request_body == NULL) {
return luaL_error(L, "no request body found; "
"maybe you should turn on lua_need_request_body?");
}
if (r->request_body->temp_file) {
lua_pushnil(L);
lua_pushliteral(L, "request body in temp file not supported");
return 2;
}
if (r->request_body->bufs == NULL) {
lua_createtable(L, 0, 0);
return 1;
}
/* we copy r->request_body->bufs over to buf to simplify
* unescaping query arg keys and values */
len = 0;
for (cl = r->request_body->bufs; cl; cl = cl->next) {
len += cl->buf->last - cl->buf->pos;
}
dd("post body length: %d", (int) len);
if (len == 0) {
lua_createtable(L, 0, 0);
return 1;
}
buf = ngx_palloc(r->pool, len);
if (buf == NULL) {
return luaL_error(L, "no memory");
}
lua_createtable(L, 0, 4);
p = buf;
for (cl = r->request_body->bufs; cl; cl = cl->next) {
p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos);
}
dd("post body: %.*s", (int) len, buf);
last = buf + len;
retval = ngx_http_lua_parse_args(L, buf, last, max);
ngx_pfree(r->pool, buf);
return retval;
}
int
ngx_http_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max)
{
u_char *p, *q;
u_char *src, *dst;
unsigned parsing_value;
size_t len;
int count = 0;
int top;
top = lua_gettop(L);
p = buf;
parsing_value = 0;
q = p;
while (p != last) {
if (*p == '=' && ! parsing_value) {
/* key data is between p and q */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key %.*s", (int) (dst - q), q);
/* push the key */
lua_pushlstring(L, (char *) q, dst - q);
/* skip the current '=' char */
p++;
q = p;
parsing_value = 1;
} else if (*p == '&') {
/* reached the end of a key or a value, just save it */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
/* push the value or key */
lua_pushlstring(L, (char *) q, dst - q);
/* skip the current '&' char */
p++;
q = p;
if (parsing_value) {
/* end of the current pair's value */
parsing_value = 0;
} else {
/* the current parsing pair takes no value,
* just push the value "true" */
dd("pushing boolean true");
lua_pushboolean(L, 1);
}
(void) lua_tolstring(L, -2, &len);
if (len == 0) {
/* ignore empty string key pairs */
dd("popping key and value...");
lua_pop(L, 2);
} else {
dd("setting table...");
ngx_http_lua_set_multi_value_table(L, top);
}
if (max > 0 && ++count == max) {
lua_pushliteral(L, "truncated");
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"lua hit query args limit %d", max);
return 2;
}
} else {
p++;
}
}
if (p != q || parsing_value) {
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
lua_pushlstring(L, (char *) q, dst - q);
if (!parsing_value) {
dd("pushing boolean true...");
lua_pushboolean(L, 1);
}
(void) lua_tolstring(L, -2, &len);
if (len == 0) {
/* ignore empty string key pairs */
dd("popping key and value...");
lua_pop(L, 2);
} else {
dd("setting table...");
ngx_http_lua_set_multi_value_table(L, top);
}
}
dd("gettop: %d", lua_gettop(L));
dd("type: %s", lua_typename(L, lua_type(L, 1)));
if (lua_gettop(L) != top) {
return luaL_error(L, "internal error: stack in bad state");
}
return 1;
}
void
ngx_http_lua_inject_req_args_api(lua_State *L)
{
lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri_args);
lua_setfield(L, -2, "set_uri_args");
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_post_args);
lua_setfield(L, -2, "get_post_args");
}
size_t
ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r)
{
return r->args.len;
}
int
ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r, int max,
int *truncated)
{
int count;
u_char *p, *last;
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
*truncated = 0;
if (max < 0) {
max = NGX_HTTP_LUA_MAX_ARGS;
}
last = r->args.data + r->args.len;
count = 0;
for (p = r->args.data; p != last; p++) {
if (*p == '&') {
if (count == 0) {
count += 2;
} else {
count++;
}
}
}
if (count) {
if (max > 0 && count > max) {
count = max;
*truncated = 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua hit query args limit %d", max);
}
return count;
}
if (r->args.len) {
return 1;
}
return 0;
}
int
ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r, u_char *buf,
ngx_http_lua_ffi_table_elt_t *out, int count)
{
int i, parsing_value = 0;
u_char *last, *p, *q;
u_char *src, *dst;
if (count <= 0) {
return NGX_OK;
}
ngx_memcpy(buf, r->args.data, r->args.len);
i = 0;
last = buf + r->args.len;
p = buf;
q = p;
while (p != last) {
if (*p == '=' && !parsing_value) {
/* key data is between p and q */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("saving key %.*s", (int) (dst - q), q);
out[i].key.data = q;
out[i].key.len = (int) (dst - q);
/* skip the current '=' char */
p++;
q = p;
parsing_value = 1;
} else if (*p == '&') {
/* reached the end of a key or a value, just save it */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
if (parsing_value) {
/* end of the current pair's value */
parsing_value = 0;
if (out[i].key.len) {
out[i].value.data = q;
out[i].value.len = (int) (dst - q);
i++;
}
} else {
/* the current parsing pair takes no value,
* just push the value "true" */
dd("pushing boolean true");
if (dst - q) {
out[i].key.data = q;
out[i].key.len = (int) (dst - q);
out[i].value.len = -1;
i++;
}
}
if (i == count) {
return i;
}
/* skip the current '&' char */
p++;
q = p;
} else {
p++;
}
}
if (p != q || parsing_value) {
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
if (parsing_value) {
if (out[i].key.len) {
out[i].value.data = q;
out[i].value.len = (int) (dst - q);
i++;
}
} else {
if (dst - q) {
out[i].key.data = q;
out[i].key.len = (int) (dst - q);
out[i].value.len = (int) -1;
i++;
}
}
}
return i;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_ARGS_H_INCLUDED_
#define _NGX_HTTP_LUA_ARGS_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_req_args_api(lua_State *L);
int ngx_http_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max);
#endif /* _NGX_HTTP_LUA_ARGS_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,812 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_cache.h"
#include "ngx_http_lua_balancer.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_directive.h"
struct ngx_http_lua_balancer_peer_data_s {
/* the round robin data must be first */
ngx_http_upstream_rr_peer_data_t rrp;
ngx_http_lua_srv_conf_t *conf;
ngx_http_request_t *request;
ngx_uint_t more_tries;
ngx_uint_t total_tries;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t *host;
in_port_t port;
int last_peer_state;
#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)
unsigned cloned_upstream_conf; /* :1 */
#endif
};
#if (NGX_HTTP_SSL)
static ngx_int_t ngx_http_lua_balancer_set_session(ngx_peer_connection_t *pc,
void *data);
static void ngx_http_lua_balancer_save_session(ngx_peer_connection_t *pc,
void *data);
#endif
static ngx_int_t ngx_http_lua_balancer_init(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us);
static ngx_int_t ngx_http_lua_balancer_init_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us);
static ngx_int_t ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc,
void *data);
static ngx_int_t ngx_http_lua_balancer_by_chunk(lua_State *L,
ngx_http_request_t *r);
static void ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc,
void *data, ngx_uint_t state);
ngx_int_t
ngx_http_lua_balancer_handler_file(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L)
{
ngx_int_t rc;
rc = ngx_http_lua_cache_loadfile(r->connection->log, L,
lscf->balancer.src.data,
&lscf->balancer.src_ref,
lscf->balancer.src_key);
if (rc != NGX_OK) {
return rc;
}
/* make sure we have a valid code chunk */
ngx_http_lua_assert(lua_isfunction(L, -1));
return ngx_http_lua_balancer_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_balancer_handler_inline(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L)
{
ngx_int_t rc;
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
lscf->balancer.src.data,
lscf->balancer.src.len,
&lscf->balancer.src_ref,
lscf->balancer.src_key,
(const char *) lscf->balancer.chunkname);
if (rc != NGX_OK) {
return rc;
}
/* make sure we have a valid code chunk */
ngx_http_lua_assert(lua_isfunction(L, -1));
return ngx_http_lua_balancer_by_chunk(L, r);
}
char *
ngx_http_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_balancer_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
size_t chunkname_len;
u_char *chunkname;
u_char *cache_key = NULL;
u_char *name;
ngx_str_t *value;
ngx_http_lua_srv_conf_t *lscf = conf;
ngx_http_upstream_srv_conf_t *uscf;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (lscf->balancer.handler) {
return "is duplicate";
}
value = cf->args->elts;
lscf->balancer.handler = (ngx_http_lua_srv_conf_handler_pt) cmd->post;
if (cmd->post == ngx_http_lua_balancer_handler_file) {
/* Lua code in an external file */
name = ngx_http_lua_rebase_path(cf->pool, value[1].data,
value[1].len);
if (name == NULL) {
return NGX_CONF_ERROR;
}
cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,
value[1].len);
if (cache_key == NULL) {
return NGX_CONF_ERROR;
}
lscf->balancer.src.data = name;
lscf->balancer.src.len = ngx_strlen(name);
} else {
cache_key = ngx_http_lua_gen_chunk_cache_key(cf, "balancer_by_lua",
value[1].data,
value[1].len);
if (cache_key == NULL) {
return NGX_CONF_ERROR;
}
chunkname = ngx_http_lua_gen_chunk_name(cf, "balancer_by_lua",
sizeof("balancer_by_lua") - 1,
&chunkname_len);
if (chunkname == NULL) {
return NGX_CONF_ERROR;
}
/* Don't eval nginx variables for inline lua code */
lscf->balancer.src = value[1];
lscf->balancer.chunkname = chunkname;
}
lscf->balancer.src_key = cache_key;
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
if (uscf->peer.init_upstream) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"load balancing method redefined");
}
uscf->peer.init_upstream = ngx_http_lua_balancer_init;
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
|NGX_HTTP_UPSTREAM_WEIGHT
|NGX_HTTP_UPSTREAM_MAX_FAILS
|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
|NGX_HTTP_UPSTREAM_DOWN;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_lua_balancer_init(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us)
{
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
return NGX_ERROR;
}
/* this callback is called upon individual requests */
us->peer.init = ngx_http_lua_balancer_init_peer;
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_balancer_init_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us)
{
ngx_http_lua_srv_conf_t *bcf;
ngx_http_lua_balancer_peer_data_t *bp;
bp = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_balancer_peer_data_t));
if (bp == NULL) {
return NGX_ERROR;
}
r->upstream->peer.data = &bp->rrp;
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
return NGX_ERROR;
}
r->upstream->peer.get = ngx_http_lua_balancer_get_peer;
r->upstream->peer.free = ngx_http_lua_balancer_free_peer;
#if (NGX_HTTP_SSL)
r->upstream->peer.set_session = ngx_http_lua_balancer_set_session;
r->upstream->peer.save_session = ngx_http_lua_balancer_save_session;
#endif
bcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module);
bp->conf = bcf;
bp->request = r;
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data)
{
lua_State *L;
ngx_int_t rc;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_srv_conf_t *lscf;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_balancer_peer_data_t *bp = data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"lua balancer peer, tries: %ui", pc->tries);
lscf = bp->conf;
r = bp->request;
ngx_http_lua_assert(lscf->balancer.handler && r);
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, ctx);
} else {
L = ngx_http_lua_get_lua_vm(r, ctx);
dd("reset ctx");
ngx_http_lua_reset_ctx(r, L, ctx);
}
ctx->context = NGX_HTTP_LUA_CONTEXT_BALANCER;
bp->sockaddr = NULL;
bp->socklen = 0;
bp->more_tries = 0;
bp->total_tries++;
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
/* balancer_by_lua does not support yielding and
* there cannot be any conflicts among concurrent requests,
* thus it is safe to store the peer data in the main conf.
*/
lmcf->balancer_peer_data = bp;
rc = lscf->balancer.handler(r, lscf, L);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->exited && ctx->exit_code != NGX_OK) {
rc = ctx->exit_code;
if (rc == NGX_ERROR
|| rc == NGX_BUSY
|| rc == NGX_DECLINED
#ifdef HAVE_BALANCER_STATUS_CODE_PATCH
|| rc >= NGX_HTTP_SPECIAL_RESPONSE
#endif
) {
return rc;
}
if (rc > NGX_OK) {
return NGX_ERROR;
}
}
if (bp->sockaddr && bp->socklen) {
pc->sockaddr = bp->sockaddr;
pc->socklen = bp->socklen;
pc->cached = 0;
pc->connection = NULL;
pc->name = bp->host;
bp->rrp.peers->single = 0;
if (bp->more_tries) {
r->upstream->peer.tries += bp->more_tries;
}
dd("tries: %d", (int) r->upstream->peer.tries);
return NGX_OK;
}
return ngx_http_upstream_get_round_robin_peer(pc, &bp->rrp);
}
static ngx_int_t
ngx_http_lua_balancer_by_chunk(lua_State *L, ngx_http_request_t *r)
{
u_char *err_msg;
size_t len;
ngx_int_t rc;
/* init nginx context in Lua VM */
ngx_http_lua_set_req(L, r);
#ifndef OPENRESTY_LUAJIT
ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
/* {{{ make new env inheriting main thread's globals table */
lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new env */
ngx_http_lua_get_globals_table(L);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
/* }}} */
lua_setfenv(L, -2); /* set new running env for the code closure */
#endif /* OPENRESTY_LUAJIT */
lua_pushcfunction(L, ngx_http_lua_traceback);
lua_insert(L, 1); /* put it under chunk and args */
/* protected call user code */
rc = lua_pcall(L, 0, 1, 1);
lua_remove(L, 1); /* remove traceback function */
dd("rc == %d", (int) rc);
if (rc != 0) {
/* error occurred when running loaded code */
err_msg = (u_char *) lua_tolstring(L, -1, &len);
if (err_msg == NULL) {
err_msg = (u_char *) "unknown reason";
len = sizeof("unknown reason") - 1;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"failed to run balancer_by_lua*: %*s", len, err_msg);
lua_settop(L, 0); /* clear remaining elems on stack */
return NGX_ERROR;
}
lua_settop(L, 0); /* clear remaining elems on stack */
return rc;
}
static void
ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state)
{
ngx_http_lua_balancer_peer_data_t *bp = data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"lua balancer free peer, tries: %ui", pc->tries);
if (bp->sockaddr && bp->socklen) {
bp->last_peer_state = (int) state;
if (pc->tries) {
pc->tries--;
}
return;
}
/* fallback */
ngx_http_upstream_free_round_robin_peer(pc, data, state);
}
#if (NGX_HTTP_SSL)
static ngx_int_t
ngx_http_lua_balancer_set_session(ngx_peer_connection_t *pc, void *data)
{
ngx_http_lua_balancer_peer_data_t *bp = data;
if (bp->sockaddr && bp->socklen) {
/* TODO */
return NGX_OK;
}
return ngx_http_upstream_set_round_robin_peer_session(pc, &bp->rrp);
}
static void
ngx_http_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data)
{
ngx_http_lua_balancer_peer_data_t *bp = data;
if (bp->sockaddr && bp->socklen) {
/* TODO */
return;
}
ngx_http_upstream_save_round_robin_peer_session(pc, &bp->rrp);
return;
}
#endif
int
ngx_http_lua_ffi_balancer_set_current_peer(ngx_http_request_t *r,
const u_char *addr, size_t addr_len, int port, char **err)
{
ngx_url_t url;
ngx_http_lua_ctx_t *ctx;
ngx_http_upstream_t *u;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_balancer_peer_data_t *bp;
if (r == NULL) {
*err = "no request found";
return NGX_ERROR;
}
u = r->upstream;
if (u == NULL) {
*err = "no upstream found";
return NGX_ERROR;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*err = "no ctx found";
return NGX_ERROR;
}
if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {
*err = "API disabled in the current context";
return NGX_ERROR;
}
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
/* we cannot read r->upstream->peer.data here directly because
* it could be overridden by other modules like
* ngx_http_upstream_keepalive_module.
*/
bp = lmcf->balancer_peer_data;
if (bp == NULL) {
*err = "no upstream peer data found";
return NGX_ERROR;
}
ngx_memzero(&url, sizeof(ngx_url_t));
url.url.data = ngx_palloc(r->pool, addr_len);
if (url.url.data == NULL) {
*err = "no memory";
return NGX_ERROR;
}
ngx_memcpy(url.url.data, addr, addr_len);
url.url.len = addr_len;
url.default_port = (in_port_t) port;
url.uri_part = 0;
url.no_resolve = 1;
if (ngx_parse_url(r->pool, &url) != NGX_OK) {
if (url.err) {
*err = url.err;
}
return NGX_ERROR;
}
if (url.addrs && url.addrs[0].sockaddr) {
bp->sockaddr = url.addrs[0].sockaddr;
bp->socklen = url.addrs[0].socklen;
bp->host = &url.addrs[0].name;
} else {
*err = "no host allowed";
return NGX_ERROR;
}
return NGX_OK;
}
int
ngx_http_lua_ffi_balancer_set_timeouts(ngx_http_request_t *r,
long connect_timeout, long send_timeout, long read_timeout,
char **err)
{
ngx_http_lua_ctx_t *ctx;
ngx_http_upstream_t *u;
#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)
ngx_http_upstream_conf_t *ucf;
#endif
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_balancer_peer_data_t *bp;
if (r == NULL) {
*err = "no request found";
return NGX_ERROR;
}
u = r->upstream;
if (u == NULL) {
*err = "no upstream found";
return NGX_ERROR;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*err = "no ctx found";
return NGX_ERROR;
}
if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {
*err = "API disabled in the current context";
return NGX_ERROR;
}
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
bp = lmcf->balancer_peer_data;
if (bp == NULL) {
*err = "no upstream peer data found";
return NGX_ERROR;
}
#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)
if (!bp->cloned_upstream_conf) {
/* we clone the upstream conf for the current request so that
* we do not affect other requests at all. */
ucf = ngx_palloc(r->pool, sizeof(ngx_http_upstream_conf_t));
if (ucf == NULL) {
*err = "no memory";
return NGX_ERROR;
}
ngx_memcpy(ucf, u->conf, sizeof(ngx_http_upstream_conf_t));
u->conf = ucf;
bp->cloned_upstream_conf = 1;
} else {
ucf = u->conf;
}
#endif
if (connect_timeout > 0) {
#if (HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)
u->connect_timeout = (ngx_msec_t) connect_timeout;
#else
ucf->connect_timeout = (ngx_msec_t) connect_timeout;
#endif
}
if (send_timeout > 0) {
#if (HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)
u->send_timeout = (ngx_msec_t) send_timeout;
#else
ucf->send_timeout = (ngx_msec_t) send_timeout;
#endif
}
if (read_timeout > 0) {
#if (HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)
u->read_timeout = (ngx_msec_t) read_timeout;
#else
ucf->read_timeout = (ngx_msec_t) read_timeout;
#endif
}
return NGX_OK;
}
int
ngx_http_lua_ffi_balancer_set_more_tries(ngx_http_request_t *r,
int count, char **err)
{
#if (nginx_version >= 1007005)
ngx_uint_t max_tries, total;
#endif
ngx_http_lua_ctx_t *ctx;
ngx_http_upstream_t *u;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_balancer_peer_data_t *bp;
if (r == NULL) {
*err = "no request found";
return NGX_ERROR;
}
u = r->upstream;
if (u == NULL) {
*err = "no upstream found";
return NGX_ERROR;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*err = "no ctx found";
return NGX_ERROR;
}
if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {
*err = "API disabled in the current context";
return NGX_ERROR;
}
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
bp = lmcf->balancer_peer_data;
if (bp == NULL) {
*err = "no upstream peer data found";
return NGX_ERROR;
}
#if (nginx_version >= 1007005)
max_tries = r->upstream->conf->next_upstream_tries;
total = bp->total_tries + r->upstream->peer.tries - 1;
if (max_tries && total + count > max_tries) {
count = max_tries - total;
*err = "reduced tries due to limit";
} else {
*err = NULL;
}
#else
*err = NULL;
#endif
bp->more_tries = count;
return NGX_OK;
}
int
ngx_http_lua_ffi_balancer_get_last_failure(ngx_http_request_t *r,
int *status, char **err)
{
ngx_http_lua_ctx_t *ctx;
ngx_http_upstream_t *u;
ngx_http_upstream_state_t *state;
ngx_http_lua_balancer_peer_data_t *bp;
ngx_http_lua_main_conf_t *lmcf;
if (r == NULL) {
*err = "no request found";
return NGX_ERROR;
}
u = r->upstream;
if (u == NULL) {
*err = "no upstream found";
return NGX_ERROR;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*err = "no ctx found";
return NGX_ERROR;
}
if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {
*err = "API disabled in the current context";
return NGX_ERROR;
}
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
bp = lmcf->balancer_peer_data;
if (bp == NULL) {
*err = "no upstream peer data found";
return NGX_ERROR;
}
if (r->upstream_states && r->upstream_states->nelts > 1) {
state = r->upstream_states->elts;
*status = (int) state[r->upstream_states->nelts - 2].status;
} else {
*status = 0;
}
return bp->last_peer_state;
}
int
ngx_http_lua_ffi_balancer_recreate_request(ngx_http_request_t *r,
char **err)
{
ngx_http_lua_ctx_t *ctx;
ngx_http_upstream_t *u;
if (r == NULL) {
*err = "no request found";
return NGX_ERROR;
}
u = r->upstream;
if (u == NULL) {
*err = "no upstream found";
return NGX_ERROR;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*err = "no ctx found";
return NGX_ERROR;
}
if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {
*err = "API disabled in the current context";
return NGX_ERROR;
}
/* u->create_request can not be NULL since we are in balancer phase */
ngx_http_lua_assert(u->create_request != NULL);
*err = NULL;
if (u->request_bufs != NULL && u->request_bufs != r->request_body->bufs) {
/* u->request_bufs already contains a valid request buffer
* remove it from chain first
*/
u->request_bufs = u->request_bufs->next;
}
return u->create_request(r);
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_BALANCER_H_INCLUDED_
#define _NGX_HTTP_LUA_BALANCER_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_balancer_handler_inline(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L);
ngx_int_t ngx_http_lua_balancer_handler_file(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L);
char *ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#endif /* _NGX_HTTP_LUA_BALANCER_H_INCLUDED_ */

View File

@ -0,0 +1,703 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_bodyfilterby.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_pcrefix.h"
#include "ngx_http_lua_log.h"
#include "ngx_http_lua_cache.h"
#include "ngx_http_lua_headers.h"
#include "ngx_http_lua_string.h"
#include "ngx_http_lua_misc.h"
#include "ngx_http_lua_consts.h"
#include "ngx_http_lua_output.h"
static void ngx_http_lua_body_filter_by_lua_env(lua_State *L,
ngx_http_request_t *r, ngx_chain_t *in);
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
/**
* Set environment table for the given code closure.
*
* Before:
* | code closure | <- top
* | ... |
*
* After:
* | code closure | <- top
* | ... |
* */
static void
ngx_http_lua_body_filter_by_lua_env(lua_State *L, ngx_http_request_t *r,
ngx_chain_t *in)
{
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_set_req(L, r);
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
lmcf->body_filter_chain = in;
#ifndef OPENRESTY_LUAJIT
/**
* we want to create empty environment for current script
*
* setmetatable({}, {__index = _G})
*
* if a function or symbol is not defined in our env, __index will lookup
* in the global env.
*
* all variables created in the script-env will be thrown away at the end
* of the script run.
* */
ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
/* {{{ make new env inheriting main thread's globals table */
lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new
env */
ngx_http_lua_get_globals_table(L);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
/* }}} */
lua_setfenv(L, -2); /* set new running env for the code closure */
#endif /* OPENRESTY_LUAJIT */
}
ngx_int_t
ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r,
ngx_chain_t *in)
{
ngx_int_t rc;
u_char *err_msg;
size_t len;
#if (NGX_PCRE)
ngx_pool_t *old_pool;
#endif
dd("initialize nginx context in Lua VM, code chunk at stack top sp = 1");
ngx_http_lua_body_filter_by_lua_env(L, r, in);
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif
lua_pushcfunction(L, ngx_http_lua_traceback);
lua_insert(L, 1); /* put it under chunk and args */
dd("protected call user code");
rc = lua_pcall(L, 0, 1, 1);
lua_remove(L, 1); /* remove traceback function */
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
ngx_http_lua_pcre_malloc_done(old_pool);
#endif
if (rc != 0) {
/* error occurred */
err_msg = (u_char *) lua_tolstring(L, -1, &len);
if (err_msg == NULL) {
err_msg = (u_char *) "unknown reason";
len = sizeof("unknown reason") - 1;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"failed to run body_filter_by_lua*: %*s", len, err_msg);
lua_settop(L, 0); /* clear remaining elems on stack */
return NGX_ERROR;
}
/* rc == 0 */
rc = (ngx_int_t) lua_tointeger(L, -1);
dd("got return value: %d", (int) rc);
lua_settop(L, 0);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in)
{
lua_State *L;
ngx_int_t rc;
ngx_http_lua_loc_conf_t *llcf;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
llcf->body_filter_src.value.data,
llcf->body_filter_src.value.len,
&llcf->body_filter_src_ref,
llcf->body_filter_src_key,
(const char *)
llcf->body_filter_chunkname);
if (rc != NGX_OK) {
return NGX_ERROR;
}
rc = ngx_http_lua_body_filter_by_chunk(L, r, in);
dd("body filter by chunk returns %d", (int) rc);
if (rc != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in)
{
lua_State *L;
ngx_int_t rc;
u_char *script_path;
ngx_http_lua_loc_conf_t *llcf;
ngx_str_t eval_src;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
/* Eval nginx variables in code path string first */
if (ngx_http_complex_value(r, &llcf->body_filter_src, &eval_src)
!= NGX_OK)
{
return NGX_ERROR;
}
script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
eval_src.len);
if (script_path == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
&llcf->body_filter_src_ref,
llcf->body_filter_src_key);
if (rc != NGX_OK) {
return NGX_ERROR;
}
/* make sure we have a valid code chunk */
ngx_http_lua_assert(lua_isfunction(L, -1));
rc = ngx_http_lua_body_filter_by_chunk(L, r, in);
if (rc != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_lua_loc_conf_t *llcf;
ngx_http_lua_ctx_t *ctx;
ngx_int_t rc;
uint16_t old_context;
ngx_pool_cleanup_t *cln;
ngx_chain_t *out;
ngx_chain_t *cl, *ln;
ngx_http_lua_main_conf_t *lmcf;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua body filter for user lua code, uri \"%V\"", &r->uri);
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->body_filter_handler == NULL || r->header_only) {
dd("no body filter handler found");
return ngx_http_next_body_filter(r, in);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
}
if (ctx->seen_last_in_filter) {
for (/* void */; in; in = in->next) {
dd("mark the buf as consumed: %d", (int) ngx_buf_size(in->buf));
in->buf->pos = in->buf->last;
in->buf->file_pos = in->buf->file_last;
}
in = NULL;
/* continue to call ngx_http_next_body_filter to process cached data */
}
if (in != NULL
&& ngx_chain_add_copy(r->pool, &ctx->filter_in_bufs, in) != NGX_OK)
{
return NGX_ERROR;
}
if (ctx->filter_busy_bufs != NULL
&& (r->connection->buffered
& (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)))
{
/* Socket write buffer was full on last write.
* Try to write the remain data, if still can not write
* do not execute body_filter_by_lua otherwise the `in` chain will be
* replaced by new content from lua and buf of `in` mark as consumed.
* And then ngx_output_chain will call the filter chain again which
* make all the data cached in the memory and long ngx_chain_t link
* cause CPU 100%.
*/
rc = ngx_http_next_body_filter(r, NULL);
if (rc == NGX_ERROR) {
return rc;
}
out = NULL;
ngx_chain_update_chains(r->pool,
&ctx->free_bufs, &ctx->filter_busy_bufs, &out,
(ngx_buf_tag_t) &ngx_http_lua_body_filter);
if (rc != NGX_OK
&& ctx->filter_busy_bufs != NULL
&& (r->connection->buffered
& (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)))
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"waiting body filter busy buffer to be sent");
return NGX_AGAIN;
}
/* continue to process bufs in ctx->filter_in_bufs */
}
if (ctx->cleanup == NULL) {
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_lua_request_cleanup_handler;
cln->data = ctx;
ctx->cleanup = &cln->handler;
}
old_context = ctx->context;
ctx->context = NGX_HTTP_LUA_CONTEXT_BODY_FILTER;
in = ctx->filter_in_bufs;
ctx->filter_in_bufs = NULL;
if (in != NULL) {
dd("calling body filter handler");
rc = llcf->body_filter_handler(r, in);
dd("calling body filter handler returned %d", (int) rc);
ctx->context = old_context;
if (rc != NGX_OK) {
return NGX_ERROR;
}
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
/* lmcf->body_filter_chain is the new buffer chain if
* body_filter_by_lua set new body content via ngx.arg[1] = new_content
* otherwise it is the original `in` buffer chain.
*/
out = lmcf->body_filter_chain;
if (in != out) {
/* content of body was replaced in
* ngx_http_lua_body_filter_param_set and the buffers was marked
* as consumed.
*/
for (cl = in; cl != NULL; cl = ln) {
ln = cl->next;
ngx_free_chain(r->pool, cl);
}
if (out == NULL) {
/* do not forward NULL to the next filters because the input is
* not NULL */
return NGX_OK;
}
}
} else {
out = NULL;
}
rc = ngx_http_next_body_filter(r, out);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
ngx_chain_update_chains(r->pool,
&ctx->free_bufs, &ctx->filter_busy_bufs, &out,
(ngx_buf_tag_t) &ngx_http_lua_body_filter);
return rc;
}
ngx_int_t
ngx_http_lua_body_filter_init(void)
{
dd("calling body filter init");
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_lua_body_filter;
return NGX_OK;
}
int
ngx_http_lua_ffi_get_body_filter_param_eof(ngx_http_request_t *r)
{
ngx_chain_t *cl;
ngx_chain_t *in;
ngx_http_lua_main_conf_t *lmcf;
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
in = lmcf->body_filter_chain;
/* asking for the eof argument */
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_buf || cl->buf->last_in_chain) {
return 1;
}
}
return 0;
}
int
ngx_http_lua_ffi_get_body_filter_param_body(ngx_http_request_t *r,
u_char **data_p, size_t *len_p)
{
size_t size;
ngx_chain_t *cl;
ngx_buf_t *b;
ngx_chain_t *in;
ngx_http_lua_main_conf_t *lmcf;
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
in = lmcf->body_filter_chain;
size = 0;
if (in == NULL) {
/* being a cleared chain on the Lua land */
*len_p = 0;
return NGX_OK;
}
if (in->next == NULL) {
dd("seen only single buffer");
b = in->buf;
*data_p = b->pos;
*len_p = b->last - b->pos;
return NGX_OK;
}
dd("seen multiple buffers");
for (cl = in; cl; cl = cl->next) {
b = cl->buf;
size += b->last - b->pos;
if (b->last_buf || b->last_in_chain) {
break;
}
}
/* the buf is need and is not allocated from Lua land yet, return with
* the actual size */
*len_p = size;
return NGX_AGAIN;
}
int
ngx_http_lua_ffi_copy_body_filter_param_body(ngx_http_request_t *r,
u_char *data)
{
u_char *p;
ngx_chain_t *cl;
ngx_buf_t *b;
ngx_chain_t *in;
ngx_http_lua_main_conf_t *lmcf;
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
in = lmcf->body_filter_chain;
for (p = data, cl = in; cl; cl = cl->next) {
b = cl->buf;
p = ngx_copy(p, b->pos, b->last - b->pos);
if (b->last_buf || b->last_in_chain) {
break;
}
}
return NGX_OK;
}
int
ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx)
{
int type;
int idx;
int found;
u_char *data;
size_t size;
unsigned last;
unsigned flush = 0;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_chain_t *in;
ngx_http_lua_main_conf_t *lmcf;
idx = luaL_checkint(L, 2);
dd("index: %d", idx);
if (idx != 1 && idx != 2) {
return luaL_error(L, "bad index: %d", idx);
}
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
if (idx == 2) {
/* overwriting the eof flag */
last = lua_toboolean(L, 3);
in = lmcf->body_filter_chain;
if (last) {
ctx->seen_last_in_filter = 1;
/* the "in" chain cannot be NULL and we set the "last_buf" or
* "last_in_chain" flag in the last buf of "in" */
for (cl = in; cl; cl = cl->next) {
if (cl->next == NULL) {
if (r == r->main) {
cl->buf->last_buf = 1;
} else {
cl->buf->last_in_chain = 1;
}
break;
}
}
} else {
/* last == 0 */
found = 0;
for (cl = in; cl; cl = cl->next) {
b = cl->buf;
if (b->last_buf) {
b->last_buf = 0;
found = 1;
}
if (b->last_in_chain) {
b->last_in_chain = 0;
found = 1;
}
if (found && b->last == b->pos && !ngx_buf_in_memory(b)) {
/* make it a special sync buf to make
* ngx_http_write_filter_module happy. */
b->sync = 1;
}
}
ctx->seen_last_in_filter = 0;
}
return 0;
}
/* idx == 1, overwriting the chunk data */
type = lua_type(L, 3);
switch (type) {
case LUA_TSTRING:
case LUA_TNUMBER:
data = (u_char *) lua_tolstring(L, 3, &size);
break;
case LUA_TNIL:
/* discard the buffers */
in = lmcf->body_filter_chain;
last = 0;
for (cl = in; cl; cl = cl->next) {
b = cl->buf;
if (b->flush) {
flush = 1;
}
if (b->last_in_chain || b->last_buf) {
last = 1;
}
dd("mark the buf as consumed: %d", (int) ngx_buf_size(b));
b->pos = b->last;
}
/* cl == NULL */
goto done;
case LUA_TTABLE:
size = ngx_http_lua_calc_strlen_in_table(L, 3 /* index */, 3 /* arg */,
1 /* strict */);
data = NULL;
break;
default:
return luaL_error(L, "bad chunk data type: %s",
lua_typename(L, type));
}
in = lmcf->body_filter_chain;
last = 0;
for (cl = in; cl; cl = cl->next) {
b = cl->buf;
if (b->flush) {
flush = 1;
}
if (b->last_buf || b->last_in_chain) {
last = 1;
}
dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf));
cl->buf->pos = cl->buf->last;
}
/* cl == NULL */
if (size == 0) {
goto done;
}
cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,
&ctx->free_bufs, size);
if (cl == NULL) {
return luaL_error(L, "no memory");
}
cl->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter;
if (type == LUA_TTABLE) {
cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last);
} else {
cl->buf->last = ngx_copy(cl->buf->pos, data, size);
}
done:
if (last || flush) {
if (cl == NULL) {
cl = ngx_http_lua_chain_get_free_buf(r->connection->log,
r->pool,
&ctx->free_bufs, 0);
if (cl == NULL) {
return luaL_error(L, "no memory");
}
cl->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter;
}
if (last) {
ctx->seen_last_in_filter = 1;
if (r == r->main) {
cl->buf->last_buf = 1;
} else {
cl->buf->last_in_chain = 1;
}
}
if (flush) {
cl->buf->flush = 1;
}
}
lmcf->body_filter_chain = cl;
return 0;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_
#define _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
extern ngx_http_output_body_filter_pt ngx_http_lua_next_filter_body_filter;
ngx_int_t ngx_http_lua_body_filter_init(void);
ngx_int_t ngx_http_lua_body_filter_by_chunk(lua_State *L,
ngx_http_request_t *r, ngx_chain_t *in);
ngx_int_t ngx_http_lua_body_filter_inline(ngx_http_request_t *r,
ngx_chain_t *in);
ngx_int_t ngx_http_lua_body_filter_file(ngx_http_request_t *r,
ngx_chain_t *in);
int ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx);
#endif /* _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,410 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include <ngx_md5.h>
#include "ngx_http_lua_common.h"
#include "ngx_http_lua_cache.h"
#include "ngx_http_lua_clfactory.h"
#include "ngx_http_lua_util.h"
static u_char *ngx_http_lua_gen_file_cache_key_helper(u_char *out,
const u_char *src, size_t src_len);
/**
* Find code chunk associated with the given key in code cache,
* and push it to the top of Lua stack if found.
*
* Stack layout before call:
* | ... | <- top
*
* Stack layout after call:
* | code chunk | <- top
* | ... |
*
* */
static ngx_int_t
ngx_http_lua_cache_load_code(ngx_log_t *log, lua_State *L,
int *ref, const char *key)
{
#ifndef OPENRESTY_LUAJIT
int rc;
u_char *err;
#endif
/* get code cache table */
lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
code_cache_key));
lua_rawget(L, LUA_REGISTRYINDEX); /* sp++ */
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"code cache lookup (key='%s', ref=%d)", key, *ref);
dd("code cache table to load: %p", lua_topointer(L, -1));
if (!lua_istable(L, -1)) {
dd("Error: code cache table to load did not exist!!");
return NGX_ERROR;
}
ngx_http_lua_assert(key != NULL);
if (*ref == LUA_NOREF) {
lua_getfield(L, -1, key); /* cache closure */
} else {
if (*ref == LUA_REFNIL) {
lua_getfield(L, -1, key); /* cache ref */
if (!lua_isnumber(L, -1)) {
goto not_found;
}
*ref = lua_tonumber(L, -1);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"code cache setting ref (key='%s', ref=%d)",
key, *ref);
lua_pop(L, 1); /* cache */
}
lua_rawgeti(L, -1, *ref); /* cache closure */
}
if (lua_isfunction(L, -1)) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"code cache hit (key='%s', ref=%d)", key, *ref);
#ifdef OPENRESTY_LUAJIT
lua_remove(L, -2); /* sp-- */
return NGX_OK;
#else
/* call closure factory to gen new closure */
rc = lua_pcall(L, 0, 1, 0);
if (rc == 0) {
/* remove cache table from stack, leave code chunk at
* top of stack */
lua_remove(L, -2); /* sp-- */
return NGX_OK;
}
if (lua_isstring(L, -1)) {
err = (u_char *) lua_tostring(L, -1);
} else {
err = (u_char *) "unknown error";
}
ngx_log_error(NGX_LOG_ERR, log, 0,
"lua: failed to run factory at key \"%s\": %s",
key, err);
lua_pop(L, 2);
return NGX_ERROR;
#endif /* OPENRESTY_LUAJIT */
}
not_found:
dd("Value associated with given key in code cache table is not code "
"chunk: stack top=%d, top value type=%s\n",
lua_gettop(L), luaL_typename(L, -1));
/* remove cache table and value from stack */
lua_pop(L, 2); /* sp-=2 */
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"code cache miss (key='%s', ref=%d)", key, *ref);
return NGX_DECLINED;
}
/**
* Store the closure factory at the top of Lua stack to code cache, and
* associate it with the given key. Then generate new closure.
*
* Stack layout before call:
* | code factory | <- top
* | ... |
*
* Stack layout after call:
* | code chunk | <- top
* | ... |
*
* */
static ngx_int_t
ngx_http_lua_cache_store_code(lua_State *L, int *ref, const char *key)
{
#ifndef OPENRESTY_LUAJIT
int rc;
#endif
/* get code cache table */
lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
code_cache_key));
lua_rawget(L, LUA_REGISTRYINDEX);
dd("Code cache table to store: %p", lua_topointer(L, -1));
if (!lua_istable(L, -1)) {
dd("Error: code cache table to load did not exist!!");
return NGX_ERROR;
}
ngx_http_lua_assert(key != NULL);
lua_pushvalue(L, -2); /* closure cache closure */
if (*ref == LUA_NOREF) {
/* cache closure by cache key */
lua_setfield(L, -2, key); /* closure cache */
} else {
/* cache closure with reference */
*ref = luaL_ref(L, -2); /* closure cache */
/* cache reference by cache key */
lua_pushnumber(L, *ref); /* closure cache ref */
lua_setfield(L, -2, key); /* closure cache */
}
/* remove cache table, leave closure factory at top of stack */
lua_pop(L, 1); /* closure */
#ifndef OPENRESTY_LUAJIT
/* call closure factory to generate new closure */
rc = lua_pcall(L, 0, 1, 0);
if (rc != 0) {
dd("Error: failed to call closure factory!!");
return NGX_ERROR;
}
#endif
return NGX_OK;
}
ngx_int_t
ngx_http_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L,
const u_char *src, size_t src_len, int *cache_ref, const u_char *cache_key,
const char *name)
{
int n;
ngx_int_t rc;
const char *err = NULL;
n = lua_gettop(L);
rc = ngx_http_lua_cache_load_code(log, L, cache_ref, (char *) cache_key);
if (rc == NGX_OK) {
return NGX_OK;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/* rc == NGX_DECLINED */
/* load closure factory of inline script to the top of lua stack, sp++ */
rc = ngx_http_lua_clfactory_loadbuffer(L, (char *) src, src_len, name);
if (rc != 0) {
/* Oops! error occurred when loading Lua script */
if (rc == LUA_ERRMEM) {
err = "memory allocation error";
} else {
if (lua_isstring(L, -1)) {
err = lua_tostring(L, -1);
} else {
err = "unknown error";
}
}
goto error;
}
/* store closure factory and gen new closure at the top of lua stack to
* code cache */
rc = ngx_http_lua_cache_store_code(L, cache_ref, (char *) cache_key);
if (rc != NGX_OK) {
err = "fail to generate new closure from the closure factory";
goto error;
}
return NGX_OK;
error:
ngx_log_error(NGX_LOG_ERR, log, 0,
"failed to load inlined Lua code: %s", err);
lua_settop(L, n);
return NGX_ERROR;
}
ngx_int_t
ngx_http_lua_cache_loadfile(ngx_log_t *log, lua_State *L,
const u_char *script, int *cache_ref, const u_char *cache_key)
{
int n;
ngx_int_t rc, errcode = NGX_ERROR;
u_char buf[NGX_HTTP_LUA_FILE_KEY_LEN + 1];
const char *err = NULL;
n = lua_gettop(L);
/* calculate digest of script file path */
if (cache_key == NULL) {
dd("CACHE file key not pre-calculated...calculating");
cache_key = ngx_http_lua_gen_file_cache_key_helper(buf, script,
ngx_strlen(script));
*cache_ref = LUA_NOREF;
} else {
dd("CACHE file key already pre-calculated");
ngx_http_lua_assert(cache_ref != NULL && *cache_ref != LUA_NOREF);
}
rc = ngx_http_lua_cache_load_code(log, L, cache_ref, (char *) cache_key);
if (rc == NGX_OK) {
return NGX_OK;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/* rc == NGX_DECLINED */
/* load closure factory of script file to the top of lua stack, sp++ */
rc = ngx_http_lua_clfactory_loadfile(L, (char *) script);
dd("loadfile returns %d (%d)", (int) rc, LUA_ERRFILE);
if (rc != 0) {
/* Oops! error occurred when loading Lua script */
switch (rc) {
case LUA_ERRMEM:
err = "memory allocation error";
break;
case LUA_ERRFILE:
if (errno == ENOENT) {
errcode = NGX_HTTP_NOT_FOUND;
} else {
errcode = NGX_HTTP_SERVICE_UNAVAILABLE;
}
/* fall through */
default:
if (lua_isstring(L, -1)) {
err = lua_tostring(L, -1);
} else {
err = "unknown error";
}
}
goto error;
}
/* store closure factory and gen new closure at the top of lua stack
* to code cache */
rc = ngx_http_lua_cache_store_code(L, cache_ref, (char *) cache_key);
if (rc != NGX_OK) {
err = "fail to generate new closure from the closure factory";
goto error;
}
return NGX_OK;
error:
ngx_log_error(NGX_LOG_ERR, log, 0,
"failed to load external Lua file \"%s\": %s", script, err);
lua_settop(L, n);
return errcode;
}
u_char *
ngx_http_lua_gen_chunk_cache_key(ngx_conf_t *cf, const char *tag,
const u_char *src, size_t src_len)
{
u_char *p, *out;
size_t tag_len;
tag_len = ngx_strlen(tag);
out = ngx_palloc(cf->pool, tag_len + NGX_HTTP_LUA_INLINE_KEY_LEN + 2);
if (out == NULL) {
return NULL;
}
p = ngx_copy(out, tag, tag_len);
p = ngx_copy(p, "_", 1);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, src, src_len);
*p = '\0';
return out;
}
static u_char *
ngx_http_lua_gen_file_cache_key_helper(u_char *out, const u_char *src,
size_t src_len)
{
u_char *p;
ngx_http_lua_assert(out != NULL);
if (out == NULL) {
return NULL;
}
p = ngx_copy(out, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, src, src_len);
*p = '\0';
return out;
}
u_char *
ngx_http_lua_gen_file_cache_key(ngx_conf_t *cf, const u_char *src,
size_t src_len)
{
u_char *out;
out = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (out == NULL) {
return NULL;
}
return ngx_http_lua_gen_file_cache_key_helper(out, src, src_len);
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CACHE_H_INCLUDED_
#define _NGX_HTTP_LUA_CACHE_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L,
const u_char *src, size_t src_len, int *cache_ref, const u_char *cache_key,
const char *name);
ngx_int_t ngx_http_lua_cache_loadfile(ngx_log_t *log, lua_State *L,
const u_char *script, int *cache_ref, const u_char *cache_key);
u_char *ngx_http_lua_gen_chunk_cache_key(ngx_conf_t *cf, const char *tag,
const u_char *src, size_t src_len);
u_char *ngx_http_lua_gen_file_cache_key(ngx_conf_t *cf, const u_char *src,
size_t src_len);
#endif /* _NGX_HTTP_LUA_CACHE_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,176 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include "ngx_http_lua_capturefilter.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_subrequest.h"
ngx_http_output_header_filter_pt ngx_http_lua_next_header_filter;
ngx_http_output_body_filter_pt ngx_http_lua_next_body_filter;
static ngx_int_t ngx_http_lua_capture_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_lua_capture_body_filter(ngx_http_request_t *r,
ngx_chain_t *in);
ngx_int_t
ngx_http_lua_capture_filter_init(ngx_conf_t *cf)
{
/* setting up output filters to intercept subrequest responses */
ngx_http_lua_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_lua_capture_header_filter;
ngx_http_lua_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_lua_capture_body_filter;
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_capture_header_filter(ngx_http_request_t *r)
{
ngx_http_post_subrequest_t *psr;
ngx_http_lua_ctx_t *old_ctx;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_post_subrequest_data_t *psr_data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua capture header filter, uri \"%V\"", &r->uri);
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("old ctx: %p", ctx);
if (ctx == NULL || ! ctx->capture) {
psr = r->post_subrequest;
if (psr != NULL
&& psr->handler == ngx_http_lua_post_subrequest
&& psr->data != NULL)
{
/* the lua ctx has been cleared by ngx_http_internal_redirect,
* resume it from the post_subrequest data
*/
psr_data = psr->data;
old_ctx = psr_data->ctx;
if (ctx == NULL) {
ctx = old_ctx;
ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
} else {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua restoring ctx with capture %d, index %d",
old_ctx->capture, old_ctx->index);
ctx->capture = old_ctx->capture;
ctx->index = old_ctx->index;
ctx->body = NULL;
ctx->last_body = &ctx->body;
psr_data->ctx = ctx;
}
}
}
if (ctx && ctx->capture) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua capturing response body");
/* force subrequest response body buffer in memory */
r->filter_need_in_memory = 1;
r->header_sent = 1;
ctx->header_sent = 1;
if (r->method == NGX_HTTP_HEAD) {
r->header_only = 1;
}
return NGX_OK;
}
return ngx_http_lua_next_header_filter(r);
}
static ngx_int_t
ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
int rc;
ngx_int_t eof;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_ctx_t *pr_ctx;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua capture body filter, uri \"%V\"", &r->uri);
if (in == NULL) {
return ngx_http_lua_next_body_filter(r, NULL);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (!ctx || !ctx->capture) {
dd("no ctx or no capture %.*s", (int) r->uri.len, r->uri.data);
return ngx_http_lua_next_body_filter(r, in);
}
if (ctx->run_post_subrequest) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua body filter skipped because post subrequest "
"already run");
return NGX_OK;
}
if (r->parent == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua body filter skipped because no parent request "
"found");
return NGX_ERROR;
}
pr_ctx = ngx_http_get_module_ctx(r->parent, ngx_http_lua_module);
if (pr_ctx == NULL) {
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua capture body filter capturing response body, uri "
"\"%V\"", &r->uri);
rc = ngx_http_lua_add_copy_chain(r, pr_ctx, &ctx->last_body, in, &eof);
if (rc != NGX_OK) {
return NGX_ERROR;
}
dd("add copy chain eof: %d, sr: %d", (int) eof, r != r->main);
if (eof) {
ctx->seen_last_for_subreq = 1;
}
ngx_http_lua_discard_bufs(r->pool, in);
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CAPTUREFILTER_H_INCLUDED_
#define _NGX_HTTP_LUA_CAPTUREFILTER_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_capture_filter_init(ngx_conf_t *cf);
#endif /* NGX_HTTP_LUA_CAPTUREFILTER_H */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,928 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include "ngx_http_lua_clfactory.h"
#ifndef OPENRESTY_LUAJIT
#define CLFACTORY_BEGIN_CODE "return function() "
#define CLFACTORY_BEGIN_SIZE (sizeof(CLFACTORY_BEGIN_CODE) - 1)
#define CLFACTORY_END_CODE "\nend"
#define CLFACTORY_END_SIZE (sizeof(CLFACTORY_END_CODE) - 1)
#endif
/*
* taken from chaoslawful:
* Lua bytecode header Luajit bytecode header
* -------------- --------------
* | \033Lua | 0-3 | \033LJ | 0-2
* -------------- --------------
* | LuaC | 4 | bytecode | 3
* | Version | | version |
* -------------- --------------
* | LuaC | 5 | misc flag | 4 [F|S|B]
* | Format | --------------
* -------------- | chunkname | ULEB128 var-len
* | Endian | 6 | len | encoded uint32
* -------------- --------------
* | size of | 7 | chunkname |
* | int | | str no \0 |
* -------------- --------------
* | size of | 8
* | size_t |
* --------------
* | size of | 9
* | instruction|
* --------------
* | size of | 10
* | number |
* --------------
* | number | 11
* | is int? |
* --------------
*/
/*
* CLOSURE 0 0 RETURN 0 2 RETURN 0 1
* length(Instruction) = 4 or 8
* little endian or big endian
*/
#ifndef OPENRESTY_LUAJIT
#define LUA_LITTLE_ENDIAN_4BYTES_CODE \
"\x24\x00\x00\x00\x1e\x00\x00\x01\x1e\x00\x80\x00"
#define LUA_LITTLE_ENDIAN_8BYTES_CODE \
"\x24\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x01" \
"\x00\x00\x00\x00\x1e\x00\x80\x00\x00\x00\x00\x00"
#define LUA_BIG_ENDIAN_4BYTES_CODE \
"\x00\x00\x00\x24\x01\x00\x00\x1e\x00\x08\x00\x1e"
#define LUA_BIG_ENDIAN_8BYTES_CODE \
"\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x00" \
"\x01\x00\x00\x1e\x00\x00\x00\x00\x00\x08\x00\x1e"
#define LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4)
#define LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8)
#define LUA_BIG_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4)
#define LUA_BIG_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8)
#define LUAC_HEADERSIZE 12
#define LUAC_VERSION 0x51
#endif /* OPENRESTY_LUAJIT */
/*
* taken from chaoslawful:
* Lua Proto
* ---------------------
* | String | Can be empty string
* | [source] | (stripped or internal function)
* ---------------------
* | Int | At which line this function is defined
* | [linedefined] |
* ---------------------
* | Int | At while line this function definition ended
* | [lastlinedefined] |
* ---------------------
* | Char | Number of upvalues referenced by this function
* | [nups] |
* ---------------------
* | Char | Number of parameters of this function
* | [numparams] |
* ---------------------
* | Char | Does this function has variable number of arguments?
* | [is_var_arg] | main function always set to VARARG_ISVARARG (2)
* ---------------------
* | Char | Maximum stack size this function used
* | [maxstacksize] | Initially set to 2
* ---------------------
* | Vector(instr) | Code instructions of this function
* | [code] |
* ---------------------
* | Int | Number of constants referenced by this function
* | [sizek] |
* ---------------------
* | Char | ------------------------------------
* | type of [k[i]] | The type and content of constants |
* --------------------- |-> repeat for i in
* | Char if boolean | No content part if type is NIL | [1..sizek]
* | Number if number | ------------------------------------
* | String if string |
* ---------------------
* | Int | Number of internal functions
* | [sizep] |
* ---------------------
* | Function | -> repeat for i in [1..sizep]
* | at [p[i]] |
* ---------------------
* | Vector | Debug lineinfo vector
* | [lineinfo] | Empty vector here if debug info is stripped
* ---------------------
* | Int | Number of local variable in this function
* | [sizelocvars] | 0 if debug info is stripped
* ---------------------
* | String | ------------------------------------
* | [locvars[i]] | Name of local var i |
* | .varname] | |
* --------------------- |
* | Int | instruction counter |
* | [locvars[i]] | where local var i start to be |-> repeat for i in
* | .startpc] | referenced | [0..sizelocvars]
* --------------------- |
* | Int | instruction counter, where local |
* | [locvars[i]] | var i ceased to be referenced |
* | .endpc] | ------------------------------------
* ---------------------
* | Int | Number of upvalues referenced by this function,
* | [sizeupvalues] | 0 if stripped
* ---------------------
* | String | -> repeat for i in[0..sizeupvalues]
* | [upvalues[i]] |
* ---------------------
*/
#ifndef OPENRESTY_LUAJIT
#define POS_SOURCE_STR_LEN LUAC_HEADERSIZE
#define POS_START_LINE (POS_SOURCE_STR_LEN + sizeof(size_t))
#define POS_LAST_LINE (POS_START_LINE + sizeof(int))
#define POS_NUM_OF_UPVS (POS_LAST_LINE + sizeof(int))
#define POS_NUM_OF_PARA (POS_NUM_OF_UPVS + sizeof(char))
#define POS_IS_VAR_ARG (POS_NUM_OF_PARA + sizeof(char))
#define POS_MAX_STACK_SIZE (POS_IS_VAR_ARG + sizeof(char))
#define POS_NUM_OF_INST (POS_MAX_STACK_SIZE +sizeof(char))
#define POS_BYTECODE (POS_NUM_OF_INST + sizeof(int))
#define MAX_BEGIN_CODE_SIZE \
(POS_BYTECODE + LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN \
+ sizeof(int) + sizeof(int))
#define MAX_END_CODE_SIZE (sizeof(int) + sizeof(int) + sizeof(int))
#endif /* OPENRESTY_LUAJIT */
/*
* taken from chaoslawful:
* Luajit bytecode format
* ---------------------
* | HEAD | Luajit bytecode head
* ---------------------
* | Internal | All internal functions
* | functions |
* ---------------------
* | ULEB128 | Rest data total length of this function
* | [Date len of | (not include itself)
* | this function] |
* ---------------------
* | Char | F(ffi) | V(vararg)| C(has internal funcs)
* | [func flag] |
* ---------------------
* | Char | Number of parameters of this function
* | [numparams] |
* ---------------------
* | Char |
* | [framesize] |
* ---------------------
* | Char | Number of upvalues referenced by this function
* | [sizeupvalues] |
* ---------------------
* | ULEB128 | Number of collectable constants referenced
* | [sizekgc] | by this function
* ---------------------
* | ULEB128 | Number of lua number constants referenced
* | [sizekn] | by this function
* ---------------------
* | ULEB128 | Number of bytecode instructions of this function
* | [sizebc]m1 | minus 1 to omit the BC_FUNCV/BC_FUNCF header bytecode
* ---------------------
* | ULEB128 |
* | [size of dbg | Size of debug lineinfo map, available when not stripped
* | lineinfo] |
* ---------------------
* | ULEB128 | Available when not stripped
* | [firstline] | The first line of this function's definition
* ---------------------
* | ULEB128 | Available when not stripped
* | [numline] | The number of lines of this function's definition
* ---------------------
* | [bytecode] | Bytecode instructions of this function
* ---------------------
* |[upvalue ref slots]| [sizeupvalues] * 2
* ---------------------
* | [collectable | [sizekgc] elems, variable length
* | constants] |
* ---------------------
* | [lua number | [sizekn] elems, variable length
* | constants] |
* ---------------------
* | [debug lineinfo | Length is the calculated size of debug lineinfo above
* | | Only available if not stripped
* ---------------------
* | Char |
* | [\x00] | Footer
* ---------------------
*/
/* bytecode for luajit 2.0 */
#ifndef OPENRESTY_LUAJIT
#define LJ20_LITTLE_ENDIAN_CODE_STRIPPED \
"\x14\x03\x00\x01\x00\x01\x00\x03" \
"\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \
"\x00\x00"
#define LJ20_BIG_ENDIAN_CODE_STRIPPED \
"\x14\x03\x00\x01\x00\x01\x00\x03" \
"\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \
"\x00\x00"
#define LJ20_LITTLE_ENDIAN_CODE \
"\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
"\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \
"\x00\x00"
#define LJ20_BIG_ENDIAN_CODE \
"\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
"\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \
"\x00\x00"
/* bytecode for luajit 2.1 */
#define LJ21_LITTLE_ENDIAN_CODE_STRIPPED \
"\x14\x03\x00\x01\x00\x01\x00\x03" \
"\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \
"\x00\x00"
#define LJ21_BIG_ENDIAN_CODE_STRIPPED \
"\x14\x03\x00\x01\x00\x01\x00\x03" \
"\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \
"\x00\x00"
#define LJ21_LITTLE_ENDIAN_CODE \
"\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
"\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \
"\x00\x00"
#define LJ21_BIG_ENDIAN_CODE \
"\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
"\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \
"\x00\x00"
#define LJ_CODE_LEN 23
#define LJ_CODE_LEN_STRIPPED 22
#define LJ_HEADERSIZE 5
#define LJ_BCDUMP_F_BE 0x01
#define LJ_BCDUMP_F_STRIP 0x02
#define LJ21_BCDUMP_VERSION 2
#define LJ20_BCDUMP_VERSION 1
#define LJ_SIGNATURE "\x1b\x4c\x4a"
#endif /* OPENRESTY_LUAJIT */
typedef enum {
NGX_LUA_TEXT_FILE,
NGX_LUA_BT_LUA,
NGX_LUA_BT_LJ,
} ngx_http_lua_clfactory_file_type_e;
enum {
NGX_LUA_READER_BUFSIZE = 4096,
};
typedef struct {
ngx_http_lua_clfactory_file_type_e file_type;
int extraline;
FILE *f;
#ifndef OPENRESTY_LUAJIT
int sent_begin;
int sent_end;
size_t begin_code_len;
size_t end_code_len;
size_t rest_len;
union {
char *ptr;
char str[MAX_BEGIN_CODE_SIZE];
} begin_code;
union {
char *ptr;
char str[MAX_END_CODE_SIZE];
} end_code;
#endif /* OPENRESTY_LUAJIT */
char buff[NGX_LUA_READER_BUFSIZE];
} ngx_http_lua_clfactory_file_ctx_t;
typedef struct {
#ifndef OPENRESTY_LUAJIT
int sent_begin;
int sent_end;
#endif
const char *s;
size_t size;
} ngx_http_lua_clfactory_buffer_ctx_t;
static const char *ngx_http_lua_clfactory_getF(lua_State *L, void *ud,
size_t *size);
static int ngx_http_lua_clfactory_errfile(lua_State *L, const char *what,
int fname_index);
static const char *ngx_http_lua_clfactory_getS(lua_State *L, void *ud,
size_t *size);
#ifndef OPENRESTY_LUAJIT
static long ngx_http_lua_clfactory_file_size(FILE *f);
#endif
#ifndef OPENRESTY_LUAJIT
int
ngx_http_lua_clfactory_bytecode_prepare(lua_State *L,
ngx_http_lua_clfactory_file_ctx_t *lf, int fname_index)
{
int x = 1, size_of_int, size_of_size_t, little_endian,
size_of_inst, version, stripped;
static int num_of_inst = 3, num_of_inter_func = 1;
const char *emsg, *serr, *bytecode;
size_t size, bytecode_len;
long fsize;
serr = NULL;
*lf->begin_code.str = LUA_SIGNATURE[0];
if (lf->file_type == NGX_LUA_BT_LJ) {
size = fread(lf->begin_code.str + 1, 1, LJ_HEADERSIZE - 1, lf->f);
if (size != LJ_HEADERSIZE - 1) {
serr = strerror(errno);
emsg = "cannot read header";
goto error;
}
version = *(lf->begin_code.str + 3);
dd("version: %d", (int) version);
if (ngx_memcmp(lf->begin_code.str, LJ_SIGNATURE,
sizeof(LJ_SIGNATURE) - 1))
{
emsg = "bad byte-code header";
goto error;
}
#if defined(DDEBUG) && (DDEBUG)
{
dd("==LJ_BT_HEADER==");
size_t i;
for (i = 0; i < LJ_HEADERSIZE; i++) {
dd("%ld: 0x%02X", i, (unsigned)(u_char) lf->begin_code.str[i]);
}
dd("==LJ_BT_HEADER_END==");
}
#endif
lf->begin_code_len = LJ_HEADERSIZE;
little_endian = !((*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_BE);
stripped = (*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_STRIP;
dd("stripped: %d", (int) stripped);
if (version == LJ21_BCDUMP_VERSION) {
if (stripped) {
if (little_endian) {
lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE_STRIPPED;
} else {
lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE_STRIPPED;
}
lf->end_code_len = LJ_CODE_LEN_STRIPPED;
} else {
if (little_endian) {
lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE;
} else {
lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE;
}
lf->end_code_len = LJ_CODE_LEN;
}
} else if (version == LJ20_BCDUMP_VERSION) {
if (stripped) {
if (little_endian) {
lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE_STRIPPED;
} else {
lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE_STRIPPED;
}
lf->end_code_len = LJ_CODE_LEN_STRIPPED;
} else {
if (little_endian) {
lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE;
} else {
lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE;
}
lf->end_code_len = LJ_CODE_LEN;
}
} else {
emsg = "bytecode format version unsupported";
goto error;
}
fsize = ngx_http_lua_clfactory_file_size(lf->f);
if (fsize < 0) {
serr = strerror(errno);
emsg = "cannot fseek/ftell";
goto error;
}
lf->rest_len = fsize - LJ_HEADERSIZE;
#if defined(DDEBUG) && (DDEBUG)
{
size_t i = 0;
dd("==LJ_END_CODE: %ld rest_len: %ld==", lf->end_code_len,
lf->rest_len);
for (i = 0; i < lf->end_code_len; i++) {
dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.ptr[i]));
}
dd("==LJ_END_CODE_END==");
}
#endif
} else {
size = fread(lf->begin_code.str + 1, 1, LUAC_HEADERSIZE - 1, lf->f);
if (size != LUAC_HEADERSIZE - 1) {
serr = strerror(errno);
emsg = "cannot read header";
goto error;
}
version = *(lf->begin_code.str + 4);
little_endian = *(lf->begin_code.str + 6);
size_of_int = *(lf->begin_code.str + 7);
size_of_size_t = *(lf->begin_code.str + 8);
size_of_inst = *(lf->begin_code.str + 9);
#if defined(DDEBUG) && (DDEBUG)
{
dd("==LUA_BT_HEADER==");
size_t i;
for (i = 0; i < LUAC_HEADERSIZE; i++) {
dd("%ld, 0x%02X", i, (unsigned)(u_char) lf->begin_code.str[i]);
}
dd("==LUA_BT_HEADER_END==");
}
#endif
if (ngx_memcmp(lf->begin_code.str, LUA_SIGNATURE,
sizeof(LUA_SIGNATURE) -1)
|| version != LUAC_VERSION
|| little_endian != (int) (*(char *) &x)
|| size_of_int != sizeof(int)
|| size_of_size_t != sizeof(size_t)
|| (size_of_inst != 4 && size_of_inst != 8))
{
emsg = "bad byte-code header";
goto error;
}
/* clear the following fields to zero:
* - source string length
* - start line
* - last line
*/
ngx_memzero(lf->begin_code.str + POS_SOURCE_STR_LEN,
sizeof(size_t) + sizeof(int) * 2);
/* number of upvalues */
*(lf->begin_code.str + POS_NUM_OF_UPVS) = 0;
/* number of parameters */
*(lf->begin_code.str + POS_NUM_OF_PARA) = 0;
/* is var-argument function? */
*(lf->begin_code.str + POS_IS_VAR_ARG) = 2;
/* max stack size */
*(lf->begin_code.str + POS_MAX_STACK_SIZE) = 2;
/* number of bytecode instructions */
ngx_memcpy(lf->begin_code.str + POS_NUM_OF_INST, &num_of_inst,
sizeof(int));
lf->begin_code_len = POS_BYTECODE;
if (little_endian) {
if (size_of_inst == 4) {
bytecode = LUA_LITTLE_ENDIAN_4BYTES_CODE;
bytecode_len = LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN;
} else {
bytecode = LUA_LITTLE_ENDIAN_8BYTES_CODE;
bytecode_len = LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN;
}
} else {
if (size_of_inst == 4) {
bytecode = LUA_BIG_ENDIAN_4BYTES_CODE;
bytecode_len = LUA_BIG_ENDIAN_4BYTES_CODE_LEN;
} else {
bytecode = LUA_BIG_ENDIAN_8BYTES_CODE;
bytecode_len = LUA_BIG_ENDIAN_8BYTES_CODE_LEN;
}
}
/* bytecode */
ngx_memcpy(lf->begin_code.str + POS_BYTECODE, bytecode, bytecode_len);
/* number of consts */
ngx_memzero(lf->begin_code.str + POS_BYTECODE + bytecode_len,
sizeof(int));
/* number of internal functions */
ngx_memcpy(lf->begin_code.str + POS_BYTECODE + bytecode_len
+ sizeof(int), &num_of_inter_func, sizeof(int));
lf->begin_code_len += bytecode_len + sizeof(int) + sizeof(int);
#if defined(DDEBUG) && (DDEBUG)
{
size_t i = 0;
dd("==LUA_BEGIN_CODE: %ld==", lf->begin_code_len);
for (i = 0; i < lf->begin_code_len; i++) {
dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->begin_code.str[i]));
}
dd("==LUA_BEGIN_CODE_END==");
}
#endif
/* clear the following fields to zero:
* - lineinfo vector size
* - number of local vars
* - number of upvalues
*/
ngx_memzero(lf->end_code.str, sizeof(int) * 3);
lf->end_code_len = sizeof(int) + sizeof(int) + sizeof(int);
#if defined(DDEBUG) && (DDEBUG)
{
size_t i = 0;
dd("==LUA_END_CODE: %ld==", lf->end_code_len);
for (i = 0; i < lf->end_code_len; i++) {
dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.str[i]));
}
dd("==LUA_END_CODE_END==");
}
#endif
}
return 0;
error:
fclose(lf->f); /* close file (even in case of errors) */
if (serr) {
lua_pushfstring(L, "%s: %s", emsg, serr);
} else {
lua_pushstring(L, emsg);
}
lua_remove(L, fname_index);
return LUA_ERRFILE;
}
#endif /* OPENRESTY_LUAJIT */
ngx_int_t
ngx_http_lua_clfactory_loadfile(lua_State *L, const char *filename)
{
int c, status, readstatus;
ngx_flag_t sharp;
ngx_http_lua_clfactory_file_ctx_t lf;
/* index of filename on the stack */
int fname_index;
sharp = 0;
fname_index = lua_gettop(L) + 1;
lf.extraline = 0;
lf.file_type = NGX_LUA_TEXT_FILE;
#ifndef OPENRESTY_LUAJIT
lf.begin_code.ptr = CLFACTORY_BEGIN_CODE;
lf.begin_code_len = CLFACTORY_BEGIN_SIZE;
lf.end_code.ptr = CLFACTORY_END_CODE;
lf.end_code_len = CLFACTORY_END_SIZE;
#endif
lua_pushfstring(L, "@%s", filename);
lf.f = fopen(filename, "r");
if (lf.f == NULL) {
return ngx_http_lua_clfactory_errfile(L, "open", fname_index);
}
c = getc(lf.f);
if (c == '#') { /* Unix exec. file? */
lf.extraline = 1;
while ((c = getc(lf.f)) != EOF && c != '\n') {
/* skip first line */
}
if (c == '\n') {
c = getc(lf.f);
}
sharp = 1;
}
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) {
return ngx_http_lua_clfactory_errfile(L, "reopen", fname_index);
}
/* check whether lib jit exists */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
lua_getfield(L, -1, "jit"); /* get _LOADED["jit"] */
if (lua_istable(L, -1)) {
lf.file_type = NGX_LUA_BT_LJ;
} else {
lf.file_type = NGX_LUA_BT_LUA;
}
lua_pop(L, 2);
/*
* Loading bytecode with an extra header is disabled for security
* reasons. This may circumvent the usual check for bytecode vs.
* Lua code by looking at the first char. Since this is a potential
* security violation no attempt is made to echo the chunkname either.
*/
if (lf.file_type == NGX_LUA_BT_LJ && sharp) {
if (filename) {
fclose(lf.f); /* close file (even in case of errors) */
}
filename = lua_tostring(L, fname_index) + 1;
lua_pushfstring(L, "bad byte-code header in %s", filename);
lua_remove(L, fname_index);
return LUA_ERRFILE;
}
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) {
/* skip eventual `#!...' */
}
#ifndef OPENRESTY_LUAJIT
status = ngx_http_lua_clfactory_bytecode_prepare(L, &lf, fname_index);
if (status != 0) {
return status;
}
#endif
lf.extraline = 0;
}
#ifndef OPENRESTY_LUAJIT
if (lf.file_type == NGX_LUA_TEXT_FILE) {
ungetc(c, lf.f);
}
lf.sent_begin = lf.sent_end = 0;
#else
ungetc(c, lf.f);
#endif
status = lua_load(L, ngx_http_lua_clfactory_getF, &lf,
lua_tostring(L, -1));
readstatus = ferror(lf.f);
if (filename) {
fclose(lf.f); /* close file (even in case of errors) */
}
if (readstatus) {
lua_settop(L, fname_index); /* ignore results from `lua_load' */
return ngx_http_lua_clfactory_errfile(L, "read", fname_index);
}
lua_remove(L, fname_index);
return status;
}
ngx_int_t
ngx_http_lua_clfactory_loadbuffer(lua_State *L, const char *buff,
size_t size, const char *name)
{
ngx_http_lua_clfactory_buffer_ctx_t ls;
ls.s = buff;
ls.size = size;
#ifndef OPENRESTY_LUAJIT
ls.sent_begin = 0;
ls.sent_end = 0;
#endif
return lua_load(L, ngx_http_lua_clfactory_getS, &ls, name);
}
static const char *
ngx_http_lua_clfactory_getF(lua_State *L, void *ud, size_t *size)
{
#ifndef OPENRESTY_LUAJIT
char *buf;
#endif
size_t num;
ngx_http_lua_clfactory_file_ctx_t *lf;
lf = (ngx_http_lua_clfactory_file_ctx_t *) ud;
if (lf->extraline) {
lf->extraline = 0;
*size = 1;
return "\n";
}
#ifndef OPENRESTY_LUAJIT
if (lf->sent_begin == 0) {
lf->sent_begin = 1;
*size = lf->begin_code_len;
if (lf->file_type == NGX_LUA_TEXT_FILE) {
buf = lf->begin_code.ptr;
} else {
buf = lf->begin_code.str;
}
return buf;
}
#endif /* OPENRESTY_LUAJIT */
num = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
dd("fread returned %d", (int) num);
if (num == 0) {
#ifndef OPENRESTY_LUAJIT
if (lf->sent_end == 0) {
lf->sent_end = 1;
*size = lf->end_code_len;
if (lf->file_type == NGX_LUA_BT_LUA) {
buf = lf->end_code.str;
} else {
buf = lf->end_code.ptr;
}
return buf;
}
#endif /* OPENRESTY_LUAJIT */
*size = 0;
return NULL;
}
#ifndef OPENRESTY_LUAJIT
if (lf->file_type == NGX_LUA_BT_LJ) {
/* skip the footer(\x00) in luajit */
lf->rest_len -= num;
if (lf->rest_len == 0) {
if (--num == 0 && lf->sent_end == 0) {
lf->sent_end = 1;
buf = lf->end_code.ptr;
*size = lf->end_code_len;
return buf;
}
}
}
#endif /* OPENRESTY_LUAJIT */
*size = num;
return lf->buff;
}
static int
ngx_http_lua_clfactory_errfile(lua_State *L, const char *what, int fname_index)
{
const char *serr;
const char *filename;
filename = lua_tostring(L, fname_index) + 1;
if (errno) {
serr = strerror(errno);
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
} else {
lua_pushfstring(L, "cannot %s %s", what, filename);
}
lua_remove(L, fname_index);
return LUA_ERRFILE;
}
static const char *
ngx_http_lua_clfactory_getS(lua_State *L, void *ud, size_t *size)
{
ngx_http_lua_clfactory_buffer_ctx_t *ls = ud;
#ifndef OPENRESTY_LUAJIT
if (ls->sent_begin == 0) {
ls->sent_begin = 1;
*size = CLFACTORY_BEGIN_SIZE;
return CLFACTORY_BEGIN_CODE;
}
#endif
if (ls->size == 0) {
#ifndef OPENRESTY_LUAJIT
if (ls->sent_end == 0) {
ls->sent_end = 1;
*size = CLFACTORY_END_SIZE;
return CLFACTORY_END_CODE;
}
#endif
return NULL;
}
*size = ls->size;
ls->size = 0;
return ls->s;
}
#ifndef OPENRESTY_LUAJIT
static long
ngx_http_lua_clfactory_file_size(FILE *f)
{
long cur_pos, len;
cur_pos = ftell(f);
if (cur_pos == -1) {
return -1;
}
if (fseek(f, 0, SEEK_END) != 0) {
return -1;
}
len = ftell(f);
if (len == -1) {
return -1;
}
if (fseek(f, cur_pos, SEEK_SET) != 0) {
return -1;
}
return len;
}
#endif /* OPENRESTY_LUAJIT */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_
#define _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_clfactory_loadfile(lua_State *L, const char *filename);
ngx_int_t ngx_http_lua_clfactory_loadbuffer(lua_State *L, const char *buff,
size_t size, const char *name);
#endif /* _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,700 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_COMMON_H_INCLUDED_
#define _NGX_HTTP_LUA_COMMON_H_INCLUDED_
#include "ngx_http_lua_autoconf.h"
#include <nginx.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_md5.h>
#include <setjmp.h>
#include <stdint.h>
#include <luajit.h>
#include <lualib.h>
#include <lauxlib.h>
#if defined(NDK) && NDK
#include <ndk.h>
typedef struct {
size_t size;
int ref;
u_char *key;
u_char *chunkname;
ngx_str_t script;
} ngx_http_lua_set_var_data_t;
#endif
#ifdef NGX_LUA_USE_ASSERT
#include <assert.h>
# define ngx_http_lua_assert(a) assert(a)
#else
# define ngx_http_lua_assert(a)
#endif
/**
* max positive +1.7976931348623158e+308
* min positive +2.2250738585072014e-308
*/
#ifndef NGX_DOUBLE_LEN
#define NGX_DOUBLE_LEN 25
#endif
#if (NGX_PCRE)
#include <pcre.h>
# if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21)
# define LUA_HAVE_PCRE_JIT 1
# else
# define LUA_HAVE_PCRE_JIT 0
# endif
#endif
#if (nginx_version < 1006000)
# error at least nginx 1.6.0 is required but found an older version
#endif
#if LUA_VERSION_NUM != 501
# error unsupported Lua language version
#endif
#if !defined(LUAJIT_VERSION_NUM) || (LUAJIT_VERSION_NUM < 20000)
# error unsupported LuaJIT version
#endif
#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)
# define NGX_HTTP_LUA_USE_OCSP 1
#endif
#ifndef NGX_HTTP_PERMANENT_REDIRECT
# define NGX_HTTP_PERMANENT_REDIRECT 308
#endif
#ifndef NGX_HAVE_SHA1
# if (nginx_version >= 1011002)
# define NGX_HAVE_SHA1 1
# endif
#endif
#ifndef MD5_DIGEST_LENGTH
# define MD5_DIGEST_LENGTH 16
#endif
#ifndef NGX_HTTP_LUA_MAX_ARGS
# define NGX_HTTP_LUA_MAX_ARGS 100
#endif
#ifndef NGX_HTTP_LUA_MAX_HEADERS
# define NGX_HTTP_LUA_MAX_HEADERS 100
#endif
/* Nginx HTTP Lua Inline tag prefix */
#define NGX_HTTP_LUA_INLINE_TAG "nhli_"
#define NGX_HTTP_LUA_INLINE_TAG_LEN \
(sizeof(NGX_HTTP_LUA_INLINE_TAG) - 1)
#define NGX_HTTP_LUA_INLINE_KEY_LEN \
(NGX_HTTP_LUA_INLINE_TAG_LEN + 2 * MD5_DIGEST_LENGTH)
/* Nginx HTTP Lua File tag prefix */
#define NGX_HTTP_LUA_FILE_TAG "nhlf_"
#define NGX_HTTP_LUA_FILE_TAG_LEN \
(sizeof(NGX_HTTP_LUA_FILE_TAG) - 1)
#define NGX_HTTP_LUA_FILE_KEY_LEN \
(NGX_HTTP_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH)
/* must be within 16 bit */
#define NGX_HTTP_LUA_CONTEXT_SET 0x0001
#define NGX_HTTP_LUA_CONTEXT_REWRITE 0x0002
#define NGX_HTTP_LUA_CONTEXT_ACCESS 0x0004
#define NGX_HTTP_LUA_CONTEXT_CONTENT 0x0008
#define NGX_HTTP_LUA_CONTEXT_LOG 0x0010
#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x0020
#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x0040
#define NGX_HTTP_LUA_CONTEXT_TIMER 0x0080
#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER 0x0100
#define NGX_HTTP_LUA_CONTEXT_BALANCER 0x0200
#define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x0400
#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x0800
#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000
#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x2000
#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x4000
#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE 0x8000
#define NGX_HTTP_LUA_FFI_NO_REQ_CTX -100
#define NGX_HTTP_LUA_FFI_BAD_CONTEXT -101
#if (NGX_PTR_SIZE >= 8 && !defined(_WIN64))
# define ngx_http_lua_lightudata_mask(ludata) \
((void *) ((uintptr_t) (&ngx_http_lua_##ludata) & ((1UL << 47) - 1)))
#else
# define ngx_http_lua_lightudata_mask(ludata) \
(&ngx_http_lua_##ludata)
#endif
typedef struct ngx_http_lua_co_ctx_s ngx_http_lua_co_ctx_t;
typedef struct ngx_http_lua_sema_mm_s ngx_http_lua_sema_mm_t;
typedef union ngx_http_lua_srv_conf_u ngx_http_lua_srv_conf_t;
typedef struct ngx_http_lua_main_conf_s ngx_http_lua_main_conf_t;
typedef struct ngx_http_lua_header_val_s ngx_http_lua_header_val_t;
typedef struct ngx_http_lua_posted_thread_s ngx_http_lua_posted_thread_t;
typedef struct ngx_http_lua_balancer_peer_data_s
ngx_http_lua_balancer_peer_data_t;
typedef ngx_int_t (*ngx_http_lua_main_conf_handler_pt)(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L);
typedef ngx_int_t (*ngx_http_lua_srv_conf_handler_pt)(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L);
typedef ngx_int_t (*ngx_http_lua_set_header_pt)(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
typedef struct {
u_char *package;
lua_CFunction loader;
} ngx_http_lua_preload_hook_t;
typedef struct {
int ref;
lua_State *co;
ngx_queue_t queue;
} ngx_http_lua_thread_ref_t;
struct ngx_http_lua_main_conf_s {
lua_State *lua;
ngx_pool_cleanup_t *vm_cleanup;
ngx_str_t lua_path;
ngx_str_t lua_cpath;
ngx_cycle_t *cycle;
ngx_pool_t *pool;
ngx_int_t max_pending_timers;
ngx_int_t pending_timers;
ngx_int_t max_running_timers;
ngx_int_t running_timers;
ngx_connection_t *watcher; /* for watching the process exit event */
ngx_int_t lua_thread_cache_max_entries;
ngx_hash_t builtin_headers_out;
#if (NGX_PCRE)
ngx_int_t regex_cache_entries;
ngx_int_t regex_cache_max_entries;
ngx_int_t regex_match_limit;
# if (LUA_HAVE_PCRE_JIT)
pcre_jit_stack *jit_stack;
# endif
#endif
ngx_array_t *shm_zones; /* of ngx_shm_zone_t* */
ngx_array_t *shdict_zones; /* shm zones of "shdict" */
ngx_array_t *preload_hooks; /* of ngx_http_lua_preload_hook_t */
ngx_flag_t postponed_to_rewrite_phase_end;
ngx_flag_t postponed_to_access_phase_end;
ngx_http_lua_main_conf_handler_pt init_handler;
ngx_str_t init_src;
u_char *init_chunkname;
ngx_http_lua_main_conf_handler_pt init_worker_handler;
ngx_str_t init_worker_src;
u_char *init_worker_chunkname;
ngx_http_lua_main_conf_handler_pt exit_worker_handler;
ngx_str_t exit_worker_src;
u_char *exit_worker_chunkname;
ngx_http_lua_balancer_peer_data_t *balancer_peer_data;
/* neither yielding nor recursion is possible in
* balancer_by_lua*, so there cannot be any races among
* concurrent requests and it is safe to store the peer
* data pointer in the main conf.
*/
ngx_chain_t *body_filter_chain;
/* neither yielding nor recursion is possible in
* body_filter_by_lua*, so there cannot be any races among
* concurrent requests when storing the chain
* data pointer in the main conf.
*/
ngx_http_variable_value_t *setby_args;
/* neither yielding nor recursion is possible in
* set_by_lua*, so there cannot be any races among
* concurrent requests when storing the args pointer
* in the main conf.
*/
size_t setby_nargs;
/* neither yielding nor recursion is possible in
* set_by_lua*, so there cannot be any races among
* concurrent requests when storing the nargs in the
* main conf.
*/
ngx_uint_t shm_zones_inited;
ngx_http_lua_sema_mm_t *sema_mm;
ngx_uint_t malloc_trim_cycle; /* a cycle is defined as the number
of requests */
ngx_uint_t malloc_trim_req_count;
ngx_uint_t directive_line;
#if (nginx_version >= 1011011)
/* the following 2 fields are only used by ngx.req.raw_headers() for now */
ngx_buf_t **busy_buf_ptrs;
ngx_int_t busy_buf_ptr_count;
#endif
ngx_int_t host_var_index;
ngx_flag_t set_sa_restart;
ngx_queue_t free_lua_threads; /* of ngx_http_lua_thread_ref_t */
ngx_queue_t cached_lua_threads; /* of ngx_http_lua_thread_ref_t */
ngx_uint_t worker_thread_vm_pool_size;
unsigned requires_header_filter:1;
unsigned requires_body_filter:1;
unsigned requires_capture_filter:1;
unsigned requires_rewrite:1;
unsigned requires_access:1;
unsigned requires_log:1;
unsigned requires_shm:1;
unsigned requires_capture_log:1;
unsigned requires_server_rewrite:1;
};
union ngx_http_lua_srv_conf_u {
struct {
#if (NGX_HTTP_SSL)
ngx_http_lua_srv_conf_handler_pt ssl_cert_handler;
ngx_str_t ssl_cert_src;
u_char *ssl_cert_src_key;
u_char *ssl_cert_chunkname;
int ssl_cert_src_ref;
ngx_http_lua_srv_conf_handler_pt ssl_sess_store_handler;
ngx_str_t ssl_sess_store_src;
u_char *ssl_sess_store_src_key;
u_char *ssl_sess_store_chunkname;
int ssl_sess_store_src_ref;
ngx_http_lua_srv_conf_handler_pt ssl_sess_fetch_handler;
ngx_str_t ssl_sess_fetch_src;
u_char *ssl_sess_fetch_src_key;
u_char *ssl_sess_fetch_chunkname;
int ssl_sess_fetch_src_ref;
ngx_http_lua_srv_conf_handler_pt ssl_client_hello_handler;
ngx_str_t ssl_client_hello_src;
u_char *ssl_client_hello_src_key;
u_char *ssl_client_hello_chunkname;
int ssl_client_hello_src_ref;
#endif
ngx_http_lua_srv_conf_handler_pt server_rewrite_handler;
ngx_http_complex_value_t server_rewrite_src;
u_char *server_rewrite_src_key;
u_char *server_rewrite_chunkname;
int server_rewrite_src_ref;
} srv;
struct {
ngx_http_lua_srv_conf_handler_pt handler;
ngx_str_t src;
u_char *src_key;
u_char *chunkname;
int src_ref;
} balancer;
};
typedef struct {
#if (NGX_HTTP_SSL)
ngx_ssl_t *ssl; /* shared by SSL cosockets */
ngx_uint_t ssl_protocols;
ngx_str_t ssl_ciphers;
ngx_uint_t ssl_verify_depth;
ngx_str_t ssl_trusted_certificate;
ngx_str_t ssl_crl;
#if (nginx_version >= 1019004)
ngx_array_t *ssl_conf_commands;
#endif
#endif
ngx_flag_t force_read_body; /* whether force request body to
be read */
ngx_flag_t enable_code_cache; /* whether to enable
code cache */
ngx_flag_t http10_buffering;
ngx_http_handler_pt rewrite_handler;
ngx_http_handler_pt access_handler;
ngx_http_handler_pt content_handler;
ngx_http_handler_pt log_handler;
ngx_http_handler_pt header_filter_handler;
ngx_http_output_body_filter_pt body_filter_handler;
u_char *rewrite_chunkname;
ngx_http_complex_value_t rewrite_src; /* rewrite_by_lua
inline script/script
file path */
u_char *rewrite_src_key; /* cached key for rewrite_src */
int rewrite_src_ref;
u_char *access_chunkname;
ngx_http_complex_value_t access_src; /* access_by_lua
inline script/script
file path */
u_char *access_src_key; /* cached key for access_src */
int access_src_ref;
u_char *content_chunkname;
ngx_http_complex_value_t content_src; /* content_by_lua
inline script/script
file path */
u_char *content_src_key; /* cached key for content_src */
int content_src_ref;
u_char *log_chunkname;
ngx_http_complex_value_t log_src; /* log_by_lua inline script/script
file path */
u_char *log_src_key; /* cached key for log_src */
int log_src_ref;
ngx_http_complex_value_t header_filter_src; /* header_filter_by_lua
inline script/script
file path */
u_char *header_filter_chunkname;
u_char *header_filter_src_key;
/* cached key for header_filter_src */
int header_filter_src_ref;
ngx_http_complex_value_t body_filter_src;
u_char *body_filter_src_key;
u_char *body_filter_chunkname;
int body_filter_src_ref;
ngx_msec_t keepalive_timeout;
ngx_msec_t connect_timeout;
ngx_msec_t send_timeout;
ngx_msec_t read_timeout;
size_t send_lowat;
size_t buffer_size;
ngx_uint_t pool_size;
ngx_flag_t transform_underscores_in_resp_headers;
ngx_flag_t log_socket_errors;
ngx_flag_t check_client_abort;
ngx_flag_t use_default_type;
} ngx_http_lua_loc_conf_t;
typedef enum {
NGX_HTTP_LUA_USER_CORO_NOP = 0,
NGX_HTTP_LUA_USER_CORO_RESUME = 1,
NGX_HTTP_LUA_USER_CORO_YIELD = 2,
NGX_HTTP_LUA_USER_THREAD_RESUME = 3,
} ngx_http_lua_user_coro_op_t;
typedef enum {
NGX_HTTP_LUA_CO_RUNNING = 0, /* coroutine running */
NGX_HTTP_LUA_CO_SUSPENDED = 1, /* coroutine suspended */
NGX_HTTP_LUA_CO_NORMAL = 2, /* coroutine normal */
NGX_HTTP_LUA_CO_DEAD = 3, /* coroutine dead */
NGX_HTTP_LUA_CO_ZOMBIE = 4, /* coroutine zombie */
} ngx_http_lua_co_status_t;
struct ngx_http_lua_posted_thread_s {
ngx_http_lua_co_ctx_t *co_ctx;
ngx_http_lua_posted_thread_t *next;
};
struct ngx_http_lua_co_ctx_s {
void *data; /* user state for cosockets */
lua_State *co;
ngx_http_lua_co_ctx_t *parent_co_ctx;
ngx_http_lua_posted_thread_t *zombie_child_threads;
ngx_http_lua_posted_thread_t **next_zombie_child_thread;
ngx_http_cleanup_pt cleanup;
ngx_int_t *sr_statuses; /* all capture subrequest statuses */
ngx_http_headers_out_t **sr_headers;
ngx_str_t *sr_bodies; /* all captured subrequest bodies */
uint8_t *sr_flags;
unsigned nresults_from_worker_thread; /* number of results
* from worker
* thread callback */
unsigned nrets; /* ngx_http_lua_run_thread nrets arg. */
unsigned nsubreqs; /* number of subrequests of the
* current request */
unsigned pending_subreqs; /* number of subrequests being
waited */
ngx_event_t sleep; /* used for ngx.sleep */
ngx_queue_t sem_wait_queue;
#ifdef NGX_LUA_USE_ASSERT
int co_top; /* stack top after yielding/creation,
only for sanity checks */
#endif
int co_ref; /* reference to anchor the thread
coroutines (entry coroutine and user
threads) in the Lua registry,
preventing the thread coroutine
from beging collected by the
Lua GC */
unsigned waited_by_parent:1; /* whether being waited by
a parent coroutine */
unsigned co_status:3; /* the current coroutine's status */
unsigned flushing:1; /* indicates whether the current
coroutine is waiting for
ngx.flush(true) */
unsigned is_uthread:1; /* whether the current coroutine is
a user thread */
unsigned thread_spawn_yielded:1; /* yielded from
the ngx.thread.spawn()
call */
unsigned sem_resume_status:1;
unsigned is_wrap:1; /* set when creating coroutines via
coroutine.wrap */
unsigned propagate_error:1; /* set when propagating an error
from a coroutine to its
parent */
};
typedef struct {
lua_State *vm;
ngx_int_t count;
} ngx_http_lua_vm_state_t;
typedef struct ngx_http_lua_ctx_s {
/* for lua_code_cache off: */
ngx_http_lua_vm_state_t *vm_state;
ngx_http_request_t *request;
ngx_http_handler_pt resume_handler;
ngx_http_lua_co_ctx_t *cur_co_ctx; /* co ctx for the current coroutine */
/* FIXME: we should use rbtree here to prevent O(n) lookup overhead */
ngx_list_t *user_co_ctx; /* coroutine contexts for user
coroutines */
ngx_http_lua_co_ctx_t entry_co_ctx; /* coroutine context for the
entry coroutine */
ngx_http_lua_co_ctx_t *on_abort_co_ctx; /* coroutine context for the
on_abort thread */
int ctx_ref; /* reference to anchor
request ctx data in lua
registry */
unsigned flushing_coros; /* number of coroutines waiting on
ngx.flush(true) */
ngx_chain_t *out; /* buffered output chain for HTTP 1.0 */
ngx_chain_t *free_bufs;
ngx_chain_t *busy_bufs;
ngx_chain_t *free_recv_bufs;
ngx_chain_t *filter_in_bufs; /* for the body filter */
ngx_chain_t *filter_busy_bufs; /* for the body filter */
ngx_pool_cleanup_pt *cleanup;
ngx_http_cleanup_t *free_cleanup; /* free list of cleanup records */
ngx_chain_t *body; /* buffered subrequest response body
chains */
ngx_chain_t **last_body; /* for the "body" field */
ngx_str_t exec_uri;
ngx_str_t exec_args;
ngx_int_t exit_code;
void *downstream; /* can be either
ngx_http_lua_socket_tcp_upstream_t
or ngx_http_lua_co_ctx_t */
ngx_uint_t index; /* index of the current
subrequest in its parent
request */
ngx_http_lua_posted_thread_t *posted_threads;
int uthreads; /* number of active user threads */
uint16_t context; /* the current running directive context
(or running phase) for the current
Lua chunk */
unsigned run_post_subrequest:1; /* whether it has run
post_subrequest
(for subrequests only) */
unsigned waiting_more_body:1; /* 1: waiting for more
request body data;
0: no need to wait */
unsigned co_op:2; /* coroutine API operation */
unsigned exited:1;
unsigned eof:1; /* 1: last_buf has been sent;
0: last_buf not sent yet */
unsigned capture:1; /* 1: response body of current request
is to be captured by the lua
capture filter,
0: not to be captured */
unsigned read_body_done:1; /* 1: request body has been all
read; 0: body has not been
all read */
unsigned headers_set:1; /* whether the user has set custom
response headers */
unsigned mime_set:1; /* whether the user has set Content-Type
response header */
unsigned entered_server_rewrite_phase:1;
unsigned entered_rewrite_phase:1;
unsigned entered_access_phase:1;
unsigned entered_content_phase:1;
unsigned buffering:1; /* HTTP 1.0 response body buffering flag */
unsigned no_abort:1; /* prohibit "world abortion" via ngx.exit()
and etc */
unsigned header_sent:1; /* r->header_sent is not sufficient for
* this because special header filters
* like ngx_image_filter may intercept
* the header. so we should always test
* both flags. see the test case in
* t/020-subrequest.t */
unsigned seen_last_in_filter:1; /* used by body_filter_by_lua* */
unsigned seen_last_for_subreq:1; /* used by body capture filter */
unsigned writing_raw_req_socket:1; /* used by raw downstream
socket */
unsigned acquired_raw_req_socket:1; /* whether a raw req socket
is acquired */
unsigned seen_body_data:1;
} ngx_http_lua_ctx_t;
struct ngx_http_lua_header_val_s {
ngx_http_complex_value_t value;
ngx_uint_t hash;
ngx_str_t key;
ngx_http_lua_set_header_pt handler;
ngx_uint_t offset;
unsigned no_override;
};
typedef struct {
ngx_str_t name;
ngx_uint_t offset;
ngx_http_lua_set_header_pt handler;
} ngx_http_lua_set_header_t;
extern ngx_module_t ngx_http_lua_module;
extern ngx_http_output_header_filter_pt ngx_http_lua_next_header_filter;
extern ngx_http_output_body_filter_pt ngx_http_lua_next_body_filter;
#endif /* _NGX_HTTP_LUA_COMMON_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_config.h"
#include "api/ngx_http_lua_api.h"
static int ngx_http_lua_config_prefix(lua_State *L);
static int ngx_http_lua_config_configure(lua_State *L);
void
ngx_http_lua_inject_config_api(lua_State *L)
{
/* ngx.config */
lua_createtable(L, 0, 6 /* nrec */); /* .config */
#if (NGX_DEBUG)
lua_pushboolean(L, 1);
#else
lua_pushboolean(L, 0);
#endif
lua_setfield(L, -2, "debug");
lua_pushcfunction(L, ngx_http_lua_config_prefix);
lua_setfield(L, -2, "prefix");
lua_pushinteger(L, nginx_version);
lua_setfield(L, -2, "nginx_version");
lua_pushinteger(L, ngx_http_lua_version);
lua_setfield(L, -2, "ngx_lua_version");
lua_pushcfunction(L, ngx_http_lua_config_configure);
lua_setfield(L, -2, "nginx_configure");
lua_pushliteral(L, "http");
lua_setfield(L, -2, "subsystem");
lua_setfield(L, -2, "config");
}
static int
ngx_http_lua_config_prefix(lua_State *L)
{
lua_pushlstring(L, (char *) ngx_cycle->prefix.data,
ngx_cycle->prefix.len);
return 1;
}
static int
ngx_http_lua_config_configure(lua_State *L)
{
lua_pushliteral(L, NGX_CONFIGURE);
return 1;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CONFIG_H_INCLUDED_
#define _NGX_HTTP_LUA_CONFIG_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_config_api(lua_State *L);
#endif /* _NGX_HTTP_LUA_CONFIG_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,204 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_consts.h"
void
ngx_http_lua_inject_core_consts(lua_State *L)
{
/* {{{ core constants */
lua_pushinteger(L, NGX_OK);
lua_setfield(L, -2, "OK");
lua_pushinteger(L, NGX_AGAIN);
lua_setfield(L, -2, "AGAIN");
lua_pushinteger(L, NGX_DONE);
lua_setfield(L, -2, "DONE");
lua_pushinteger(L, NGX_DECLINED);
lua_setfield(L, -2, "DECLINED");
lua_pushinteger(L, NGX_ERROR);
lua_setfield(L, -2, "ERROR");
lua_pushlightuserdata(L, NULL);
lua_setfield(L, -2, "null");
/* }}} */
}
void
ngx_http_lua_inject_http_consts(lua_State *L)
{
/* {{{ HTTP status constants */
lua_pushinteger(L, NGX_HTTP_GET);
lua_setfield(L, -2, "HTTP_GET");
lua_pushinteger(L, NGX_HTTP_POST);
lua_setfield(L, -2, "HTTP_POST");
lua_pushinteger(L, NGX_HTTP_PUT);
lua_setfield(L, -2, "HTTP_PUT");
lua_pushinteger(L, NGX_HTTP_HEAD);
lua_setfield(L, -2, "HTTP_HEAD");
lua_pushinteger(L, NGX_HTTP_DELETE);
lua_setfield(L, -2, "HTTP_DELETE");
lua_pushinteger(L, NGX_HTTP_OPTIONS);
lua_setfield(L, -2, "HTTP_OPTIONS");
lua_pushinteger(L, NGX_HTTP_MKCOL);
lua_setfield(L, -2, "HTTP_MKCOL");
lua_pushinteger(L, NGX_HTTP_COPY);
lua_setfield(L, -2, "HTTP_COPY");
lua_pushinteger(L, NGX_HTTP_MOVE);
lua_setfield(L, -2, "HTTP_MOVE");
lua_pushinteger(L, NGX_HTTP_PROPFIND);
lua_setfield(L, -2, "HTTP_PROPFIND");
lua_pushinteger(L, NGX_HTTP_PROPPATCH);
lua_setfield(L, -2, "HTTP_PROPPATCH");
lua_pushinteger(L, NGX_HTTP_LOCK);
lua_setfield(L, -2, "HTTP_LOCK");
lua_pushinteger(L, NGX_HTTP_UNLOCK);
lua_setfield(L, -2, "HTTP_UNLOCK");
lua_pushinteger(L, NGX_HTTP_PATCH);
lua_setfield(L, -2, "HTTP_PATCH");
lua_pushinteger(L, NGX_HTTP_TRACE);
lua_setfield(L, -2, "HTTP_TRACE");
/* }}} */
lua_pushinteger(L, NGX_HTTP_CONTINUE);
lua_setfield(L, -2, "HTTP_CONTINUE");
lua_pushinteger(L, NGX_HTTP_SWITCHING_PROTOCOLS);
lua_setfield(L, -2, "HTTP_SWITCHING_PROTOCOLS");
lua_pushinteger(L, NGX_HTTP_OK);
lua_setfield(L, -2, "HTTP_OK");
lua_pushinteger(L, NGX_HTTP_CREATED);
lua_setfield(L, -2, "HTTP_CREATED");
lua_pushinteger(L, NGX_HTTP_ACCEPTED);
lua_setfield(L, -2, "HTTP_ACCEPTED");
lua_pushinteger(L, NGX_HTTP_NO_CONTENT);
lua_setfield(L, -2, "HTTP_NO_CONTENT");
lua_pushinteger(L, NGX_HTTP_PARTIAL_CONTENT);
lua_setfield(L, -2, "HTTP_PARTIAL_CONTENT");
lua_pushinteger(L, NGX_HTTP_SPECIAL_RESPONSE);
lua_setfield(L, -2, "HTTP_SPECIAL_RESPONSE");
lua_pushinteger(L, NGX_HTTP_MOVED_PERMANENTLY);
lua_setfield(L, -2, "HTTP_MOVED_PERMANENTLY");
lua_pushinteger(L, NGX_HTTP_MOVED_TEMPORARILY);
lua_setfield(L, -2, "HTTP_MOVED_TEMPORARILY");
lua_pushinteger(L, NGX_HTTP_SEE_OTHER);
lua_setfield(L, -2, "HTTP_SEE_OTHER");
lua_pushinteger(L, NGX_HTTP_PERMANENT_REDIRECT);
lua_setfield(L, -2, "HTTP_PERMANENT_REDIRECT");
lua_pushinteger(L, NGX_HTTP_NOT_MODIFIED);
lua_setfield(L, -2, "HTTP_NOT_MODIFIED");
lua_pushinteger(L, NGX_HTTP_TEMPORARY_REDIRECT);
lua_setfield(L, -2, "HTTP_TEMPORARY_REDIRECT");
lua_pushinteger(L, NGX_HTTP_BAD_REQUEST);
lua_setfield(L, -2, "HTTP_BAD_REQUEST");
lua_pushinteger(L, NGX_HTTP_UNAUTHORIZED);
lua_setfield(L, -2, "HTTP_UNAUTHORIZED");
lua_pushinteger(L, 402);
lua_setfield(L, -2, "HTTP_PAYMENT_REQUIRED");
lua_pushinteger(L, NGX_HTTP_FORBIDDEN);
lua_setfield(L, -2, "HTTP_FORBIDDEN");
lua_pushinteger(L, NGX_HTTP_NOT_FOUND);
lua_setfield(L, -2, "HTTP_NOT_FOUND");
lua_pushinteger(L, NGX_HTTP_NOT_ALLOWED);
lua_setfield(L, -2, "HTTP_NOT_ALLOWED");
lua_pushinteger(L, 406);
lua_setfield(L, -2, "HTTP_NOT_ACCEPTABLE");
lua_pushinteger(L, NGX_HTTP_REQUEST_TIME_OUT);
lua_setfield(L, -2, "HTTP_REQUEST_TIMEOUT");
lua_pushinteger(L, NGX_HTTP_CONFLICT);
lua_setfield(L, -2, "HTTP_CONFLICT");
lua_pushinteger(L, 410);
lua_setfield(L, -2, "HTTP_GONE");
lua_pushinteger(L, 426);
lua_setfield(L, -2, "HTTP_UPGRADE_REQUIRED");
lua_pushinteger(L, 429);
lua_setfield(L, -2, "HTTP_TOO_MANY_REQUESTS");
lua_pushinteger(L, 451);
lua_setfield(L, -2, "HTTP_ILLEGAL");
lua_pushinteger(L, NGX_HTTP_CLOSE);
lua_setfield(L, -2, "HTTP_CLOSE");
lua_pushinteger(L, NGX_HTTP_INTERNAL_SERVER_ERROR);
lua_setfield(L, -2, "HTTP_INTERNAL_SERVER_ERROR");
lua_pushinteger(L, NGX_HTTP_NOT_IMPLEMENTED);
lua_setfield(L, -2, "HTTP_NOT_IMPLEMENTED");
/* keep for backward compatibility */
lua_pushinteger(L, NGX_HTTP_NOT_IMPLEMENTED);
lua_setfield(L, -2, "HTTP_METHOD_NOT_IMPLEMENTED");
lua_pushinteger(L, NGX_HTTP_BAD_GATEWAY);
lua_setfield(L, -2, "HTTP_BAD_GATEWAY");
lua_pushinteger(L, NGX_HTTP_SERVICE_UNAVAILABLE);
lua_setfield(L, -2, "HTTP_SERVICE_UNAVAILABLE");
lua_pushinteger(L, NGX_HTTP_GATEWAY_TIME_OUT);
lua_setfield(L, -2, "HTTP_GATEWAY_TIMEOUT");
lua_pushinteger(L, 505);
lua_setfield(L, -2, "HTTP_VERSION_NOT_SUPPORTED");
lua_pushinteger(L, NGX_HTTP_INSUFFICIENT_STORAGE);
lua_setfield(L, -2, "HTTP_INSUFFICIENT_STORAGE");
/* }}} */
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CONSTS_H_INCLUDED_
#define _NGX_HTTP_LUA_CONSTS_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_http_consts(lua_State *L);
void ngx_http_lua_inject_core_consts(lua_State *L);
#endif /* _NGX_HTTP_LUA_CONSTS_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,389 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_contentby.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_cache.h"
#include "ngx_http_lua_probe.h"
static void ngx_http_lua_content_phase_post_read(ngx_http_request_t *r);
ngx_int_t
ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r)
{
int co_ref;
ngx_int_t rc;
lua_State *co;
ngx_event_t *rev;
ngx_http_lua_ctx_t *ctx;
ngx_pool_cleanup_t *cln;
ngx_http_lua_loc_conf_t *llcf;
dd("content by chunk");
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
} else {
dd("reset ctx");
ngx_http_lua_reset_ctx(r, L, ctx);
}
ctx->entered_content_phase = 1;
/* {{{ new coroutine to handle request */
co = ngx_http_lua_new_thread(r, L, &co_ref);
if (co == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"lua: failed to create new coroutine to handle request");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* move code closure to new coroutine */
lua_xmove(L, co, 1);
#ifndef OPENRESTY_LUAJIT
/* set closure's env table to new coroutine's globals table */
ngx_http_lua_get_globals_table(co);
lua_setfenv(co, -2);
#endif
/* save nginx request in coroutine globals table */
ngx_http_lua_set_req(co, r);
ctx->cur_co_ctx = &ctx->entry_co_ctx;
ctx->cur_co_ctx->co = co;
ctx->cur_co_ctx->co_ref = co_ref;
#ifdef NGX_LUA_USE_ASSERT
ctx->cur_co_ctx->co_top = 1;
#endif
ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
/* {{{ register request cleanup hooks */
if (ctx->cleanup == NULL) {
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cln->handler = ngx_http_lua_request_cleanup_handler;
cln->data = ctx;
ctx->cleanup = &cln->handler;
}
/* }}} */
ctx->context = NGX_HTTP_LUA_CONTEXT_CONTENT;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->check_client_abort) {
r->read_event_handler = ngx_http_lua_rd_check_broken_connection;
#if (NGX_HTTP_V2)
if (!r->stream) {
#endif
rev = r->connection->read;
if (!rev->active) {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
return NGX_ERROR;
}
}
#if (NGX_HTTP_V2)
}
#endif
} else {
r->read_event_handler = ngx_http_block_reading;
}
rc = ngx_http_lua_run_thread(L, r, ctx, 0);
if (rc == NGX_ERROR || rc >= NGX_OK) {
return rc;
}
if (rc == NGX_AGAIN) {
return ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);
}
if (rc == NGX_DONE) {
return ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);
}
return NGX_OK;
}
void
ngx_http_lua_content_wev_handler(ngx_http_request_t *r)
{
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return;
}
(void) ctx->resume_handler(r);
}
ngx_int_t
ngx_http_lua_content_handler(ngx_http_request_t *r)
{
ngx_http_lua_loc_conf_t *llcf;
ngx_http_lua_ctx_t *ctx;
ngx_int_t rc;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua content handler, uri:\"%V\" c:%ud", &r->uri,
r->main->count);
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->content_handler == NULL) {
dd("no content handler found");
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
dd("entered? %d", (int) ctx->entered_content_phase);
if (ctx->waiting_more_body) {
return NGX_DONE;
}
if (ctx->entered_content_phase) {
dd("calling wev handler");
rc = ctx->resume_handler(r);
dd("wev handler returns %d", (int) rc);
return rc;
}
if (llcf->force_read_body && !ctx->read_body_done) {
r->request_body_in_single_buf = 1;
r->request_body_in_persistent_file = 1;
r->request_body_in_clean_file = 1;
rc = ngx_http_read_client_request_body(r,
ngx_http_lua_content_phase_post_read);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
if (rc == NGX_AGAIN) {
ctx->waiting_more_body = 1;
return NGX_DONE;
}
}
dd("setting entered");
ctx->entered_content_phase = 1;
dd("calling content handler");
return llcf->content_handler(r);
}
/* post read callback for the content phase */
static void
ngx_http_lua_content_phase_post_read(ngx_http_request_t *r)
{
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
ctx->read_body_done = 1;
if (ctx->waiting_more_body) {
ctx->waiting_more_body = 0;
ngx_http_lua_finalize_request(r, ngx_http_lua_content_handler(r));
} else {
r->main->count--;
}
}
ngx_int_t
ngx_http_lua_content_handler_file(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
u_char *script_path;
ngx_http_lua_loc_conf_t *llcf;
ngx_str_t eval_src;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (ngx_http_complex_value(r, &llcf->content_src, &eval_src) != NGX_OK) {
return NGX_ERROR;
}
script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
eval_src.len);
if (script_path == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
&llcf->content_src_ref,
llcf->content_src_key);
if (rc != NGX_OK) {
if (rc < NGX_HTTP_SPECIAL_RESPONSE) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return rc;
}
/* make sure we have a valid code chunk */
ngx_http_lua_assert(lua_isfunction(L, -1));
return ngx_http_lua_content_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_content_handler_inline(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
ngx_http_lua_loc_conf_t *llcf;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
llcf->content_src.value.data,
llcf->content_src.value.len,
&llcf->content_src_ref,
llcf->content_src_key,
(const char *)
llcf->content_chunkname);
if (rc != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return ngx_http_lua_content_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_content_run_posted_threads(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, int n)
{
ngx_int_t rc;
ngx_http_lua_posted_thread_t *pt;
dd("run posted threads: %p", ctx->posted_threads);
for ( ;; ) {
pt = ctx->posted_threads;
if (pt == NULL) {
goto done;
}
ctx->posted_threads = pt->next;
ngx_http_lua_probe_run_posted_thread(r, pt->co_ctx->co,
(int) pt->co_ctx->co_status);
dd("posted thread status: %d", pt->co_ctx->co_status);
if (pt->co_ctx->co_status != NGX_HTTP_LUA_CO_RUNNING) {
continue;
}
ctx->cur_co_ctx = pt->co_ctx;
rc = ngx_http_lua_run_thread(L, r, ctx, 0);
if (rc == NGX_AGAIN) {
continue;
}
if (rc == NGX_DONE) {
n++;
continue;
}
if (rc == NGX_OK) {
while (n > 0) {
ngx_http_lua_finalize_request(r, NGX_DONE);
n--;
}
return NGX_OK;
}
/* rc == NGX_ERROR || rc > NGX_OK */
return rc;
}
done:
if (n == 1) {
return NGX_DONE;
}
if (n == 0) {
r->main->count++;
return NGX_DONE;
}
/* n > 1 */
do {
ngx_http_lua_finalize_request(r, NGX_DONE);
} while (--n > 1);
return NGX_DONE;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_
#define _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r);
void ngx_http_lua_content_wev_handler(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_content_handler_file(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_content_handler_inline(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_content_handler(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_content_run_posted_threads(lua_State *L,
ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int n);
#endif /* _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,465 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_control.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_coroutine.h"
static int ngx_http_lua_ngx_exec(lua_State *L);
static int ngx_http_lua_ngx_redirect(lua_State *L);
static int ngx_http_lua_on_abort(lua_State *L);
void
ngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L)
{
/* ngx.redirect */
lua_pushcfunction(L, ngx_http_lua_ngx_redirect);
lua_setfield(L, -2, "redirect");
/* ngx.exec */
lua_pushcfunction(L, ngx_http_lua_ngx_exec);
lua_setfield(L, -2, "exec");
/* ngx.on_abort */
lua_pushcfunction(L, ngx_http_lua_on_abort);
lua_setfield(L, -2, "on_abort");
}
static int
ngx_http_lua_ngx_exec(lua_State *L)
{
int n;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_str_t uri;
ngx_str_t args, user_args;
ngx_uint_t flags;
u_char *p;
u_char *q;
size_t len;
const char *msg;
n = lua_gettop(L);
if (n != 1 && n != 2) {
return luaL_error(L, "expecting one or two arguments, but got %d",
n);
}
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ngx_str_null(&args);
/* read the 1st argument (uri) */
p = (u_char *) luaL_checklstring(L, 1, &len);
if (len == 0) {
return luaL_error(L, "The uri argument is empty");
}
uri.data = ngx_palloc(r->pool, len);
if (uri.data == NULL) {
return luaL_error(L, "no memory");
}
ngx_memcpy(uri.data, p, len);
uri.len = len;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);
ngx_http_lua_check_if_abortable(L, ctx);
flags = NGX_HTTP_LOG_UNSAFE;
if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
return luaL_error(L, "unsafe uri");
}
if (n == 2) {
/* read the 2nd argument (args) */
dd("args type: %s", luaL_typename(L, 2));
switch (lua_type(L, 2)) {
case LUA_TNUMBER:
case LUA_TSTRING:
p = (u_char *) lua_tolstring(L, 2, &len);
user_args.data = ngx_palloc(r->pool, len);
if (user_args.data == NULL) {
return luaL_error(L, "no memory");
}
ngx_memcpy(user_args.data, p, len);
user_args.len = len;
break;
case LUA_TTABLE:
ngx_http_lua_process_args_option(r, L, 2, &user_args);
dd("user_args: %.*s", (int) user_args.len, user_args.data);
break;
case LUA_TNIL:
ngx_str_null(&user_args);
break;
default:
msg = lua_pushfstring(L, "string, number, or table expected, "
"but got %s", luaL_typename(L, 2));
return luaL_argerror(L, 2, msg);
}
} else {
user_args.data = NULL;
user_args.len = 0;
}
if (user_args.len) {
if (args.len == 0) {
args = user_args;
} else {
p = ngx_palloc(r->pool, args.len + user_args.len + 1);
if (p == NULL) {
return luaL_error(L, "no memory");
}
q = ngx_copy(p, args.data, args.len);
*q++ = '&';
ngx_memcpy(q, user_args.data, user_args.len);
args.data = p;
args.len += user_args.len + 1;
}
}
if (r->header_sent || ctx->header_sent) {
return luaL_error(L, "attempt to call ngx.exec after "
"sending out response headers");
}
ctx->exec_uri = uri;
ctx->exec_args = args;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua exec \"%V?%V\"",
&ctx->exec_uri, &ctx->exec_args);
return lua_yield(L, 0);
}
static int
ngx_http_lua_ngx_redirect(lua_State *L)
{
ngx_http_lua_ctx_t *ctx;
ngx_int_t rc;
int n;
u_char *p;
u_char *uri;
u_char byte;
size_t len;
ngx_table_elt_t *h;
ngx_http_request_t *r;
size_t buf_len;
u_char *buf;
n = lua_gettop(L);
if (n != 1 && n != 2) {
return luaL_error(L, "expecting one or two arguments");
}
p = (u_char *) luaL_checklstring(L, 1, &len);
if (n == 2) {
rc = (ngx_int_t) luaL_checknumber(L, 2);
if (rc != NGX_HTTP_MOVED_TEMPORARILY
&& rc != NGX_HTTP_MOVED_PERMANENTLY
&& rc != NGX_HTTP_SEE_OTHER
&& rc != NGX_HTTP_PERMANENT_REDIRECT
&& rc != NGX_HTTP_TEMPORARY_REDIRECT)
{
return luaL_error(L, "only ngx.HTTP_MOVED_TEMPORARILY, "
"ngx.HTTP_MOVED_PERMANENTLY, "
"ngx.HTTP_PERMANENT_REDIRECT, "
"ngx.HTTP_SEE_OTHER, and "
"ngx.HTTP_TEMPORARY_REDIRECT are allowed");
}
} else {
rc = NGX_HTTP_MOVED_TEMPORARILY;
}
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);
ngx_http_lua_check_if_abortable(L, ctx);
if (r->header_sent || ctx->header_sent) {
return luaL_error(L, "attempt to call ngx.redirect after sending out "
"the headers");
}
if (ngx_http_lua_check_unsafe_uri_bytes(r, p, len, &byte) != NGX_OK) {
buf_len = ngx_http_lua_escape_log(NULL, p, len) + 1;
buf = ngx_palloc(r->pool, buf_len);
if (buf == NULL) {
return NGX_ERROR;
}
ngx_http_lua_escape_log(buf, p, len);
buf[buf_len - 1] = '\0';
return luaL_error(L, "unsafe byte \"0x%02x\" in redirect uri \"%s\"",
byte, buf);
}
uri = ngx_palloc(r->pool, len);
if (uri == NULL) {
return luaL_error(L, "no memory");
}
ngx_memcpy(uri, p, len);
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return luaL_error(L, "no memory");
}
h->hash = ngx_http_lua_location_hash;
#if 0
dd("location hash: %lu == %lu",
(unsigned long) h->hash,
(unsigned long) ngx_hash_key_lc((u_char *) "Location",
sizeof("Location") - 1));
#endif
h->value.len = len;
h->value.data = uri;
ngx_str_set(&h->key, "Location");
r->headers_out.status = rc;
ctx->exit_code = rc;
ctx->exited = 1;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua redirect to \"%V\" with code %i",
&h->value, ctx->exit_code);
if (len && uri[0] != '/') {
r->headers_out.location = h;
}
/*
* we do not set r->headers_out.location here to avoid the handling
* the local redirects without a host name by ngx_http_header_filter()
*/
return lua_yield(L, 0);
}
static int
ngx_http_lua_on_abort(lua_State *L)
{
int co_ref;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *coctx = NULL;
ngx_http_lua_loc_conf_t *llcf;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_check_fake_request2(L, r, ctx);
if (ctx->on_abort_co_ctx) {
lua_pushnil(L);
lua_pushliteral(L, "duplicate call");
return 2;
}
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (!llcf->check_client_abort) {
lua_pushnil(L);
lua_pushliteral(L, "lua_check_client_abort is off");
return 2;
}
ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx, &co_ref);
coctx->co_ref = co_ref;
coctx->is_uthread = 1;
ctx->on_abort_co_ctx = coctx;
dd("on_wait thread 2: %p", coctx->co);
coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;
coctx->parent_co_ctx = ctx->cur_co_ctx;
lua_pushinteger(L, 1);
return 1;
}
int
ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err,
size_t *errlen)
{
ngx_http_lua_ctx_t *ctx;
if (status == NGX_AGAIN || status == NGX_DONE) {
*errlen = ngx_snprintf(err, *errlen,
"bad argument to 'ngx.exit': does not accept "
"NGX_AGAIN or NGX_DONE")
- err;
return NGX_ERROR;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err;
return NGX_ERROR;
}
if (ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT
| NGX_HTTP_LUA_CONTEXT_TIMER
| NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
| NGX_HTTP_LUA_CONTEXT_BALANCER
| NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO
| NGX_HTTP_LUA_CONTEXT_SSL_CERT
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH,
err, errlen)
!= NGX_OK)
{
return NGX_ERROR;
}
if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
| NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH))
{
#if (NGX_HTTP_SSL)
ctx->exit_code = status;
ctx->exited = 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua exit with code %d", status);
if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE) {
return NGX_DONE;
}
return NGX_OK;
#else
return NGX_ERROR;
#endif
}
if (ctx->no_abort
&& status != NGX_ERROR
&& status != NGX_HTTP_CLOSE
&& status != NGX_HTTP_REQUEST_TIME_OUT
&& status != NGX_HTTP_CLIENT_CLOSED_REQUEST)
{
*errlen = ngx_snprintf(err, *errlen,
"attempt to abort with pending subrequests")
- err;
return NGX_ERROR;
}
if ((r->header_sent || ctx->header_sent)
&& status >= NGX_HTTP_SPECIAL_RESPONSE
&& status != NGX_HTTP_REQUEST_TIME_OUT
&& status != NGX_HTTP_CLIENT_CLOSED_REQUEST
&& status != NGX_HTTP_CLOSE)
{
if (status != (ngx_int_t) r->headers_out.status) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to "
"set status %d via ngx.exit after sending out the "
"response status %ui", status,
r->headers_out.status);
}
status = NGX_HTTP_OK;
}
ctx->exit_code = status;
ctx->exited = 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua exit with code %i", ctx->exit_code);
if (ctx->context & (NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
| NGX_HTTP_LUA_CONTEXT_BALANCER))
{
return NGX_DONE;
}
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CONTROL_H_INCLUDED_
#define _NGX_HTTP_LUA_CONTROL_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L);
#endif /* _NGX_HTTP_LUA_CONTROL_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,436 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_coroutine.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_probe.h"
/*
* Design:
*
* In order to support using ngx.* API in Lua coroutines, we have to create
* new coroutine in the main coroutine instead of the calling coroutine
*/
static int ngx_http_lua_coroutine_create(lua_State *L);
static int ngx_http_lua_coroutine_wrap(lua_State *L);
static int ngx_http_lua_coroutine_resume(lua_State *L);
static int ngx_http_lua_coroutine_yield(lua_State *L);
static int ngx_http_lua_coroutine_status(lua_State *L);
static const ngx_str_t
ngx_http_lua_co_status_names[] =
{
ngx_string("running"),
ngx_string("suspended"),
ngx_string("normal"),
ngx_string("dead"),
ngx_string("zombie")
};
static int
ngx_http_lua_coroutine_create(lua_State *L)
{
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
return ngx_http_lua_coroutine_create_helper(L, r, ctx, NULL, NULL);
}
static int
ngx_http_lua_coroutine_wrap_runner(lua_State *L)
{
/* retrieve closure and insert it at the bottom of
* the stack for coroutine.resume() */
lua_pushvalue(L, lua_upvalueindex(1));
lua_insert(L, 1);
return ngx_http_lua_coroutine_resume(L);
}
static int
ngx_http_lua_coroutine_wrap(lua_State *L)
{
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *coctx = NULL;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx, NULL);
coctx->is_wrap = 1;
lua_pushcclosure(L, ngx_http_lua_coroutine_wrap_runner, 1);
return 1;
}
int
ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx, int *co_ref)
{
lua_State *vm; /* the Lua VM */
lua_State *co; /* new coroutine to be created */
ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */
ngx_http_lua_main_conf_t *lmcf;
luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
"Lua function expected");
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);
vm = ngx_http_lua_get_lua_vm(r, ctx);
/* create new coroutine on root Lua state, so it always yields
* to main Lua thread
*/
if (co_ref == NULL) {
co = lua_newthread(vm);
} else {
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
*co_ref = ngx_http_lua_new_cached_thread(vm, &co, lmcf, 0);
}
ngx_http_lua_probe_user_coroutine_create(r, L, co);
coctx = ngx_http_lua_get_co_ctx(co, ctx);
if (coctx == NULL) {
coctx = ngx_http_lua_create_co_ctx(r, ctx);
if (coctx == NULL) {
return luaL_error(L, "no memory");
}
} else {
ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t));
coctx->next_zombie_child_thread = &coctx->zombie_child_threads;
coctx->co_ref = LUA_NOREF;
}
coctx->co = co;
coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;
#ifdef OPENRESTY_LUAJIT
ngx_http_lua_set_req(co, r);
ngx_http_lua_attach_co_ctx_to_L(co, coctx);
#else
/* make new coroutine share globals of the parent coroutine.
* NOTE: globals don't have to be separated! */
ngx_http_lua_get_globals_table(L);
lua_xmove(L, co, 1);
ngx_http_lua_set_globals_table(co);
#endif
lua_xmove(vm, L, 1); /* move coroutine from main thread to L */
if (co_ref) {
lua_pop(vm, 1); /* pop coroutines */
}
lua_pushvalue(L, 1); /* copy entry function to top of L*/
lua_xmove(L, co, 1); /* move entry function from L to co */
if (pcoctx) {
*pcoctx = coctx;
}
#ifdef NGX_LUA_USE_ASSERT
coctx->co_top = 1;
#endif
return 1; /* return new coroutine to Lua */
}
static int
ngx_http_lua_coroutine_resume(lua_State *L)
{
lua_State *co;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *coctx;
ngx_http_lua_co_ctx_t *p_coctx; /* parent co ctx */
co = lua_tothread(L, 1);
luaL_argcheck(L, co, 1, "coroutine expected");
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);
p_coctx = ctx->cur_co_ctx;
if (p_coctx == NULL) {
return luaL_error(L, "no parent co ctx found");
}
coctx = ngx_http_lua_get_co_ctx(co, ctx);
if (coctx == NULL) {
return luaL_error(L, "no co ctx found");
}
ngx_http_lua_probe_user_coroutine_resume(r, L, co);
if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {
dd("coroutine resume: %d", coctx->co_status);
lua_pushboolean(L, 0);
lua_pushfstring(L, "cannot resume %s coroutine",
ngx_http_lua_co_status_names[coctx->co_status].data);
return 2;
}
p_coctx->co_status = NGX_HTTP_LUA_CO_NORMAL;
coctx->parent_co_ctx = p_coctx;
dd("set coroutine to running");
coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
ctx->co_op = NGX_HTTP_LUA_USER_CORO_RESUME;
ctx->cur_co_ctx = coctx;
/* yield and pass args to main thread, and resume target coroutine from
* there */
return lua_yield(L, lua_gettop(L) - 1);
}
static int
ngx_http_lua_coroutine_yield(lua_State *L)
{
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *coctx;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);
coctx = ctx->cur_co_ctx;
coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;
ctx->co_op = NGX_HTTP_LUA_USER_CORO_YIELD;
if (!coctx->is_uthread && coctx->parent_co_ctx) {
dd("set coroutine to running");
coctx->parent_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
ngx_http_lua_probe_user_coroutine_yield(r, coctx->parent_co_ctx->co, L);
} else {
ngx_http_lua_probe_user_coroutine_yield(r, NULL, L);
}
/* yield and pass retvals to main thread,
* and resume parent coroutine there */
return lua_yield(L, lua_gettop(L));
}
void
ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
{
int rc;
/* new coroutine table */
lua_createtable(L, 0 /* narr */, 16 /* nrec */);
/* get old coroutine table */
lua_getglobal(L, "coroutine");
/* set running to the old one */
lua_getfield(L, -1, "running");
lua_setfield(L, -3, "running");
lua_getfield(L, -1, "create");
lua_setfield(L, -3, "_create");
lua_getfield(L, -1, "wrap");
lua_setfield(L, -3, "_wrap");
lua_getfield(L, -1, "resume");
lua_setfield(L, -3, "_resume");
lua_getfield(L, -1, "yield");
lua_setfield(L, -3, "_yield");
lua_getfield(L, -1, "status");
lua_setfield(L, -3, "_status");
/* pop the old coroutine */
lua_pop(L, 1);
lua_pushcfunction(L, ngx_http_lua_coroutine_create);
lua_setfield(L, -2, "__create");
lua_pushcfunction(L, ngx_http_lua_coroutine_wrap);
lua_setfield(L, -2, "__wrap");
lua_pushcfunction(L, ngx_http_lua_coroutine_resume);
lua_setfield(L, -2, "__resume");
lua_pushcfunction(L, ngx_http_lua_coroutine_yield);
lua_setfield(L, -2, "__yield");
lua_pushcfunction(L, ngx_http_lua_coroutine_status);
lua_setfield(L, -2, "__status");
lua_setglobal(L, "coroutine");
/* inject coroutine APIs */
{
const char buf[] =
"local keys = {'create', 'yield', 'resume', 'status', 'wrap'}\n"
#ifdef OPENRESTY_LUAJIT
"local get_req = require 'thread.exdata'\n"
#else
"local getfenv = getfenv\n"
#endif
"for _, key in ipairs(keys) do\n"
"local std = coroutine['_' .. key]\n"
"local ours = coroutine['__' .. key]\n"
"local raw_ctx = ngx._phase_ctx\n"
"coroutine[key] = function (...)\n"
#ifdef OPENRESTY_LUAJIT
"local r = get_req()\n"
#else
"local r = getfenv(0).__ngx_req\n"
#endif
"if r ~= nil then\n"
#ifdef OPENRESTY_LUAJIT
"local ctx = raw_ctx()\n"
#else
"local ctx = raw_ctx(r)\n"
#endif
/* ignore header and body filters */
"if ctx ~= 0x020 and ctx ~= 0x040 then\n"
"return ours(...)\n"
"end\n"
"end\n"
"return std(...)\n"
"end\n"
"end\n"
"package.loaded.coroutine = coroutine"
#if 0
"debug.sethook(function () collectgarbage() end, 'rl', 1)"
#endif
;
rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine_api");
}
if (rc != 0) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"failed to load Lua code for coroutine_api: %i: %s",
rc, lua_tostring(L, -1));
lua_pop(L, 1);
return;
}
rc = lua_pcall(L, 0, 0, 0);
if (rc != 0) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"failed to run the Lua code for coroutine_api: %i: %s",
rc, lua_tostring(L, -1));
lua_pop(L, 1);
}
}
static int
ngx_http_lua_coroutine_status(lua_State *L)
{
lua_State *co; /* new coroutine to be created */
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */
co = lua_tothread(L, 1);
luaL_argcheck(L, co, 1, "coroutine expected");
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);
coctx = ngx_http_lua_get_co_ctx(co, ctx);
if (coctx == NULL) {
lua_pushlstring(L, (const char *)
ngx_http_lua_co_status_names[NGX_HTTP_LUA_CO_DEAD].data,
ngx_http_lua_co_status_names[NGX_HTTP_LUA_CO_DEAD].len);
return 1;
}
dd("co status: %d", coctx->co_status);
lua_pushlstring(L, (const char *)
ngx_http_lua_co_status_names[coctx->co_status].data,
ngx_http_lua_co_status_names[coctx->co_status].len);
return 1;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_
#define _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L);
int ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx, int *co_ref);
#endif /* _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,206 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_ssl.h"
#include "ngx_http_lua_ctx.h"
typedef struct {
int ref;
lua_State *vm;
} ngx_http_lua_ngx_ctx_cleanup_data_t;
static ngx_int_t ngx_http_lua_ngx_ctx_add_cleanup(ngx_http_request_t *r,
ngx_pool_t *pool, int ref);
static void ngx_http_lua_ngx_ctx_cleanup(void *data);
int
ngx_http_lua_ngx_set_ctx_helper(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, int index)
{
ngx_pool_t *pool;
if (index < 0) {
index = lua_gettop(L) + index + 1;
}
if (ctx->ctx_ref == LUA_NOREF) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua create ngx.ctx table for the current request");
lua_pushliteral(L, ngx_http_lua_ctx_tables_key);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushvalue(L, index);
ctx->ctx_ref = luaL_ref(L, -2);
lua_pop(L, 1);
pool = r->pool;
if (ngx_http_lua_ngx_ctx_add_cleanup(r, pool, ctx->ctx_ref) != NGX_OK) {
return luaL_error(L, "no memory");
}
return 0;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua fetching existing ngx.ctx table for the current "
"request");
lua_pushliteral(L, ngx_http_lua_ctx_tables_key);
lua_rawget(L, LUA_REGISTRYINDEX);
luaL_unref(L, -1, ctx->ctx_ref);
lua_pushvalue(L, index);
ctx->ctx_ref = luaL_ref(L, -2);
lua_pop(L, 1);
return 0;
}
int
ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r, int *in_ssl_phase,
int *ssl_ctx_ref)
{
ngx_http_lua_ctx_t *ctx;
#if (NGX_HTTP_SSL)
ngx_http_lua_ssl_ctx_t *ssl_ctx;
#endif
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return NGX_HTTP_LUA_FFI_NO_REQ_CTX;
}
if (ctx->ctx_ref >= 0 || in_ssl_phase == NULL) {
return ctx->ctx_ref;
}
*in_ssl_phase = ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
| NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE);
*ssl_ctx_ref = LUA_NOREF;
#if (NGX_HTTP_SSL)
if (r->connection->ssl != NULL) {
ssl_ctx = ngx_http_lua_ssl_get_ctx(r->connection->ssl->connection);
if (ssl_ctx != NULL) {
*ssl_ctx_ref = ssl_ctx->ctx_ref;
}
}
#endif
return LUA_NOREF;
}
int
ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref)
{
ngx_pool_t *pool;
ngx_http_lua_ctx_t *ctx;
#if (NGX_HTTP_SSL)
ngx_connection_t *c;
ngx_http_lua_ssl_ctx_t *ssl_ctx;
#endif
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return NGX_HTTP_LUA_FFI_NO_REQ_CTX;
}
#if (NGX_HTTP_SSL)
if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
| NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH
| NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE))
{
ssl_ctx = ngx_http_lua_ssl_get_ctx(r->connection->ssl->connection);
if (ssl_ctx == NULL) {
return NGX_ERROR;
}
ssl_ctx->ctx_ref = ref;
c = ngx_ssl_get_connection(r->connection->ssl->connection);
pool = c->pool;
} else {
pool = r->pool;
}
#else
pool = r->pool;
#endif
ctx->ctx_ref = ref;
if (ngx_http_lua_ngx_ctx_add_cleanup(r, pool, ref) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_ngx_ctx_add_cleanup(ngx_http_request_t *r, ngx_pool_t *pool,
int ref)
{
lua_State *L;
ngx_pool_cleanup_t *cln;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_ngx_ctx_cleanup_data_t *data;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, ctx);
cln = ngx_pool_cleanup_add(pool,
sizeof(ngx_http_lua_ngx_ctx_cleanup_data_t));
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_lua_ngx_ctx_cleanup;
data = cln->data;
data->vm = L;
data->ref = ref;
return NGX_OK;
}
static void
ngx_http_lua_ngx_ctx_cleanup(void *data)
{
lua_State *L;
ngx_http_lua_ngx_ctx_cleanup_data_t *clndata = data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"lua release ngx.ctx at ref %d", clndata->ref);
L = clndata->vm;
lua_pushliteral(L, ngx_http_lua_ctx_tables_key);
lua_rawget(L, LUA_REGISTRYINDEX);
luaL_unref(L, -1, clndata->ref);
lua_pop(L, 1);
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_CTX_H_INCLUDED_
#define _NGX_HTTP_LUA_CTX_H_INCLUDED_
#include "ngx_http_lua_common.h"
int ngx_http_lua_ngx_set_ctx_helper(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, int index);
#endif /* _NGX_HTTP_LUA_CTX_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_
#define _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_
#include "ngx_http_lua_common.h"
char *ngx_http_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
char *ngx_http_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_regex_cache_max_entries(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_regex_match_limit(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_server_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_server_rewrite_by_lua_block(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
char *ngx_http_lua_rewrite_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_access_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_header_filter_by_lua_block(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
char *ngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_body_filter_by_lua_block(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
char *ngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_init_worker_by_lua_block(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
char *ngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_exit_worker_by_lua_block(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
char *ngx_http_lua_exit_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
char *ngx_http_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#if defined(NDK) && NDK
char *ngx_http_lua_set_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
char *ngx_http_lua_set_by_lua_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
ngx_int_t ngx_http_lua_filter_set_by_lua_inline(ngx_http_request_t *r,
ngx_str_t *val, ngx_http_variable_value_t *v, void *data);
ngx_int_t ngx_http_lua_filter_set_by_lua_file(ngx_http_request_t *r,
ngx_str_t *val, ngx_http_variable_value_t *v, void *data);
#endif
char *ngx_http_lua_rewrite_no_postpone(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf,
ngx_command_t *cmd);
char *ngx_http_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
u_char *ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag,
size_t tag_len, size_t *chunkname_len);
#endif /* _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_util.h"
/* longjmp mark for restoring nginx execution after Lua VM crashing */
jmp_buf ngx_http_lua_exception;
/**
* Override default Lua panic handler, output VM crash reason to nginx error
* log, and restore execution to the nearest jmp-mark.
*
* @param L Lua state pointer
* @retval Long jump to the nearest jmp-mark, never returns.
* @note nginx request pointer should be stored in Lua thread's globals table
* in order to make logging working.
* */
int
ngx_http_lua_atpanic(lua_State *L)
{
#ifdef NGX_LUA_ABORT_AT_PANIC
abort();
#else
u_char *s = NULL;
size_t len = 0;
if (lua_type(L, -1) == LUA_TSTRING) {
s = (u_char *) lua_tolstring(L, -1, &len);
}
if (s == NULL) {
s = (u_char *) "unknown reason";
len = sizeof("unknown reason") - 1;
}
ngx_log_stderr(0, "lua atpanic: Lua VM crashed, reason: %*s", len, s);
ngx_quit = 1;
/* restore nginx execution */
NGX_LUA_EXCEPTION_THROW(1);
/* impossible to reach here */
#endif
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_
#define _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_
#include "ngx_http_lua_common.h"
#define NGX_LUA_EXCEPTION_TRY \
if (setjmp(ngx_http_lua_exception) == 0)
#define NGX_LUA_EXCEPTION_CATCH \
else
#define NGX_LUA_EXCEPTION_THROW(x) \
longjmp(ngx_http_lua_exception, (x))
extern jmp_buf ngx_http_lua_exception;
int ngx_http_lua_atpanic(lua_State *L);
#endif /* _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,127 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_exitworkerby.h"
#include "ngx_http_lua_util.h"
#if (NGX_THREADS)
#include "ngx_http_lua_worker_thread.h"
#endif
void
ngx_http_lua_exit_worker(ngx_cycle_t *cycle)
{
ngx_http_lua_main_conf_t *lmcf;
ngx_connection_t *c = NULL;
ngx_http_request_t *r = NULL;
ngx_http_lua_ctx_t *ctx;
ngx_http_conf_ctx_t *conf_ctx;
#if (NGX_THREADS)
ngx_http_lua_thread_exit_process();
#endif
lmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lua_module);
if (lmcf == NULL
|| lmcf->exit_worker_handler == NULL
|| lmcf->lua == NULL
#if !(NGX_WIN32)
|| (ngx_process == NGX_PROCESS_HELPER
# ifdef HAVE_PRIVILEGED_PROCESS_PATCH
&& !ngx_is_privileged_agent
# endif
)
#endif /* NGX_WIN32 */
)
{
return;
}
conf_ctx = ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]);
c = ngx_http_lua_create_fake_connection(NULL);
if (c == NULL) {
goto failed;
}
c->log = ngx_cycle->log;
r = ngx_http_lua_create_fake_request(c);
if (r == NULL) {
goto failed;
}
r->main_conf = conf_ctx->main_conf;
r->srv_conf = conf_ctx->srv_conf;
r->loc_conf = conf_ctx->loc_conf;
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
goto failed;
}
ctx->context = NGX_HTTP_LUA_CONTEXT_EXIT_WORKER;
ctx->cur_co_ctx = NULL;
ngx_http_lua_set_req(lmcf->lua, r);
(void) lmcf->exit_worker_handler(cycle->log, lmcf, lmcf->lua);
ngx_destroy_pool(c->pool);
return;
failed:
if (c) {
ngx_http_lua_close_fake_connection(c);
}
return;
}
ngx_int_t
ngx_http_lua_exit_worker_by_inline(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L)
{
int status;
const char *chunkname;
if (lmcf->exit_worker_chunkname == NULL) {
chunkname = "=exit_worker_by_lua";
} else {
chunkname = (const char *) lmcf->exit_worker_chunkname;
}
status = luaL_loadbuffer(L, (char *) lmcf->exit_worker_src.data,
lmcf->exit_worker_src.len, chunkname)
|| ngx_http_lua_do_call(log, L);
return ngx_http_lua_report(log, L, status, "exit_worker_by_lua");
}
ngx_int_t
ngx_http_lua_exit_worker_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,
lua_State *L)
{
int status;
status = luaL_loadfile(L, (char *) lmcf->exit_worker_src.data)
|| ngx_http_lua_do_call(log, L);
return ngx_http_lua_report(log, L, status, "exit_worker_by_lua_file");
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_
#define _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_exit_worker_by_inline(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L);
ngx_int_t ngx_http_lua_exit_worker_by_file(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L);
void ngx_http_lua_exit_worker(ngx_cycle_t *cycle);
#endif /* _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,303 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_headerfilterby.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_pcrefix.h"
#include "ngx_http_lua_log.h"
#include "ngx_http_lua_cache.h"
#include "ngx_http_lua_headers.h"
#include "ngx_http_lua_string.h"
#include "ngx_http_lua_misc.h"
#include "ngx_http_lua_consts.h"
#include "ngx_http_lua_shdict.h"
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
/**
* Set environment table for the given code closure.
*
* Before:
* | code closure | <- top
* | ... |
*
* After:
* | code closure | <- top
* | ... |
* */
static void
ngx_http_lua_header_filter_by_lua_env(lua_State *L, ngx_http_request_t *r)
{
ngx_http_lua_set_req(L, r);
#ifndef OPENRESTY_LUAJIT
/**
* we want to create empty environment for current script
*
* newt = {}
* newt["_G"] = newt
* setmetatable(newt, {__index = _G})
*
* if a function or symbol is not defined in our env, __index will lookup
* in the global env.
*
* all variables created in the script-env will be thrown away at the end
* of the script run.
* */
ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
/* {{{ make new env inheriting main thread's globals table */
lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new env */
ngx_http_lua_get_globals_table(L);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
/* }}} */
lua_setfenv(L, -2); /* set new running env for the code closure */
#endif /* OPENRESTY_LUAJIT */
}
ngx_int_t
ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r)
{
int old_exit_code = 0;
ngx_int_t rc;
u_char *err_msg;
size_t len;
#if (NGX_PCRE)
ngx_pool_t *old_pool;
#endif
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx->exited) {
old_exit_code = ctx->exit_code;
}
/* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */
ngx_http_lua_header_filter_by_lua_env(L, r);
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif
lua_pushcfunction(L, ngx_http_lua_traceback);
lua_insert(L, 1); /* put it under chunk and args */
/* protected call user code */
rc = lua_pcall(L, 0, 1, 1);
lua_remove(L, 1); /* remove traceback function */
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
ngx_http_lua_pcre_malloc_done(old_pool);
#endif
dd("rc == %d", (int) rc);
if (rc != 0) {
/* error occurred when running loaded code */
err_msg = (u_char *) lua_tolstring(L, -1, &len);
if (err_msg == NULL) {
err_msg = (u_char *) "unknown reason";
len = sizeof("unknown reason") - 1;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"failed to run header_filter_by_lua*: %*s", len, err_msg);
lua_settop(L, 0); /* clear remaining elems on stack */
return NGX_ERROR;
}
dd("exited: %d, exit code: %d, old exit code: %d",
(int) ctx->exited, (int) ctx->exit_code, (int) old_exit_code);
#if 1
/* clear Lua stack */
lua_settop(L, 0);
#endif
if (ctx->exited && ctx->exit_code != old_exit_code) {
if (ctx->exit_code == NGX_ERROR) {
return NGX_ERROR;
}
dd("finalize request with %d", (int) ctx->exit_code);
rc = ngx_http_filter_finalize_request(r, &ngx_http_lua_module,
ctx->exit_code);
if (rc == NGX_ERROR || rc == NGX_AGAIN) {
return rc;
}
return NGX_DECLINED;
}
return NGX_OK;
}
ngx_int_t
ngx_http_lua_header_filter_inline(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
ngx_http_lua_loc_conf_t *llcf;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
llcf->header_filter_src.value.data,
llcf->header_filter_src.value.len,
&llcf->header_filter_src_ref,
llcf->header_filter_src_key,
(const char *)
llcf->header_filter_chunkname);
if (rc != NGX_OK) {
return NGX_ERROR;
}
dd("calling header filter by chunk");
return ngx_http_lua_header_filter_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_header_filter_file(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
u_char *script_path;
ngx_http_lua_loc_conf_t *llcf;
ngx_str_t eval_src;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
/* Eval nginx variables in code path string first */
if (ngx_http_complex_value(r, &llcf->header_filter_src, &eval_src)
!= NGX_OK)
{
return NGX_ERROR;
}
script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
eval_src.len);
if (script_path == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
&llcf->header_filter_src_ref,
llcf->header_filter_src_key);
if (rc != NGX_OK) {
return NGX_ERROR;
}
/* make sure we have a valid code chunk */
ngx_http_lua_assert(lua_isfunction(L, -1));
return ngx_http_lua_header_filter_by_chunk(L, r);
}
static ngx_int_t
ngx_http_lua_header_filter(ngx_http_request_t *r)
{
ngx_http_lua_loc_conf_t *llcf;
ngx_http_lua_ctx_t *ctx;
ngx_int_t rc;
ngx_pool_cleanup_t *cln;
uint16_t old_context;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua header filter for user lua code, uri \"%V\"", &r->uri);
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->body_filter_handler) {
r->filter_need_in_memory = 1;
}
if (llcf->header_filter_handler == NULL) {
dd("no header filter handler found");
return ngx_http_next_header_filter(r);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
}
if (ctx->cleanup == NULL) {
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_lua_request_cleanup_handler;
cln->data = ctx;
ctx->cleanup = &cln->handler;
}
old_context = ctx->context;
ctx->context = NGX_HTTP_LUA_CONTEXT_HEADER_FILTER;
dd("calling header filter handler");
rc = llcf->header_filter_handler(r);
ctx->context = old_context;
if (rc == NGX_DECLINED) {
return NGX_OK;
}
if (rc == NGX_AGAIN || rc == NGX_ERROR) {
return rc;
}
return ngx_http_next_header_filter(r);
}
ngx_int_t
ngx_http_lua_header_filter_init(void)
{
dd("calling header filter init");
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_lua_header_filter;
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_
#define _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
extern ngx_http_output_header_filter_pt ngx_http_lua_next_filter_header_filter;
ngx_int_t ngx_http_lua_header_filter_init(void);
ngx_int_t ngx_http_lua_header_filter_by_chunk(lua_State *L,
ngx_http_request_t *r);
ngx_int_t ngx_http_lua_header_filter_inline(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_header_filter_file(ngx_http_request_t *r);
#endif /* _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_HEADERS_H_INCLUDED_
#define _NGX_HTTP_LUA_HEADERS_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_resp_header_api(lua_State *L);
void ngx_http_lua_inject_req_header_api(lua_State *L);
void ngx_http_lua_create_headers_metatable(ngx_log_t *log, lua_State *L);
#if (nginx_version >= 1011011)
void ngx_http_lua_ngx_raw_header_cleanup(void *data);
#endif
#endif /* _NGX_HTTP_LUA_HEADERS_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,902 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include "ngx_http_lua_headers_in.h"
#include "ngx_http_lua_util.h"
#include <ctype.h>
static ngx_int_t ngx_http_set_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value,
ngx_table_elt_t **output_header);
static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_user_agent_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_connection_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_lua_validate_host(ngx_str_t *host, ngx_pool_t *pool,
ngx_uint_t alloc);
static ngx_int_t ngx_http_set_host_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_lua_rm_header_helper(ngx_list_t *l,
ngx_list_part_t *cur, ngx_uint_t i);
static ngx_http_lua_set_header_t ngx_http_lua_set_handlers[] = {
{ ngx_string("Host"),
offsetof(ngx_http_headers_in_t, host),
ngx_http_set_host_header },
{ ngx_string("Connection"),
offsetof(ngx_http_headers_in_t, connection),
ngx_http_set_connection_header },
{ ngx_string("If-Modified-Since"),
offsetof(ngx_http_headers_in_t, if_modified_since),
ngx_http_set_builtin_header },
{ ngx_string("If-Unmodified-Since"),
offsetof(ngx_http_headers_in_t, if_unmodified_since),
ngx_http_set_builtin_header },
{ ngx_string("If-Match"),
offsetof(ngx_http_headers_in_t, if_match),
ngx_http_set_builtin_header },
{ ngx_string("If-None-Match"),
offsetof(ngx_http_headers_in_t, if_none_match),
ngx_http_set_builtin_header },
{ ngx_string("User-Agent"),
offsetof(ngx_http_headers_in_t, user_agent),
ngx_http_set_user_agent_header },
{ ngx_string("Referer"),
offsetof(ngx_http_headers_in_t, referer),
ngx_http_set_builtin_header },
{ ngx_string("Content-Length"),
offsetof(ngx_http_headers_in_t, content_length),
ngx_http_set_content_length_header },
{ ngx_string("Content-Type"),
offsetof(ngx_http_headers_in_t, content_type),
ngx_http_set_builtin_header },
{ ngx_string("Range"),
offsetof(ngx_http_headers_in_t, range),
ngx_http_set_builtin_header },
{ ngx_string("If-Range"),
offsetof(ngx_http_headers_in_t, if_range),
ngx_http_set_builtin_header },
{ ngx_string("Transfer-Encoding"),
offsetof(ngx_http_headers_in_t, transfer_encoding),
ngx_http_set_builtin_header },
{ ngx_string("Expect"),
offsetof(ngx_http_headers_in_t, expect),
ngx_http_set_builtin_header },
{ ngx_string("Upgrade"),
offsetof(ngx_http_headers_in_t, upgrade),
ngx_http_set_builtin_header },
#if (NGX_HTTP_GZIP)
{ ngx_string("Accept-Encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding),
ngx_http_set_builtin_header },
{ ngx_string("Via"),
offsetof(ngx_http_headers_in_t, via),
ngx_http_set_builtin_header },
#endif
{ ngx_string("Authorization"),
offsetof(ngx_http_headers_in_t, authorization),
ngx_http_set_builtin_header },
{ ngx_string("Keep-Alive"),
offsetof(ngx_http_headers_in_t, keep_alive),
ngx_http_set_builtin_header },
#if (NGX_HTTP_X_FORWARDED_FOR)
{ ngx_string("X-Forwarded-For"),
offsetof(ngx_http_headers_in_t, x_forwarded_for),
ngx_http_set_builtin_multi_header },
#endif
#if (NGX_HTTP_REALIP)
{ ngx_string("X-Real-IP"),
offsetof(ngx_http_headers_in_t, x_real_ip),
ngx_http_set_builtin_header },
#endif
#if (NGX_HTTP_DAV)
{ ngx_string("Depth"),
offsetof(ngx_http_headers_in_t, depth),
ngx_http_set_builtin_header },
{ ngx_string("Destination"),
offsetof(ngx_http_headers_in_t, destination),
ngx_http_set_builtin_header },
{ ngx_string("Overwrite"),
offsetof(ngx_http_headers_in_t, overwrite),
ngx_http_set_builtin_header },
{ ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),
ngx_http_set_builtin_header },
#endif
#if defined(nginx_version) && nginx_version >= 1023000
{ ngx_string("Cookie"),
offsetof(ngx_http_headers_in_t, cookie),
ngx_http_set_builtin_multi_header },
#else
{ ngx_string("Cookie"),
offsetof(ngx_http_headers_in_t, cookies),
ngx_http_set_builtin_multi_header },
#endif
{ ngx_null_string, 0, ngx_http_set_header }
};
/* request time implementation */
static ngx_int_t
ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
ngx_str_t *value)
{
return ngx_http_set_header_helper(r, hv, value, NULL);
}
static ngx_int_t
ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
ngx_str_t *value, ngx_table_elt_t **output_header)
{
ngx_table_elt_t *h, *matched;
ngx_list_part_t *part;
ngx_uint_t i;
ngx_uint_t rc;
if (hv->no_override) {
goto new_header;
}
matched = NULL;
retry:
part = &r->headers_in.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
dd("i: %d, part: %p", (int) i, part);
if (h[i].key.len == hv->key.len
&& ngx_strncasecmp(h[i].key.data, hv->key.data, h[i].key.len)
== 0)
{
if (value->len == 0 || (matched && matched != &h[i])) {
h[i].hash = 0;
dd("rm header %.*s: %.*s", (int) h[i].key.len, h[i].key.data,
(int) h[i].value.len, h[i].value.data);
rc = ngx_http_lua_rm_header_helper(&r->headers_in.headers,
part, i);
ngx_http_lua_assert(!(r->headers_in.headers.part.next == NULL
&& r->headers_in.headers.last
!= &r->headers_in.headers.part));
dd("rm header: rc=%d", (int) rc);
if (rc == NGX_OK) {
if (output_header) {
*output_header = NULL;
}
goto retry;
}
return NGX_ERROR;
}
h[i].value = *value;
if (output_header) {
*output_header = &h[i];
dd("setting existing builtin input header");
}
if (matched == NULL) {
matched = &h[i];
}
}
}
if (matched){
return NGX_OK;
}
if (value->len == 0) {
return NGX_OK;
}
new_header:
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
dd("created new header for %.*s", (int) hv->key.len, hv->key.data);
if (value->len == 0) {
h->hash = 0;
} else {
h->hash = hv->hash;
}
h->key = hv->key;
h->value = *value;
h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
if (h->lowcase_key == NULL) {
return NGX_ERROR;
}
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
if (output_header) {
*output_header = h;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_set_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
ngx_table_elt_t *h, **old;
dd("entered set_builtin_header (input)");
if (hv->offset) {
old = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset);
} else {
old = NULL;
}
dd("old builtin ptr ptr: %p", old);
if (old) {
dd("old builtin ptr: %p", *old);
}
if (old == NULL || *old == NULL) {
dd("set normal header");
return ngx_http_set_header_helper(r, hv, value, old);
}
h = *old;
if (value->len == 0) {
h->hash = 0;
h->value = *value;
return ngx_http_set_header_helper(r, hv, value, old);
}
h->hash = hv->hash;
h->value = *value;
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
{
u_char *h, ch;
size_t i, dot_pos, host_len;
enum {
sw_usual = 0,
sw_literal,
sw_rest,
} state;
dot_pos = host->len;
host_len = host->len;
h = host->data;
state = sw_usual;
for (i = 0; i < host->len; i++) {
ch = h[i];
switch (ch) {
case '.':
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case ':':
if (state == sw_usual) {
host_len = i;
state = sw_rest;
}
break;
case '[':
if (i == 0) {
state = sw_literal;
}
break;
case ']':
if (state == sw_literal) {
host_len = i + 1;
state = sw_rest;
}
break;
case '\0':
return NGX_DECLINED;
default:
if (ngx_path_separator(ch)) {
return NGX_DECLINED;
}
if (ch >= 'A' && ch <= 'Z') {
alloc = 1;
}
break;
}
}
if (dot_pos == host_len - 1) {
host_len--;
}
if (host_len == 0) {
return NGX_DECLINED;
}
if (alloc) {
host->data = ngx_pnalloc(pool, host_len);
if (host->data == NULL) {
return NGX_ERROR;
}
ngx_strlow(host->data, h, host_len);
}
host->len = host_len;
return NGX_OK;
}
static ngx_int_t
ngx_http_set_host_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
ngx_str_t *value)
{
ngx_str_t host;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_variable_value_t *var;
dd("server new value len: %d", (int) value->len);
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
if (value->len) {
host= *value;
if (ngx_http_lua_validate_host(&host, r->pool, 0) != NGX_OK) {
return NGX_ERROR;
}
r->headers_in.server = host;
} else {
r->headers_in.server = *value;
}
var = &r->variables[lmcf->host_var_index];
var->valid = 0;
var->not_found = 0;
return ngx_http_set_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_set_connection_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
r->headers_in.connection_type = 0;
if (value->len == 0) {
return ngx_http_set_builtin_header(r, hv, value);
}
if (ngx_strcasestrn(value->data, "close", 5 - 1)) {
r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
r->headers_in.keep_alive_n = -1;
} else if (ngx_strcasestrn(value->data, "keep-alive", 10 - 1)) {
r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
}
return ngx_http_set_builtin_header(r, hv, value);
}
/* borrowed the code from ngx_http_request.c:ngx_http_process_user_agent */
static ngx_int_t
ngx_http_set_user_agent_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
u_char *user_agent, *msie;
/* clear existing settings */
r->headers_in.msie = 0;
r->headers_in.msie6 = 0;
r->headers_in.opera = 0;
r->headers_in.gecko = 0;
r->headers_in.chrome = 0;
r->headers_in.safari = 0;
r->headers_in.konqueror = 0;
if (value->len == 0) {
return ngx_http_set_builtin_header(r, hv, value);
}
/* check some widespread browsers */
user_agent = value->data;
msie = ngx_strstrn(user_agent, "MSIE ", 5 - 1);
if (msie && msie + 7 < user_agent + value->len) {
r->headers_in.msie = 1;
if (msie[6] == '.') {
switch (msie[5]) {
case '4':
case '5':
r->headers_in.msie6 = 1;
break;
case '6':
if (ngx_strstrn(msie + 8, "SV1", 3 - 1) == NULL) {
r->headers_in.msie6 = 1;
}
break;
}
}
}
if (ngx_strstrn(user_agent, "Opera", 5 - 1)) {
r->headers_in.opera = 1;
r->headers_in.msie = 0;
r->headers_in.msie6 = 0;
}
if (!r->headers_in.msie && !r->headers_in.opera) {
if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) {
r->headers_in.gecko = 1;
} else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) {
r->headers_in.chrome = 1;
} else if (ngx_strstrn(user_agent, "Safari/", 7 - 1)
&& ngx_strstrn(user_agent, "Mac OS X", 8 - 1))
{
r->headers_in.safari = 1;
} else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) {
r->headers_in.konqueror = 1;
}
}
return ngx_http_set_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_set_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
off_t len;
if (value->len == 0) {
return ngx_http_clear_content_length_header(r, hv, value);
}
len = ngx_atoof(value->data, value->len);
if (len == NGX_ERROR) {
return NGX_ERROR;
}
dd("reset headers_in.content_length_n to %d", (int) len);
r->headers_in.content_length_n = len;
return ngx_http_set_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_set_builtin_multi_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
#if defined(nginx_version) && nginx_version >= 1023000
ngx_table_elt_t **headers, **ph, *h;
int nelts;
headers = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset);
if (!hv->no_override && *headers != NULL) {
nelts = 0;
for (h = *headers; h; h = h->next) {
nelts++;
}
*headers = NULL;
dd("clear multi-value headers: %d", nelts);
}
if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) {
return NGX_ERROR;
}
if (value->len == 0) {
return NGX_OK;
}
dd("new multi-value header: %p", h);
if (*headers) {
for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ }
*ph = h;
} else {
*headers = h;
}
h->next = NULL;
return NGX_OK;
#else
ngx_array_t *headers;
ngx_table_elt_t **v, *h;
headers = (ngx_array_t *) ((char *) &r->headers_in + hv->offset);
if (!hv->no_override && headers->nelts > 0) {
ngx_array_destroy(headers);
if (ngx_array_init(headers, r->pool, 2,
sizeof(ngx_table_elt_t *))
!= NGX_OK)
{
return NGX_ERROR;
}
dd("clear multi-value headers: %d", (int) headers->nelts);
}
#if 1
if (headers->nalloc == 0) {
if (ngx_array_init(headers, r->pool, 2,
sizeof(ngx_table_elt_t *))
!= NGX_OK)
{
return NGX_ERROR;
}
}
#endif
if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) {
return NGX_ERROR;
}
if (value->len == 0) {
return NGX_OK;
}
dd("new multi-value header: %p", h);
v = ngx_array_push(headers);
if (v == NULL) {
return NGX_ERROR;
}
*v = h;
return NGX_OK;
#endif
}
static ngx_int_t
ngx_http_clear_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
r->headers_in.content_length_n = -1;
return ngx_http_clear_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_clear_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
value->len = 0;
return ngx_http_set_builtin_header(r, hv, value);
}
ngx_int_t
ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key,
ngx_str_t value, unsigned override)
{
ngx_http_lua_header_val_t hv;
ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers;
ngx_int_t rc;
ngx_uint_t i;
dd("set header value: %.*s", (int) value.len, value.data);
rc = ngx_http_lua_copy_escaped_header(r, &key, 1);
if (rc != NGX_OK) {
return NGX_ERROR;
}
rc = ngx_http_lua_copy_escaped_header(r, &value, 0);
if (rc != NGX_OK) {
return NGX_ERROR;
}
if (value.len > 0) {
hv.hash = ngx_hash_key_lc(key.data, key.len);
} else {
hv.hash = 0;
}
hv.key = key;
hv.offset = 0;
hv.no_override = !override;
hv.handler = NULL;
for (i = 0; handlers[i].name.len; i++) {
if (hv.key.len != handlers[i].name.len
|| ngx_strncasecmp(hv.key.data, handlers[i].name.data,
handlers[i].name.len) != 0)
{
dd("hv key comparison: %s <> %s", handlers[i].name.data,
hv.key.data);
continue;
}
dd("Matched handler: %s %s", handlers[i].name.data, hv.key.data);
hv.offset = handlers[i].offset;
hv.handler = handlers[i].handler;
break;
}
if (handlers[i].name.len == 0 && handlers[i].handler) {
hv.offset = handlers[i].offset;
hv.handler = handlers[i].handler;
}
#if 1
if (hv.handler == NULL) {
return NGX_ERROR;
}
#endif
if (r->headers_out.status == 400 || r->headers_in.headers.last == NULL) {
/* must be a 400 Bad Request */
return NGX_OK;
}
return hv.handler(r, &hv, &value);
}
static ngx_int_t
ngx_http_lua_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur,
ngx_uint_t i)
{
ngx_table_elt_t *data;
ngx_list_part_t *new, *part;
dd("list rm item: part %p, i %d, nalloc %d", cur, (int) i,
(int) l->nalloc);
data = cur->elts;
dd("cur: nelts %d, nalloc %d", (int) cur->nelts,
(int) l->nalloc);
dd("removing: \"%.*s:%.*s\"", (int) data[i].key.len, data[i].key.data,
(int) data[i].value.len, data[i].value.data);
if (i == 0) {
dd("first entry in the part");
cur->elts = (char *) cur->elts + l->size;
cur->nelts--;
if (cur == l->last) {
dd("being the last part");
if (cur->nelts == 0) {
#if 1
part = &l->part;
dd("cur=%p, part=%p, part next=%p, last=%p",
cur, part, part->next, l->last);
if (part == cur) {
cur->elts = (char *) cur->elts - l->size;
/* do nothing */
} else {
while (part->next != cur) {
if (part->next == NULL) {
return NGX_ERROR;
}
part = part->next;
}
l->last = part;
part->next = NULL;
dd("part nelts: %d", (int) part->nelts);
l->nalloc = part->nelts;
}
#endif
} else {
l->nalloc--;
dd("nalloc decreased: %d", (int) l->nalloc);
}
return NGX_OK;
}
if (cur->nelts == 0) {
dd("current part is empty");
part = &l->part;
if (part == cur) {
ngx_http_lua_assert(cur->next != NULL);
dd("remove 'cur' from the list by rewriting 'cur': "
"l->last: %p, cur: %p, cur->next: %p, part: %p",
l->last, cur, cur->next, part);
if (l->last == cur->next) {
dd("last is cur->next");
l->part = *(cur->next);
l->last = part;
l->nalloc = part->nelts;
} else {
l->part = *(cur->next);
}
} else {
dd("remove 'cur' from the list");
while (part->next != cur) {
if (part->next == NULL) {
return NGX_ERROR;
}
part = part->next;
}
part->next = cur->next;
}
return NGX_OK;
}
return NGX_OK;
}
if (i == cur->nelts - 1) {
dd("last entry in the part");
cur->nelts--;
if (cur == l->last) {
l->nalloc--;
}
return NGX_OK;
}
dd("the middle entry in the part");
new = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
if (new == NULL) {
return NGX_ERROR;
}
new->elts = &data[i + 1];
new->nelts = cur->nelts - i - 1;
new->next = cur->next;
cur->nelts = i;
cur->next = new;
if (cur == l->last) {
l->last = new;
l->nalloc = new->nelts;
}
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_
#define _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_
#include <nginx.h>
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key,
ngx_str_t value, unsigned override);
#endif /* _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,742 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include "ngx_http_lua_headers_out.h"
#include "ngx_http_lua_util.h"
#include <ctype.h>
static ngx_int_t ngx_http_set_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value,
ngx_table_elt_t **output_header, unsigned no_create);
static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_clear_last_modified_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_location_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
static ngx_http_lua_set_header_t ngx_http_lua_set_handlers[] = {
{ ngx_string("Server"),
offsetof(ngx_http_headers_out_t, server),
ngx_http_set_builtin_header },
{ ngx_string("Date"),
offsetof(ngx_http_headers_out_t, date),
ngx_http_set_builtin_header },
#if 1
{ ngx_string("Content-Encoding"),
offsetof(ngx_http_headers_out_t, content_encoding),
ngx_http_set_builtin_header },
#endif
{ ngx_string("Location"),
offsetof(ngx_http_headers_out_t, location),
ngx_http_set_location_header },
{ ngx_string("Refresh"),
offsetof(ngx_http_headers_out_t, refresh),
ngx_http_set_builtin_header },
{ ngx_string("Last-Modified"),
offsetof(ngx_http_headers_out_t, last_modified),
ngx_http_set_last_modified_header },
{ ngx_string("Content-Range"),
offsetof(ngx_http_headers_out_t, content_range),
ngx_http_set_builtin_header },
{ ngx_string("Accept-Ranges"),
offsetof(ngx_http_headers_out_t, accept_ranges),
ngx_http_set_builtin_header },
{ ngx_string("WWW-Authenticate"),
offsetof(ngx_http_headers_out_t, www_authenticate),
ngx_http_set_builtin_header },
{ ngx_string("Expires"),
offsetof(ngx_http_headers_out_t, expires),
ngx_http_set_builtin_header },
{ ngx_string("E-Tag"),
offsetof(ngx_http_headers_out_t, etag),
ngx_http_set_builtin_header },
{ ngx_string("ETag"),
offsetof(ngx_http_headers_out_t, etag),
ngx_http_set_builtin_header },
{ ngx_string("Content-Length"),
offsetof(ngx_http_headers_out_t, content_length),
ngx_http_set_content_length_header },
{ ngx_string("Content-Type"),
offsetof(ngx_http_headers_out_t, content_type),
ngx_http_set_content_type_header },
{ ngx_string("Cache-Control"),
offsetof(ngx_http_headers_out_t, cache_control),
ngx_http_set_builtin_multi_header },
#if (nginx_version >= 1013009)
{ ngx_string("Link"),
offsetof(ngx_http_headers_out_t, link),
ngx_http_set_builtin_multi_header },
#endif
{ ngx_null_string, 0, ngx_http_set_header }
};
/* request time implementation */
static ngx_int_t
ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
ngx_str_t *value)
{
return ngx_http_set_header_helper(r, hv, value, NULL, 0);
}
static ngx_int_t
ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
ngx_str_t *value, ngx_table_elt_t **output_header,
unsigned no_create)
{
ngx_table_elt_t *h;
ngx_list_part_t *part;
ngx_uint_t i;
unsigned matched = 0;
if (hv->no_override) {
goto new_header;
}
#if 1
if (r->headers_out.location
&& r->headers_out.location->value.len
&& r->headers_out.location->value.data[0] == '/')
{
/* XXX ngx_http_core_find_config_phase, for example,
* may not initialize the "key" and "hash" fields
* for a nasty optimization purpose, and
* we have to work-around it here */
r->headers_out.location->hash = ngx_http_lua_location_hash;
ngx_str_set(&r->headers_out.location->key, "Location");
}
#endif
part = &r->headers_out.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
if (h[i].hash != 0
&& h[i].key.len == hv->key.len
&& ngx_strncasecmp(hv->key.data, h[i].key.data, h[i].key.len) == 0)
{
dd("found out header %.*s", (int) h[i].key.len, h[i].key.data);
if (value->len == 0 || matched) {
dd("clearing normal header for %.*s", (int) hv->key.len,
hv->key.data);
h[i].value.len = 0;
h[i].hash = 0;
} else {
dd("setting header to value %.*s", (int) value->len,
value->data);
h[i].value = *value;
h[i].hash = hv->hash;
}
if (output_header) {
*output_header = &h[i];
}
/* return NGX_OK; */
matched = 1;
}
}
if (matched){
return NGX_OK;
}
if (no_create && value->len == 0) {
return NGX_OK;
}
new_header:
/* XXX we still need to create header slot even if the value
* is empty because some builtin headers like Last-Modified
* relies on this to get cleared */
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
if (value->len == 0) {
h->hash = 0;
} else {
h->hash = hv->hash;
}
h->key = hv->key;
h->value = *value;
h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
if (h->lowcase_key == NULL) {
return NGX_ERROR;
}
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
if (output_header) {
*output_header = h;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_set_location_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
ngx_int_t rc;
ngx_table_elt_t *h;
rc = ngx_http_set_builtin_header(r, hv, value);
if (rc != NGX_OK) {
return rc;
}
/*
* we do not set r->headers_out.location here to avoid the handling
* the local redirects without a host name by ngx_http_header_filter()
*/
h = r->headers_out.location;
if (h && h->value.len && h->value.data[0] == '/') {
r->headers_out.location = NULL;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_set_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
ngx_table_elt_t *h, **old;
if (hv->offset) {
old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
} else {
old = NULL;
}
if (old == NULL || *old == NULL) {
return ngx_http_set_header_helper(r, hv, value, old, 0);
}
h = *old;
if (value->len == 0) {
dd("clearing the builtin header");
h->hash = 0;
h->value = *value;
return NGX_OK;
}
h->hash = hv->hash;
h->key = hv->key;
h->value = *value;
return NGX_OK;
}
static ngx_int_t
ngx_http_set_builtin_multi_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
#if defined(nginx_version) && nginx_version >= 1023000
ngx_table_elt_t **headers, *h, *ho, **ph;
headers = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
if (hv->no_override) {
for (h = *headers; h; h = h->next) {
if (!h->hash) {
h->value = *value;
h->hash = hv->hash;
return NGX_OK;
}
}
goto create;
}
/* override old values (if any) */
if (*headers) {
for (h = (*headers)->next; h; h = h->next) {
h->hash = 0;
h->value.len = 0;
}
h = *headers;
h->value = *value;
if (value->len == 0) {
h->hash = 0;
} else {
h->hash = hv->hash;
}
return NGX_OK;
}
create:
for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ }
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
ho->value = *value;
if (value->len == 0) {
ho->hash = 0;
} else {
ho->hash = hv->hash;
}
ho->key = hv->key;
ho->next = NULL;
*ph = ho;
return NGX_OK;
#else
ngx_array_t *pa;
ngx_table_elt_t *ho, **ph;
ngx_uint_t i;
pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset);
if (pa->elts == NULL) {
if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *))
!= NGX_OK)
{
return NGX_ERROR;
}
}
if (hv->no_override) {
ph = pa->elts;
for (i = 0; i < pa->nelts; i++) {
if (!ph[i]->hash) {
ph[i]->value = *value;
ph[i]->hash = hv->hash;
return NGX_OK;
}
}
goto create;
}
/* override old values (if any) */
if (pa->nelts > 0) {
ph = pa->elts;
for (i = 1; i < pa->nelts; i++) {
ph[i]->hash = 0;
ph[i]->value.len = 0;
}
ph[0]->value = *value;
if (value->len == 0) {
ph[0]->hash = 0;
} else {
ph[0]->hash = hv->hash;
}
return NGX_OK;
}
create:
ph = ngx_array_push(pa);
if (ph == NULL) {
return NGX_ERROR;
}
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
ho->value = *value;
if (value->len == 0) {
ho->hash = 0;
} else {
ho->hash = hv->hash;
}
ho->key = hv->key;
*ph = ho;
return NGX_OK;
#endif
}
static ngx_int_t
ngx_http_set_content_type_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
ngx_uint_t i;
r->headers_out.content_type_len = value->len;
#if 1
for (i = 0; i < value->len; i++) {
if (value->data[i] == ';') {
r->headers_out.content_type_len = i;
break;
}
}
#endif
r->headers_out.content_type = *value;
r->headers_out.content_type_hash = hv->hash;
r->headers_out.content_type_lowcase = NULL;
value->len = 0;
return ngx_http_set_header_helper(r, hv, value, NULL, 1);
}
static ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
if (value->len == 0) {
return ngx_http_clear_last_modified_header(r, hv, value);
}
r->headers_out.last_modified_time = ngx_http_parse_time(value->data,
value->len);
dd("last modified time: %d", (int) r->headers_out.last_modified_time);
return ngx_http_set_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_clear_last_modified_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
r->headers_out.last_modified_time = -1;
return ngx_http_clear_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_set_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
off_t len;
if (value->len == 0) {
return ngx_http_clear_content_length_header(r, hv, value);
}
len = ngx_atoof(value->data, value->len);
if (len == NGX_ERROR) {
return NGX_ERROR;
}
r->headers_out.content_length_n = len;
return ngx_http_set_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_clear_content_length_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
r->headers_out.content_length_n = -1;
return ngx_http_clear_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_clear_builtin_header(ngx_http_request_t *r,
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
{
value->len = 0;
return ngx_http_set_builtin_header(r, hv, value);
}
ngx_int_t
ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
ngx_str_t key, ngx_str_t value, unsigned override)
{
ngx_http_lua_header_val_t hv;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_set_header_t *lsh;
ngx_hash_t *hash;
dd("set header value: %.*s", (int) value.len, value.data);
if (ngx_http_lua_copy_escaped_header(r, &key, 1) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_http_lua_copy_escaped_header(r, &value, 0) != NGX_OK) {
return NGX_ERROR;
}
hv.hash = ngx_hash_key_lc(key.data, key.len);
hv.key = key;
hv.offset = 0;
hv.no_override = !override;
hv.handler = ngx_http_set_header;
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
hash = &lmcf->builtin_headers_out;
lsh = ngx_http_lua_hash_find_lc(hash, hv.hash, hv.key.data, hv.key.len);
if (lsh) {
dd("Matched handler: %s %s", lsh->name.data, hv.key.data);
hv.offset = lsh->offset;
hv.handler = lsh->handler;
if (hv.handler == ngx_http_set_content_type_header) {
ctx->mime_set = 1;
}
}
return hv.handler(r, &hv, &value);
}
int
ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, ngx_str_t *key)
{
ngx_table_elt_t *h;
ngx_list_part_t *part;
ngx_uint_t i;
unsigned found;
dd("looking for response header \"%.*s\"", (int) key->len, key->data);
switch (key->len) {
case 14:
if (r->headers_out.content_length == NULL
&& r->headers_out.content_length_n >= 0
&& ngx_strncasecmp(key->data, (u_char *) "Content-Length", 14) == 0)
{
lua_pushinteger(L, (lua_Integer) r->headers_out.content_length_n);
return 1;
}
break;
case 12:
if (ngx_strncasecmp(key->data, (u_char *) "Content-Type", 12) == 0
&& r->headers_out.content_type.len)
{
lua_pushlstring(L, (char *) r->headers_out.content_type.data,
r->headers_out.content_type.len);
return 1;
}
break;
default:
break;
}
dd("not a built-in output header");
found = 0;
#if 1
if (r->headers_out.location
&& r->headers_out.location->value.len
&& r->headers_out.location->value.data[0] == '/')
{
/* XXX ngx_http_core_find_config_phase, for example,
* may not initialize the "key" and "hash" fields
* for a nasty optimization purpose, and
* we have to work-around it here */
r->headers_out.location->hash = ngx_http_lua_location_hash;
ngx_str_set(&r->headers_out.location->key, "Location");
}
#endif
part = &r->headers_out.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
if (h[i].hash == 0) {
continue;
}
if (h[i].hash != 0
&& h[i].key.len == key->len
&& ngx_strncasecmp(key->data, h[i].key.data, h[i].key.len) == 0)
{
if (!found) {
found = 1;
lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len);
continue;
}
if (found == 1) {
lua_createtable(L, 4 /* narr */, 0);
lua_insert(L, -2);
lua_rawseti(L, -2, found);
}
found++;
lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len);
lua_rawseti(L, -2, found);
}
}
if (found) {
return 1;
}
lua_pushnil(L);
return 1;
}
ngx_int_t
ngx_http_lua_init_builtin_headers_out(ngx_conf_t *cf,
ngx_http_lua_main_conf_t *lmcf)
{
ngx_array_t headers;
ngx_hash_key_t *hk;
ngx_hash_init_t hash;
ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers;
ngx_uint_t count;
count = sizeof(ngx_http_lua_set_handlers)
/ sizeof(ngx_http_lua_set_header_t);
if (ngx_array_init(&headers, cf->temp_pool, count, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_ERROR;
}
while (handlers->name.data) {
hk = ngx_array_push(&headers);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key = handlers->name;
hk->key_hash = ngx_hash_key_lc(handlers->name.data, handlers->name.len);
hk->value = (void *) handlers;
handlers++;
}
hash.hash = &lmcf->builtin_headers_out;
hash.key = ngx_hash_key_lc;
hash.max_size = 512;
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
hash.name = "builtin_headers_out_hash";
hash.pool = cf->pool;
hash.temp_pool = NULL;
return ngx_hash_init(&hash, headers.elts, headers.nelts);
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_
#define _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_
#include "ngx_http_lua_common.h"
#if (NGX_DARWIN)
typedef struct {
ngx_http_request_t *r;
const char *key_data;
size_t key_len;
int is_nil;
const char *sval;
size_t sval_len;
void *mvals;
size_t mvals_len;
int override;
char **errmsg;
} ngx_http_lua_set_resp_header_params_t;
#endif
ngx_int_t ngx_http_lua_set_output_header(ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, ngx_str_t key, ngx_str_t value, unsigned override);
int ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, ngx_str_t *key);
ngx_int_t ngx_http_lua_init_builtin_headers_out(ngx_conf_t *cf,
ngx_http_lua_main_conf_t *lmcf);
#endif /* _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_initby.h"
#include "ngx_http_lua_util.h"
ngx_int_t
ngx_http_lua_init_by_inline(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,
lua_State *L)
{
int status;
const char *chunkname;
if (lmcf->init_chunkname == NULL) {
chunkname = "=init_by_lua";
} else {
chunkname = (const char *) lmcf->init_chunkname;
}
status = luaL_loadbuffer(L, (char *) lmcf->init_src.data,
lmcf->init_src.len, chunkname)
|| ngx_http_lua_do_call(log, L);
return ngx_http_lua_report(log, L, status, "init_by_lua");
}
ngx_int_t
ngx_http_lua_init_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,
lua_State *L)
{
int status;
status = luaL_loadfile(L, (char *) lmcf->init_src.data)
|| ngx_http_lua_do_call(log, L);
return ngx_http_lua_report(log, L, status, "init_by_lua_file");
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_INITBY_H_INCLUDED_
#define _NGX_HTTP_LUA_INITBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_init_by_inline(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L);
ngx_int_t ngx_http_lua_init_by_file(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L);
#endif /* _NGX_HTTP_LUA_INITBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,367 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_initworkerby.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_pipe.h"
static u_char *ngx_http_lua_log_init_worker_error(ngx_log_t *log,
u_char *buf, size_t len);
ngx_int_t
ngx_http_lua_init_worker(ngx_cycle_t *cycle)
{
char *rv;
void *cur, *prev;
ngx_uint_t i;
ngx_conf_t conf;
ngx_conf_file_t cf_file;
ngx_cycle_t *fake_cycle;
ngx_module_t **modules;
ngx_open_file_t *file, *ofile;
ngx_list_part_t *part;
ngx_connection_t *c = NULL;
ngx_http_module_t *module;
ngx_http_request_t *r = NULL;
ngx_http_lua_ctx_t *ctx;
ngx_http_conf_ctx_t *conf_ctx, http_ctx;
ngx_http_lua_loc_conf_t *top_llcf;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_core_loc_conf_t *clcf, *top_clcf;
lmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lua_module);
if (lmcf == NULL || lmcf->lua == NULL) {
return NGX_OK;
}
/* lmcf != NULL && lmcf->lua != NULL */
#if !(NGX_WIN32)
if (ngx_process == NGX_PROCESS_HELPER
# ifdef HAVE_PRIVILEGED_PROCESS_PATCH
&& !ngx_is_privileged_agent
# endif
)
{
/* disable init_worker_by_lua* and destroy lua VM in cache processes */
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"lua close the global Lua VM %p in the "
"cache helper process %P", lmcf->lua, ngx_pid);
lmcf->vm_cleanup->handler(lmcf->vm_cleanup->data);
lmcf->vm_cleanup->handler = NULL;
return NGX_OK;
}
# ifdef HAVE_NGX_LUA_PIPE
if (ngx_http_lua_pipe_add_signal_handler(cycle) != NGX_OK) {
return NGX_ERROR;
}
# endif
#endif /* NGX_WIN32 */
#if (NGX_HTTP_LUA_HAVE_SA_RESTART)
if (lmcf->set_sa_restart) {
ngx_http_lua_set_sa_restart(ngx_cycle->log);
}
#endif
if (lmcf->init_worker_handler == NULL) {
return NGX_OK;
}
conf_ctx = (ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index];
http_ctx.main_conf = conf_ctx->main_conf;
top_clcf = conf_ctx->loc_conf[ngx_http_core_module.ctx_index];
top_llcf = conf_ctx->loc_conf[ngx_http_lua_module.ctx_index];
ngx_memzero(&conf, sizeof(ngx_conf_t));
conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, cycle->log);
if (conf.temp_pool == NULL) {
return NGX_ERROR;
}
conf.temp_pool->log = cycle->log;
/* we fake a temporary ngx_cycle_t here because some
* modules' merge conf handler may produce side effects in
* cf->cycle (like ngx_proxy vs cf->cycle->paths).
* also, we cannot allocate our temp cycle on the stack
* because some modules like ngx_http_core_module reference
* addresses within cf->cycle (i.e., via "&cf->cycle->new_log")
*/
fake_cycle = ngx_palloc(cycle->pool, sizeof(ngx_cycle_t));
if (fake_cycle == NULL) {
goto failed;
}
ngx_memcpy(fake_cycle, cycle, sizeof(ngx_cycle_t));
ngx_queue_init(&fake_cycle->reusable_connections_queue);
if (ngx_array_init(&fake_cycle->listening, cycle->pool,
cycle->listening.nelts ? cycle->listening.nelts : 1,
sizeof(ngx_listening_t))
!= NGX_OK)
{
goto failed;
}
if (ngx_array_init(&fake_cycle->paths, cycle->pool,
cycle->paths.nelts ? cycle->paths.nelts : 1,
sizeof(ngx_path_t *))
!= NGX_OK)
{
goto failed;
}
part = &cycle->open_files.part;
ofile = part->elts;
if (ngx_list_init(&fake_cycle->open_files, cycle->pool,
part->nelts ? part->nelts : 1,
sizeof(ngx_open_file_t))
!= NGX_OK)
{
goto failed;
}
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
ofile = part->elts;
i = 0;
}
file = ngx_list_push(&fake_cycle->open_files);
if (file == NULL) {
goto failed;
}
ngx_memcpy(file, ofile, sizeof(ngx_open_file_t));
}
if (ngx_list_init(&fake_cycle->shared_memory, cycle->pool, 1,
sizeof(ngx_shm_zone_t))
!= NGX_OK)
{
goto failed;
}
conf.ctx = &http_ctx;
conf.cycle = fake_cycle;
conf.pool = fake_cycle->pool;
conf.log = cycle->log;
ngx_memzero(&cf_file, sizeof(cf_file));
cf_file.file.name = cycle->conf_file;
conf.conf_file = &cf_file;
http_ctx.loc_conf = ngx_pcalloc(conf.pool,
sizeof(void *) * ngx_http_max_module);
if (http_ctx.loc_conf == NULL) {
return NGX_ERROR;
}
http_ctx.srv_conf = ngx_pcalloc(conf.pool,
sizeof(void *) * ngx_http_max_module);
if (http_ctx.srv_conf == NULL) {
return NGX_ERROR;
}
#if (nginx_version >= 1009011)
modules = cycle->modules;
#else
modules = ngx_modules;
#endif
for (i = 0; modules[i]; i++) {
if (modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = modules[i]->ctx;
if (module->create_srv_conf) {
cur = module->create_srv_conf(&conf);
if (cur == NULL) {
return NGX_ERROR;
}
http_ctx.srv_conf[modules[i]->ctx_index] = cur;
if (module->merge_srv_conf) {
prev = module->create_srv_conf(&conf);
if (prev == NULL) {
return NGX_ERROR;
}
rv = module->merge_srv_conf(&conf, prev, cur);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
}
if (module->create_loc_conf) {
cur = module->create_loc_conf(&conf);
if (cur == NULL) {
return NGX_ERROR;
}
http_ctx.loc_conf[modules[i]->ctx_index] = cur;
if (module->merge_loc_conf) {
if (modules[i] == &ngx_http_lua_module) {
prev = top_llcf;
} else if (modules[i] == &ngx_http_core_module) {
prev = top_clcf;
} else {
prev = module->create_loc_conf(&conf);
if (prev == NULL) {
return NGX_ERROR;
}
}
rv = module->merge_loc_conf(&conf, prev, cur);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
}
}
ngx_destroy_pool(conf.temp_pool);
conf.temp_pool = NULL;
c = ngx_http_lua_create_fake_connection(NULL);
if (c == NULL) {
goto failed;
}
c->log->handler = ngx_http_lua_log_init_worker_error;
r = ngx_http_lua_create_fake_request(c);
if (r == NULL) {
goto failed;
}
r->main_conf = http_ctx.main_conf;
r->srv_conf = http_ctx.srv_conf;
r->loc_conf = http_ctx.loc_conf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
#if (nginx_version >= 1009000)
ngx_set_connection_log(r->connection, clcf->error_log);
#else
ngx_http_set_connection_log(r->connection, clcf->error_log);
#endif
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
goto failed;
}
ctx->context = NGX_HTTP_LUA_CONTEXT_INIT_WORKER;
ctx->cur_co_ctx = NULL;
r->read_event_handler = ngx_http_block_reading;
ngx_http_lua_set_req(lmcf->lua, r);
(void) lmcf->init_worker_handler(cycle->log, lmcf, lmcf->lua);
ngx_destroy_pool(c->pool);
return NGX_OK;
failed:
if (conf.temp_pool) {
ngx_destroy_pool(conf.temp_pool);
}
if (c) {
ngx_http_lua_close_fake_connection(c);
}
return NGX_ERROR;
}
ngx_int_t
ngx_http_lua_init_worker_by_inline(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L)
{
int status;
const char *chunkname;
if (lmcf->init_worker_chunkname == NULL) {
chunkname = "=init_worker_by_lua";
} else {
chunkname = (const char *) lmcf->init_worker_chunkname;
}
status = luaL_loadbuffer(L, (char *) lmcf->init_worker_src.data,
lmcf->init_worker_src.len, chunkname)
|| ngx_http_lua_do_call(log, L);
return ngx_http_lua_report(log, L, status, "init_worker_by_lua");
}
ngx_int_t
ngx_http_lua_init_worker_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,
lua_State *L)
{
int status;
status = luaL_loadfile(L, (char *) lmcf->init_worker_src.data)
|| ngx_http_lua_do_call(log, L);
return ngx_http_lua_report(log, L, status, "init_worker_by_lua_file");
}
static u_char *
ngx_http_lua_log_init_worker_error(ngx_log_t *log, u_char *buf, size_t len)
{
u_char *p;
if (log->action) {
p = ngx_snprintf(buf, len, " while %s", log->action);
len -= p - buf;
buf = p;
}
return ngx_snprintf(buf, len, ", context: init_worker_by_lua*");
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_INITWORKERBY_H_INCLUDED_
#define _NGX_HTTP_LUA_INITWORKERBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_init_worker_by_inline(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L);
ngx_int_t ngx_http_lua_init_worker_by_file(ngx_log_t *log,
ngx_http_lua_main_conf_t *lmcf, lua_State *L);
ngx_int_t ngx_http_lua_init_worker(ngx_cycle_t *cycle);
#endif /* _NGX_HTTP_LUA_INITWORKERBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) by OpenResty Inc.
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_common.h"
ngx_int_t
ngx_http_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *rest,
ssize_t bytes, ngx_log_t *log)
{
if (bytes == 0) {
return NGX_ERROR;
}
if ((size_t) bytes >= *rest) {
buf_in->buf->last += *rest;
src->pos += *rest;
*rest = 0;
return NGX_OK;
}
/* bytes < *rest */
buf_in->buf->last += bytes;
src->pos += bytes;
*rest -= bytes;
return NGX_AGAIN;
}
ngx_int_t
ngx_http_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in, ssize_t bytes,
ngx_log_t *log)
{
if (bytes == 0) {
return NGX_OK;
}
buf_in->buf->last += bytes;
src->pos += bytes;
return NGX_AGAIN;
}
ngx_int_t
ngx_http_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *max,
ssize_t bytes, ngx_log_t *log)
{
if (bytes == 0) {
return NGX_ERROR;
}
if (bytes >= (ssize_t) *max) {
bytes = (ssize_t) *max;
}
buf_in->buf->last += bytes;
src->pos += bytes;
return NGX_OK;
}
ngx_int_t
ngx_http_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in, ssize_t bytes,
ngx_log_t *log)
{
u_char *dst;
u_char c;
#if (NGX_DEBUG)
u_char *begin;
#endif
#if (NGX_DEBUG)
begin = src->pos;
#endif
if (bytes == 0) {
return NGX_ERROR;
}
dd("already read: %p: %.*s", buf_in,
(int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos);
dd("data read: %.*s", (int) bytes, src->pos);
dst = buf_in->buf->last;
while (bytes--) {
c = *src->pos++;
switch (c) {
case '\n':
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"lua read the final line part: \"%*s\"",
src->pos - 1 - begin, begin);
buf_in->buf->last = dst;
dd("read a line: %p: %.*s", buf_in,
(int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos);
return NGX_OK;
case '\r':
/* ignore it */
break;
default:
*dst++ = c;
break;
}
}
#if (NGX_DEBUG)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"lua read partial line data: %*s", dst - begin, begin);
#endif
buf_in->buf->last = dst;
return NGX_AGAIN;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) by OpenResty Inc.
*/
#ifndef _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_
#define _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in,
size_t *rest, ssize_t bytes, ngx_log_t *log);
ngx_int_t ngx_http_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in,
ssize_t bytes, ngx_log_t *log);
ngx_int_t ngx_http_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in,
size_t *max, ssize_t bytes, ngx_log_t *log);
ngx_int_t ngx_http_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in,
ssize_t bytes, ngx_log_t *log);
#endif /* _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_LEX_H_INCLUDED_
#define _NGX_HTTP_LUA_LEX_H_INCLUDED_
#include "ngx_http_lua_common.h"
int ngx_http_lua_lex(const u_char *const s, size_t len, int *const ovec);
#endif

View File

@ -0,0 +1,462 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_log.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_log_ringbuf.h"
#include "ngx_http_lua_output.h"
static int ngx_http_lua_print(lua_State *L);
static int ngx_http_lua_ngx_log(lua_State *L);
static int log_wrapper(ngx_log_t *log, const char *ident,
ngx_uint_t level, lua_State *L);
static void ngx_http_lua_inject_log_consts(lua_State *L);
/**
* Wrapper of nginx log functionality. Take a log level param and varargs of
* log message params.
*
* @param L Lua state pointer
* @retval always 0 (don't return values to Lua)
* */
int
ngx_http_lua_ngx_log(lua_State *L)
{
ngx_log_t *log;
ngx_http_request_t *r;
const char *msg;
int level;
r = ngx_http_lua_get_req(L);
if (r && r->connection && r->connection->log) {
log = r->connection->log;
} else {
log = ngx_cycle->log;
}
level = luaL_checkint(L, 1);
if (level < NGX_LOG_STDERR || level > NGX_LOG_DEBUG) {
msg = lua_pushfstring(L, "bad log level: %d", level);
return luaL_argerror(L, 1, msg);
}
/* remove log-level param from stack */
lua_remove(L, 1);
return log_wrapper(log, "[lua] ", (ngx_uint_t) level, L);
}
/**
* Override Lua print function, output message to nginx error logs. Equal to
* ngx.log(ngx.NOTICE, ...).
*
* @param L Lua state pointer
* @retval always 0 (don't return values to Lua)
* */
int
ngx_http_lua_print(lua_State *L)
{
ngx_log_t *log;
ngx_http_request_t *r;
r = ngx_http_lua_get_req(L);
if (r && r->connection && r->connection->log) {
log = r->connection->log;
} else {
log = ngx_cycle->log;
}
return log_wrapper(log, "[lua] ", NGX_LOG_NOTICE, L);
}
static int
log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level,
lua_State *L)
{
u_char *buf;
u_char *p, *q;
ngx_str_t name;
int nargs, i;
size_t size, len;
size_t src_len = 0;
int type;
const char *msg;
lua_Debug ar;
if (level > log->log_level) {
return 0;
}
#if 1
/* add debug info */
lua_getstack(L, 1, &ar);
lua_getinfo(L, "Snl", &ar);
/* get the basename of the Lua source file path, stored in q */
name.data = (u_char *) ar.short_src;
if (name.data == NULL) {
name.len = 0;
} else {
p = name.data;
while (*p != '\0') {
if (*p == '/' || *p == '\\') {
name.data = p + 1;
}
p++;
}
name.len = p - name.data;
}
#endif
nargs = lua_gettop(L);
size = name.len + NGX_INT_T_LEN + sizeof(":: ") - 1;
if (*ar.namewhat != '\0' && *ar.what == 'L') {
src_len = ngx_strlen(ar.name);
size += src_len + sizeof("(): ") - 1;
}
for (i = 1; i <= nargs; i++) {
type = lua_type(L, i);
switch (type) {
case LUA_TNUMBER:
size += ngx_http_lua_get_num_len(L, i);
break;
case LUA_TSTRING:
lua_tolstring(L, i, &len);
size += len;
break;
case LUA_TNIL:
size += sizeof("nil") - 1;
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, i)) {
size += sizeof("true") - 1;
} else {
size += sizeof("false") - 1;
}
break;
case LUA_TTABLE:
if (!luaL_callmeta(L, i, "__tostring")) {
return luaL_argerror(L, i, "expected table to have "
"__tostring metamethod");
}
lua_tolstring(L, -1, &len);
size += len;
break;
case LUA_TLIGHTUSERDATA:
if (lua_touserdata(L, i) == NULL) {
size += sizeof("null") - 1;
break;
}
continue;
default:
msg = lua_pushfstring(L, "string, number, boolean, or nil "
"expected, got %s",
lua_typename(L, type));
return luaL_argerror(L, i, msg);
}
}
buf = lua_newuserdata(L, size);
p = ngx_copy(buf, name.data, name.len);
*p++ = ':';
p = ngx_snprintf(p, NGX_INT_T_LEN, "%d",
ar.currentline > 0 ? ar.currentline : ar.linedefined);
*p++ = ':'; *p++ = ' ';
if (*ar.namewhat != '\0' && *ar.what == 'L') {
p = ngx_copy(p, ar.name, src_len);
*p++ = '(';
*p++ = ')';
*p++ = ':';
*p++ = ' ';
}
for (i = 1; i <= nargs; i++) {
type = lua_type(L, i);
switch (type) {
case LUA_TNUMBER:
p = ngx_http_lua_write_num(L, i, p);
break;
case LUA_TSTRING:
q = (u_char *) lua_tolstring(L, i, &len);
p = ngx_copy(p, q, len);
break;
case LUA_TNIL:
*p++ = 'n';
*p++ = 'i';
*p++ = 'l';
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, i)) {
*p++ = 't';
*p++ = 'r';
*p++ = 'u';
*p++ = 'e';
} else {
*p++ = 'f';
*p++ = 'a';
*p++ = 'l';
*p++ = 's';
*p++ = 'e';
}
break;
case LUA_TTABLE:
luaL_callmeta(L, i, "__tostring");
q = (u_char *) lua_tolstring(L, -1, &len);
p = ngx_copy(p, q, len);
break;
case LUA_TLIGHTUSERDATA:
*p++ = 'n';
*p++ = 'u';
*p++ = 'l';
*p++ = 'l';
break;
default:
return luaL_error(L, "impossible to reach here");
}
}
if (p - buf > (off_t) size) {
return luaL_error(L, "buffer error: %d > %d", (int) (p - buf),
(int) size);
}
ngx_log_error(level, log, 0, "%s%*s", ident, (size_t) (p - buf), buf);
return 0;
}
void
ngx_http_lua_inject_log_api(lua_State *L)
{
ngx_http_lua_inject_log_consts(L);
lua_pushcfunction(L, ngx_http_lua_ngx_log);
lua_setfield(L, -2, "log");
lua_pushcfunction(L, ngx_http_lua_print);
lua_setglobal(L, "print");
}
static void
ngx_http_lua_inject_log_consts(lua_State *L)
{
/* {{{ nginx log level constants */
lua_pushinteger(L, NGX_LOG_STDERR);
lua_setfield(L, -2, "STDERR");
lua_pushinteger(L, NGX_LOG_EMERG);
lua_setfield(L, -2, "EMERG");
lua_pushinteger(L, NGX_LOG_ALERT);
lua_setfield(L, -2, "ALERT");
lua_pushinteger(L, NGX_LOG_CRIT);
lua_setfield(L, -2, "CRIT");
lua_pushinteger(L, NGX_LOG_ERR);
lua_setfield(L, -2, "ERR");
lua_pushinteger(L, NGX_LOG_WARN);
lua_setfield(L, -2, "WARN");
lua_pushinteger(L, NGX_LOG_NOTICE);
lua_setfield(L, -2, "NOTICE");
lua_pushinteger(L, NGX_LOG_INFO);
lua_setfield(L, -2, "INFO");
lua_pushinteger(L, NGX_LOG_DEBUG);
lua_setfield(L, -2, "DEBUG");
/* }}} */
}
#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
ngx_int_t
ngx_http_lua_capture_log_handler(ngx_log_t *log,
ngx_uint_t level, u_char *buf, size_t n)
{
ngx_http_lua_log_ringbuf_t *ringbuf;
dd("enter");
ringbuf = (ngx_http_lua_log_ringbuf_t *)
ngx_cycle->intercept_error_log_data;
if (level > ringbuf->filter_level) {
return NGX_OK;
}
ngx_http_lua_log_ringbuf_write(ringbuf, level, buf, n);
dd("capture log: %s\n", buf);
return NGX_OK;
}
#endif
int
ngx_http_lua_ffi_errlog_set_filter_level(int level, u_char *err, size_t *errlen)
{
#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
ngx_http_lua_log_ringbuf_t *ringbuf;
ringbuf = ngx_cycle->intercept_error_log_data;
if (ringbuf == NULL) {
*errlen = ngx_snprintf(err, *errlen,
"directive \"lua_capture_error_log\" is not set")
- err;
return NGX_ERROR;
}
if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) {
*errlen = ngx_snprintf(err, *errlen, "bad log level: %d", level)
- err;
return NGX_ERROR;
}
ringbuf->filter_level = level;
return NGX_OK;
#else
*errlen = ngx_snprintf(err, *errlen,
"missing the capture error log patch for nginx")
- err;
return NGX_ERROR;
#endif
}
int
ngx_http_lua_ffi_errlog_get_msg(char **log, int *loglevel, u_char *err,
size_t *errlen, double *log_time)
{
#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
ngx_uint_t loglen;
ngx_http_lua_log_ringbuf_t *ringbuf;
ringbuf = ngx_cycle->intercept_error_log_data;
if (ringbuf == NULL) {
*errlen = ngx_snprintf(err, *errlen,
"directive \"lua_capture_error_log\" is not set")
- err;
return NGX_ERROR;
}
if (ringbuf->count == 0) {
return NGX_DONE;
}
ngx_http_lua_log_ringbuf_read(ringbuf, loglevel, (void **) log, &loglen,
log_time);
return loglen;
#else
*errlen = ngx_snprintf(err, *errlen,
"missing the capture error log patch for nginx")
- err;
return NGX_ERROR;
#endif
}
int
ngx_http_lua_ffi_errlog_get_sys_filter_level(ngx_http_request_t *r)
{
ngx_log_t *log;
int log_level;
if (r && r->connection && r->connection->log) {
log = r->connection->log;
} else {
log = ngx_cycle->log;
}
log_level = log->log_level;
if (log_level == NGX_LOG_DEBUG_ALL) {
log_level = NGX_LOG_DEBUG;
}
return log_level;
}
int
ngx_http_lua_ffi_raw_log(ngx_http_request_t *r, int level, u_char *s,
size_t s_len)
{
ngx_log_t *log;
if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) {
return NGX_ERROR;
}
if (r && r->connection && r->connection->log) {
log = r->connection->log;
} else {
log = ngx_cycle->log;
}
ngx_log_error((unsigned) level, log, 0, "%*s", s_len, s);
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_LOG_H_INCLUDED_
#define _NGX_HTTP_LUA_LOG_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_log_api(lua_State *L);
#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
ngx_int_t ngx_http_lua_capture_log_handler(ngx_log_t *log,
ngx_uint_t level, u_char *buf, size_t n);
#endif
#endif /* _NGX_HTTP_LUA_LOG_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,228 @@
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_common.h"
#include "ngx_http_lua_log_ringbuf.h"
typedef struct {
double time;
unsigned len;
unsigned log_level;
} ngx_http_lua_log_ringbuf_header_t;
enum {
HEADER_LEN = sizeof(ngx_http_lua_log_ringbuf_header_t),
};
static void *ngx_http_lua_log_ringbuf_next_header(
ngx_http_lua_log_ringbuf_t *rb);
static void ngx_http_lua_log_ringbuf_append(
ngx_http_lua_log_ringbuf_t *rb, int log_level, void *buf, int n);
static size_t ngx_http_lua_log_ringbuf_free_spaces(
ngx_http_lua_log_ringbuf_t *rb);
void
ngx_http_lua_log_ringbuf_init(ngx_http_lua_log_ringbuf_t *rb, void *buf,
size_t len)
{
rb->data = buf;
rb->size = len;
rb->tail = rb->data;
rb->head = rb->data;
rb->sentinel = rb->data + rb->size;
rb->count = 0;
rb->filter_level = NGX_LOG_DEBUG;
return;
}
void
ngx_http_lua_log_ringbuf_reset(ngx_http_lua_log_ringbuf_t *rb)
{
rb->tail = rb->data;
rb->head = rb->data;
rb->sentinel = rb->data + rb->size;
rb->count = 0;
return;
}
/*
* get the next data header, it'll skip the useless data space or
* placehold data
*/
static void *
ngx_http_lua_log_ringbuf_next_header(ngx_http_lua_log_ringbuf_t *rb)
{
/* useless data */
if (rb->size - (rb->head - rb->data) < HEADER_LEN)
{
return rb->data;
}
/* placehold data */
if (rb->head >= rb->sentinel) {
return rb->data;
}
return rb->head;
}
/* append data to ring buffer directly */
static void
ngx_http_lua_log_ringbuf_append(ngx_http_lua_log_ringbuf_t *rb,
int log_level, void *buf, int n)
{
ngx_http_lua_log_ringbuf_header_t *head;
ngx_time_t *tp;
head = (ngx_http_lua_log_ringbuf_header_t *) rb->tail;
head->len = n;
head->log_level = log_level;
tp = ngx_timeofday();
head->time = tp->sec + tp->msec / 1000.0L;
rb->tail += HEADER_LEN;
ngx_memcpy(rb->tail, buf, n);
rb->tail += n;
rb->count++;
if (rb->tail > rb->sentinel) {
rb->sentinel = rb->tail;
}
return;
}
/* throw away data at head */
static void
ngx_http_lua_log_ringbuf_throw_away(ngx_http_lua_log_ringbuf_t *rb)
{
ngx_http_lua_log_ringbuf_header_t *head;
if (rb->count == 0) {
return;
}
head = (ngx_http_lua_log_ringbuf_header_t *) rb->head;
rb->head += HEADER_LEN + head->len;
rb->count--;
if (rb->count == 0) {
ngx_http_lua_log_ringbuf_reset(rb);
}
rb->head = ngx_http_lua_log_ringbuf_next_header(rb);
return;
}
/* size of free spaces */
static size_t
ngx_http_lua_log_ringbuf_free_spaces(ngx_http_lua_log_ringbuf_t *rb)
{
if (rb->count == 0) {
return rb->size;
}
if (rb->tail > rb->head) {
return rb->data + rb->size - rb->tail;
}
return rb->head - rb->tail;
}
/*
* try to write log data to ring buffer, throw away old data
* if there was not enough free spaces.
*/
ngx_int_t
ngx_http_lua_log_ringbuf_write(ngx_http_lua_log_ringbuf_t *rb, int log_level,
void *buf, size_t n)
{
if (n + HEADER_LEN > rb->size) {
return NGX_ERROR;
}
if (ngx_http_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) {
/* if the right space is not enough, mark it as placehold data */
if ((size_t)(rb->data + rb->size - rb->tail) < n + HEADER_LEN) {
while (rb->head >= rb->tail && rb->count) {
/* head is after tail, so we will throw away all data between
* head and sentinel */
ngx_http_lua_log_ringbuf_throw_away(rb);
}
rb->sentinel = rb->tail;
rb->tail = rb->data;
}
while (ngx_http_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) {
ngx_http_lua_log_ringbuf_throw_away(rb);
}
}
ngx_http_lua_log_ringbuf_append(rb, log_level, buf, n);
return NGX_OK;
}
/* read log from ring buffer, do reset if all of the logs were readed. */
ngx_int_t
ngx_http_lua_log_ringbuf_read(ngx_http_lua_log_ringbuf_t *rb, int *log_level,
void **buf, size_t *n, double *log_time)
{
ngx_http_lua_log_ringbuf_header_t *head;
if (rb->count == 0) {
return NGX_ERROR;
}
head = (ngx_http_lua_log_ringbuf_header_t *) rb->head;
if (rb->head >= rb->sentinel) {
return NGX_ERROR;
}
*log_level = head->log_level;
*n = head->len;
rb->head += HEADER_LEN;
*buf = rb->head;
rb->head += head->len;
if (log_time) {
*log_time = head->time;
}
rb->count--;
if (rb->count == 0) {
ngx_http_lua_log_ringbuf_reset(rb);
}
rb->head = ngx_http_lua_log_ringbuf_next_header(rb);
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,31 @@
#ifndef _NGX_HTTP_LUA_RINGBUF_H_INCLUDED_
#define _NGX_HTTP_LUA_RINGBUF_H_INCLUDED_
#include "ngx_http_lua_common.h"
typedef struct {
ngx_uint_t filter_level;
char *tail; /* writed point */
char *head; /* readed point */
char *data; /* buffer */
char *sentinel;
size_t size; /* buffer total size */
size_t count; /* count of logs */
} ngx_http_lua_log_ringbuf_t;
void ngx_http_lua_log_ringbuf_init(ngx_http_lua_log_ringbuf_t *rb,
void *buf, size_t len);
void ngx_http_lua_log_ringbuf_reset(ngx_http_lua_log_ringbuf_t *rb);
ngx_int_t ngx_http_lua_log_ringbuf_read(ngx_http_lua_log_ringbuf_t *rb,
int *log_level, void **buf, size_t *n, double *log_time);
ngx_int_t ngx_http_lua_log_ringbuf_write(ngx_http_lua_log_ringbuf_t *rb,
int log_level, void *buf, size_t n);
#endif /* _NGX_HTTP_LUA_RINGBUF_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,268 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_directive.h"
#include "ngx_http_lua_logby.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_pcrefix.h"
#include "ngx_http_lua_log.h"
#include "ngx_http_lua_cache.h"
#include "ngx_http_lua_headers.h"
#include "ngx_http_lua_string.h"
#include "ngx_http_lua_misc.h"
#include "ngx_http_lua_consts.h"
#include "ngx_http_lua_shdict.h"
#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)
#include <malloc.h>
#endif
static ngx_int_t ngx_http_lua_log_by_chunk(lua_State *L, ngx_http_request_t *r);
static void
ngx_http_lua_log_by_lua_env(lua_State *L, ngx_http_request_t *r)
{
ngx_http_lua_set_req(L, r);
#ifndef OPENRESTY_LUAJIT
/**
* we want to create empty environment for current script
*
* newt = {}
* newt["_G"] = newt
* setmetatable(newt, {__index = _G})
*
* if a function or symbol is not defined in our env, __index will lookup
* in the global env.
*
* all variables created in the script-env will be thrown away at the end
* of the script run.
* */
ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
/* {{{ make new env inheriting main thread's globals table */
lua_createtable(L, 0, 1); /* the metatable for the new env */
ngx_http_lua_get_globals_table(L);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
/* }}} */
lua_setfenv(L, -2); /* set new running env for the code closure */
#endif /* OPENRESTY_LUAJIT */
}
ngx_int_t
ngx_http_lua_log_handler(ngx_http_request_t *r)
{
#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)
ngx_uint_t trim_cycle, trim_nreq;
ngx_http_lua_main_conf_t *lmcf;
#if (NGX_DEBUG)
ngx_int_t trim_ret;
#endif
#endif
ngx_http_lua_loc_conf_t *llcf;
ngx_http_lua_ctx_t *ctx;
#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
trim_cycle = lmcf->malloc_trim_cycle;
if (trim_cycle > 0) {
dd("cycle: %d", (int) trim_cycle);
trim_nreq = ++lmcf->malloc_trim_req_count;
if (trim_nreq >= trim_cycle) {
lmcf->malloc_trim_req_count = 0;
#if (NGX_DEBUG)
trim_ret = malloc_trim(1);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"malloc_trim(1) returned %d", trim_ret);
#else
(void) malloc_trim(1);
#endif
}
}
# if (NGX_DEBUG)
else {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"malloc_trim() disabled");
}
# endif
#endif
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua log handler, uri:\"%V\" c:%ud", &r->uri,
r->main->count);
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->log_handler == NULL) {
dd("no log handler found");
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
}
ctx->context = NGX_HTTP_LUA_CONTEXT_LOG;
dd("calling log handler");
return llcf->log_handler(r);
}
ngx_int_t
ngx_http_lua_log_handler_inline(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
ngx_http_lua_loc_conf_t *llcf;
dd("log by lua inline");
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
llcf->log_src.value.data,
llcf->log_src.value.len,
&llcf->log_src_ref,
llcf->log_src_key,
(const char *) llcf->log_chunkname);
if (rc != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_lua_log_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_log_handler_file(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
u_char *script_path;
ngx_http_lua_loc_conf_t *llcf;
ngx_str_t eval_src;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (ngx_http_complex_value(r, &llcf->log_src, &eval_src) != NGX_OK) {
return NGX_ERROR;
}
script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
eval_src.len);
if (script_path == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
&llcf->log_src_ref,
llcf->log_src_key);
if (rc != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_lua_log_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_log_by_chunk(lua_State *L, ngx_http_request_t *r)
{
ngx_int_t rc;
u_char *err_msg;
size_t len;
#if (NGX_PCRE)
ngx_pool_t *old_pool;
#endif
/* set Lua VM panic handler */
lua_atpanic(L, ngx_http_lua_atpanic);
NGX_LUA_EXCEPTION_TRY {
/* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */
ngx_http_lua_log_by_lua_env(L, r);
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif
lua_pushcfunction(L, ngx_http_lua_traceback);
lua_insert(L, 1); /* put it under chunk and args */
/* protected call user code */
rc = lua_pcall(L, 0, 1, 1);
lua_remove(L, 1); /* remove traceback function */
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
ngx_http_lua_pcre_malloc_done(old_pool);
#endif
if (rc != 0) {
/* error occurred when running loaded code */
err_msg = (u_char *) lua_tolstring(L, -1, &len);
if (err_msg == NULL) {
err_msg = (u_char *) "unknown reason";
len = sizeof("unknown reason") - 1;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"failed to run log_by_lua*: %*s", len, err_msg);
lua_settop(L, 0); /* clear remaining elems on stack */
return NGX_ERROR;
}
} NGX_LUA_EXCEPTION_CATCH {
dd("nginx execution restored");
return NGX_ERROR;
}
/* clear Lua stack */
lua_settop(L, 0);
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_LOGBY_H_INCLUDED_
#define _NGX_HTTP_LUA_LOGBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_log_handler(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_log_handler_inline(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_log_handler_file(ngx_http_request_t *r);
#endif /* _NGX_HTTP_LUA_LOGBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_misc.h"
#include "ngx_http_lua_util.h"
static int ngx_http_lua_ngx_req_is_internal(lua_State *L);
void
ngx_http_lua_inject_req_misc_api(lua_State *L)
{
lua_pushcfunction(L, ngx_http_lua_ngx_req_is_internal);
lua_setfield(L, -2, "is_internal");
}
static int
ngx_http_lua_ngx_req_is_internal(lua_State *L)
{
ngx_http_request_t *r;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
lua_pushboolean(L, r->internal == 1);
return 1;
}
int
ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r)
{
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
if (r->err_status) {
return r->err_status;
} else if (r->headers_out.status) {
return r->headers_out.status;
} else if (r->http_version == NGX_HTTP_VERSION_9) {
return 9;
} else {
return 0;
}
}
int
ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status)
{
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
if (r->header_sent) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"attempt to set ngx.status after sending out "
"response headers");
return NGX_DECLINED;
}
r->headers_out.status = status;
if (r->err_status) {
r->err_status = 0;
}
if (status == 101) {
/*
* XXX work-around a bug in the Nginx core older than 1.5.5
* that 101 does not have a default status line
*/
ngx_str_set(&r->headers_out.status_line, "101 Switching Protocols");
} else {
r->headers_out.status_line.len = 0;
}
return NGX_OK;
}
int
ngx_http_lua_ffi_req_is_internal(ngx_http_request_t *r)
{
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
return r->internal;
}
int
ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r)
{
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
return r != r->main;
}
int
ngx_http_lua_ffi_headers_sent(ngx_http_request_t *r)
{
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return NGX_HTTP_LUA_FFI_NO_REQ_CTX;
}
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
return r->header_sent ? 1 : 0;
}
int
ngx_http_lua_ffi_get_conf_env(u_char *name, u_char **env_buf, size_t *name_len)
{
ngx_uint_t i;
ngx_str_t *var;
ngx_core_conf_t *ccf;
ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_core_module);
var = ccf->env.elts;
for (i = 0; i < ccf->env.nelts; i++) {
if (var[i].data[var[i].len] == '='
&& ngx_strncmp(name, var[i].data, var[i].len) == 0)
{
*env_buf = var[i].data;
*name_len = var[i].len;
return NGX_OK;
}
}
return NGX_DECLINED;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_MISC_H_INCLUDED_
#define _NGX_HTTP_LUA_MISC_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_req_misc_api(lua_State *L);
#endif /* _NGX_HTTP_LUA_MISC_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_ndk.h"
#include "ngx_http_lua_util.h"
#if defined(NDK) && NDK
static ndk_set_var_value_pt ngx_http_lookup_ndk_set_var_directive(u_char *name,
size_t name_len);
void
ngx_http_lua_inject_ndk_api(lua_State *L)
{
lua_createtable(L, 0, 1 /* nrec */); /* ndk.* */
lua_getglobal(L, "package"); /* ndk package */
lua_getfield(L, -1, "loaded"); /* ndk package loaded */
lua_pushvalue(L, -3); /* ndk package loaded ndk */
lua_setfield(L, -2, "ndk"); /* ndk package loaded */
lua_pop(L, 2);
lua_setglobal(L, "ndk");
}
static ndk_set_var_value_pt
ngx_http_lookup_ndk_set_var_directive(u_char *name,
size_t name_len)
{
ndk_set_var_t *filter;
ngx_uint_t i;
ngx_module_t *module;
ngx_module_t **modules;
ngx_command_t *cmd;
#if (nginx_version >= 1009011)
modules = ngx_cycle->modules;
#else
modules = ngx_modules;
#endif
for (i = 0; modules[i]; i++) {
module = modules[i];
if (module->type != NGX_HTTP_MODULE) {
continue;
}
cmd = modules[i]->commands;
if (cmd == NULL) {
continue;
}
for ( /* void */ ; cmd->name.len; cmd++) {
if (cmd->set != ndk_set_var_value) {
continue;
}
filter = cmd->post;
if (filter == NULL) {
continue;
}
if (cmd->name.len != name_len
|| ngx_strncmp(cmd->name.data, name, name_len) != 0)
{
continue;
}
return (ndk_set_var_value_pt)(filter->func);
}
}
return NULL;
}
int
ngx_http_lua_ffi_ndk_lookup_directive(const u_char *var_data,
size_t var_len, ndk_set_var_value_pt *func)
{
*func = ngx_http_lookup_ndk_set_var_directive((u_char *) var_data, var_len);
if (*func == NULL) {
return NGX_ERROR;
}
return NGX_OK;
}
int
ngx_http_lua_ffi_ndk_set_var_get(ngx_http_request_t *r,
ndk_set_var_value_pt func, const u_char *arg_data, size_t arg_len,
ngx_http_lua_ffi_str_t *value)
{
ngx_int_t rc;
ngx_str_t res;
ngx_http_variable_value_t arg;
ngx_memzero(&arg, sizeof(ngx_http_variable_value_t));
arg.valid = 1;
arg.data = (u_char *) arg_data;
arg.len = arg_len;
rc = func(r, &res, &arg);
if (rc != NGX_OK) {
return rc;
}
value->data = res.data;
value->len = res.len;
return NGX_OK;
}
#endif /* defined(NDK) && NDK */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_NDK_H_INCLUDED_
#define _NGX_HTTP_LUA_NDK_H_INCLUDED_
#include "ngx_http_lua_common.h"
#if defined(NDK) && NDK
void ngx_http_lua_inject_ndk_api(lua_State *L);
#endif
#endif /* _NGX_HTTP_LUA_NDK_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,829 @@
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_output.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_contentby.h"
#include <math.h>
static int ngx_http_lua_ngx_say(lua_State *L);
static int ngx_http_lua_ngx_print(lua_State *L);
static int ngx_http_lua_ngx_flush(lua_State *L);
static int ngx_http_lua_ngx_eof(lua_State *L);
static int ngx_http_lua_ngx_send_headers(lua_State *L);
static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline);
static void ngx_http_lua_flush_cleanup(void *data);
static int
ngx_http_lua_ngx_print(lua_State *L)
{
dd("calling lua print");
return ngx_http_lua_ngx_echo(L, 0);
}
static int
ngx_http_lua_ngx_say(lua_State *L)
{
dd("calling");
return ngx_http_lua_ngx_echo(L, 1);
}
static int
ngx_http_lua_ngx_echo(lua_State *L, unsigned newline)
{
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
const char *p;
size_t len;
size_t size;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_int_t rc;
int i;
int nargs;
int type;
const char *msg;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);
if (ctx->acquired_raw_req_socket) {
lua_pushnil(L);
lua_pushliteral(L, "raw request socket acquired");
return 2;
}
if (r->header_only) {
lua_pushnil(L);
lua_pushliteral(L, "header only");
return 2;
}
if (ctx->eof) {
lua_pushnil(L);
lua_pushliteral(L, "seen eof");
return 2;
}
nargs = lua_gettop(L);
size = 0;
for (i = 1; i <= nargs; i++) {
type = lua_type(L, i);
switch (type) {
case LUA_TNUMBER:
size += ngx_http_lua_get_num_len(L, i);
break;
case LUA_TSTRING:
lua_tolstring(L, i, &len);
size += len;
break;
case LUA_TNIL:
size += sizeof("nil") - 1;
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, i)) {
size += sizeof("true") - 1;
} else {
size += sizeof("false") - 1;
}
break;
case LUA_TTABLE:
size += ngx_http_lua_calc_strlen_in_table(L, i, i,
0 /* strict */);
break;
case LUA_TLIGHTUSERDATA:
dd("userdata: %p", lua_touserdata(L, i));
if (lua_touserdata(L, i) == NULL) {
size += sizeof("null") - 1;
break;
}
continue;
default:
msg = lua_pushfstring(L, "string, number, boolean, nil, "
"ngx.null, or array table expected, "
"but got %s", lua_typename(L, type));
return luaL_argerror(L, i, msg);
}
}
if (newline) {
size += sizeof("\n") - 1;
}
if (size == 0) {
rc = ngx_http_lua_send_header_if_needed(r, ctx);
if (rc == NGX_ERROR || rc > NGX_OK) {
lua_pushnil(L);
lua_pushliteral(L, "nginx output filter error");
return 2;
}
lua_pushinteger(L, 1);
return 1;
}
ctx->seen_body_data = 1;
cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,
&ctx->free_bufs, size);
if (cl == NULL) {
return luaL_error(L, "no memory");
}
b = cl->buf;
for (i = 1; i <= nargs; i++) {
type = lua_type(L, i);
switch (type) {
case LUA_TNUMBER:
b->last = ngx_http_lua_write_num(L, i, b->last);
break;
case LUA_TSTRING:
p = lua_tolstring(L, i, &len);
b->last = ngx_copy(b->last, (u_char *) p, len);
break;
case LUA_TNIL:
*b->last++ = 'n';
*b->last++ = 'i';
*b->last++ = 'l';
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, i)) {
*b->last++ = 't';
*b->last++ = 'r';
*b->last++ = 'u';
*b->last++ = 'e';
} else {
*b->last++ = 'f';
*b->last++ = 'a';
*b->last++ = 'l';
*b->last++ = 's';
*b->last++ = 'e';
}
break;
case LUA_TTABLE:
b->last = ngx_http_lua_copy_str_in_table(L, i, b->last);
break;
case LUA_TLIGHTUSERDATA:
*b->last++ = 'n';
*b->last++ = 'u';
*b->last++ = 'l';
*b->last++ = 'l';
break;
default:
return luaL_error(L, "impossible to reach here");
}
}
if (newline) {
*b->last++ = '\n';
}
#if 0
if (b->last != b->end) {
return luaL_error(L, "buffer error: %p != %p", b->last, b->end);
}
#endif
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
newline ? "lua say response" : "lua print response");
rc = ngx_http_lua_send_chain_link(r, ctx, cl);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
lua_pushnil(L);
lua_pushliteral(L, "nginx output filter error");
return 2;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"%s has %sbusy bufs",
newline ? "lua say response" : "lua print response",
ctx->busy_bufs != NULL ? "" : "no ");
dd("downstream write: %d, buf len: %d", (int) rc,
(int) (b->last - b->pos));
lua_pushinteger(L, 1);
return 1;
}
size_t
ngx_http_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i,
unsigned strict)
{
double key;
int max;
int i;
int type;
size_t size;
size_t len;
const char *msg;
if (index < 0) {
index = lua_gettop(L) + index + 1;
}
dd("table index: %d", index);
max = 0;
lua_pushnil(L); /* stack: table key */
while (lua_next(L, index) != 0) { /* stack: table key value */
dd("key type: %s", luaL_typename(L, -2));
if (lua_type(L, -2) == LUA_TNUMBER) {
key = lua_tonumber(L, -2);
dd("key value: %d", (int) key);
if (floor(key) == key && key >= 1) {
if (key > max) {
max = (int) key;
}
lua_pop(L, 1); /* stack: table key */
continue;
}
}
/* not an array (non positive integer key) */
lua_pop(L, 2); /* stack: table */
luaL_argerror(L, arg_i, "non-array table found");
return 0;
}
size = 0;
for (i = 1; i <= max; i++) {
lua_rawgeti(L, index, i); /* stack: table value */
type = lua_type(L, -1);
switch (type) {
case LUA_TNUMBER:
size += ngx_http_lua_get_num_len(L, -1);
break;
case LUA_TSTRING:
lua_tolstring(L, -1, &len);
size += len;
break;
case LUA_TNIL:
if (strict) {
goto bad_type;
}
size += sizeof("nil") - 1;
break;
case LUA_TBOOLEAN:
if (strict) {
goto bad_type;
}
if (lua_toboolean(L, -1)) {
size += sizeof("true") - 1;
} else {
size += sizeof("false") - 1;
}
break;
case LUA_TTABLE:
size += ngx_http_lua_calc_strlen_in_table(L, -1, arg_i, strict);
break;
case LUA_TLIGHTUSERDATA:
if (strict) {
goto bad_type;
}
if (lua_touserdata(L, -1) == NULL) {
size += sizeof("null") - 1;
break;
}
continue;
default:
bad_type:
msg = lua_pushfstring(L, "bad data type %s found",
lua_typename(L, type));
return luaL_argerror(L, arg_i, msg);
}
lua_pop(L, 1); /* stack: table */
}
return size;
}
u_char *
ngx_http_lua_copy_str_in_table(lua_State *L, int index, u_char *dst)
{
double key;
int max;
int i;
int type;
size_t len;
u_char *p;
if (index < 0) {
index = lua_gettop(L) + index + 1;
}
max = 0;
lua_pushnil(L); /* stack: table key */
while (lua_next(L, index) != 0) { /* stack: table key value */
key = lua_tonumber(L, -2);
if (key > max) {
max = (int) key;
}
lua_pop(L, 1); /* stack: table key */
}
for (i = 1; i <= max; i++) {
lua_rawgeti(L, index, i); /* stack: table value */
type = lua_type(L, -1);
switch (type) {
case LUA_TNUMBER:
dst = ngx_http_lua_write_num(L, -1, dst);
break;
case LUA_TSTRING:
p = (u_char *) lua_tolstring(L, -1, &len);
dst = ngx_copy(dst, p, len);
break;
case LUA_TNIL:
*dst++ = 'n';
*dst++ = 'i';
*dst++ = 'l';
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, -1)) {
*dst++ = 't';
*dst++ = 'r';
*dst++ = 'u';
*dst++ = 'e';
} else {
*dst++ = 'f';
*dst++ = 'a';
*dst++ = 'l';
*dst++ = 's';
*dst++ = 'e';
}
break;
case LUA_TTABLE:
dst = ngx_http_lua_copy_str_in_table(L, -1, dst);
break;
case LUA_TLIGHTUSERDATA:
*dst++ = 'n';
*dst++ = 'u';
*dst++ = 'l';
*dst++ = 'l';
break;
default:
luaL_error(L, "impossible to reach here");
return NULL;
}
lua_pop(L, 1); /* stack: table */
}
return dst;
}
/**
* Force flush out response content
* */
static int
ngx_http_lua_ngx_flush(lua_State *L)
{
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_chain_t *cl;
ngx_int_t rc;
int n;
unsigned wait = 0;
ngx_event_t *wev;
ngx_http_core_loc_conf_t *clcf;
ngx_http_lua_co_ctx_t *coctx;
n = lua_gettop(L);
if (n > 1) {
return luaL_error(L, "attempt to pass %d arguments, but accepted 0 "
"or 1", n);
}
r = ngx_http_lua_get_req(L);
if (n == 1 && r == r->main) {
luaL_checktype(L, 1, LUA_TBOOLEAN);
wait = lua_toboolean(L, 1);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no request ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);
if (ctx->acquired_raw_req_socket) {
lua_pushnil(L);
lua_pushliteral(L, "raw request socket acquired");
return 2;
}
coctx = ctx->cur_co_ctx;
if (coctx == NULL) {
return luaL_error(L, "no co ctx found");
}
if (r->header_only) {
lua_pushnil(L);
lua_pushliteral(L, "header only");
return 2;
}
if (ctx->eof) {
lua_pushnil(L);
lua_pushliteral(L, "seen eof");
return 2;
}
if (ctx->buffering) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua http 1.0 buffering makes ngx.flush() a no-op");
lua_pushnil(L);
lua_pushliteral(L, "buffering");
return 2;
}
#if 1
if ((!r->header_sent && !ctx->header_sent)
|| (!ctx->seen_body_data && !wait))
{
lua_pushnil(L);
lua_pushliteral(L, "nothing to flush");
return 2;
}
#endif
cl = ngx_http_lua_get_flush_chain(r, ctx);
if (cl == NULL) {
return luaL_error(L, "no memory");
}
rc = ngx_http_lua_send_chain_link(r, ctx, cl);
dd("send chain: %d", (int) rc);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
lua_pushnil(L);
lua_pushliteral(L, "nginx output filter error");
return 2;
}
dd("wait:%d, rc:%d, buffered:0x%x", wait, (int) rc,
r->connection->buffered);
wev = r->connection->write;
if (wait && (r->connection->buffered
& (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)
|| wev->delayed))
{
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua flush requires waiting: buffered 0x%uxd, "
"delayed:%d", (unsigned) r->connection->buffered,
wev->delayed);
coctx->flushing = 1;
ctx->flushing_coros++;
if (ctx->entered_content_phase) {
/* mimic ngx_http_set_write_handler */
r->write_event_handler = ngx_http_lua_content_wev_handler;
} else {
r->write_event_handler = ngx_http_core_run_phases;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!wev->delayed) {
ngx_add_timer(wev, clcf->send_timeout);
}
if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
if (wev->timer_set) {
wev->delayed = 0;
ngx_del_timer(wev);
}
lua_pushnil(L);
lua_pushliteral(L, "connection broken");
return 2;
}
ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
coctx->cleanup = ngx_http_lua_flush_cleanup;
coctx->data = r;
return lua_yield(L, 0);
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua flush asynchronously");
lua_pushinteger(L, 1);
return 1;
}
/**
* Send last_buf, terminate output stream
* */
static int
ngx_http_lua_ngx_eof(lua_State *L)
{
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_int_t rc;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
if (lua_gettop(L) != 0) {
return luaL_error(L, "no argument is expected");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no ctx found");
}
if (ctx->acquired_raw_req_socket) {
lua_pushnil(L);
lua_pushliteral(L, "raw request socket acquired");
return 2;
}
if (ctx->eof) {
lua_pushnil(L);
lua_pushliteral(L, "seen eof");
return 2;
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua send eof");
rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */);
dd("send chain: %d", (int) rc);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
lua_pushnil(L);
lua_pushliteral(L, "nginx output filter error");
return 2;
}
lua_pushinteger(L, 1);
return 1;
}
void
ngx_http_lua_inject_output_api(lua_State *L)
{
lua_pushcfunction(L, ngx_http_lua_ngx_send_headers);
lua_setfield(L, -2, "send_headers");
lua_pushcfunction(L, ngx_http_lua_ngx_print);
lua_setfield(L, -2, "print");
lua_pushcfunction(L, ngx_http_lua_ngx_say);
lua_setfield(L, -2, "say");
lua_pushcfunction(L, ngx_http_lua_ngx_flush);
lua_setfield(L, -2, "flush");
lua_pushcfunction(L, ngx_http_lua_ngx_eof);
lua_setfield(L, -2, "eof");
}
/**
* Send out headers
* */
static int
ngx_http_lua_ngx_send_headers(lua_State *L)
{
ngx_int_t rc;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no ctx found");
}
ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);
if (!r->header_sent && !ctx->header_sent) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua send headers");
rc = ngx_http_lua_send_header_if_needed(r, ctx);
if (rc == NGX_ERROR || rc > NGX_OK) {
lua_pushnil(L);
lua_pushliteral(L, "nginx output filter error");
return 2;
}
}
lua_pushinteger(L, 1);
return 1;
}
ngx_int_t
ngx_http_lua_flush_resume_helper(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)
{
int n;
lua_State *vm;
ngx_int_t rc;
ngx_uint_t nreqs;
ngx_connection_t *c;
c = r->connection;
ctx->cur_co_ctx->cleanup = NULL;
/* push the return values */
if (c->timedout) {
lua_pushnil(ctx->cur_co_ctx->co);
lua_pushliteral(ctx->cur_co_ctx->co, "timeout");
n = 2;
} else if (c->error) {
lua_pushnil(ctx->cur_co_ctx->co);
lua_pushliteral(ctx->cur_co_ctx->co, "client aborted");
n = 2;
} else {
lua_pushinteger(ctx->cur_co_ctx->co, 1);
n = 1;
}
vm = ngx_http_lua_get_lua_vm(r, ctx);
nreqs = c->requests;
rc = ngx_http_lua_run_thread(vm, r, ctx, n);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua run thread returned %d", rc);
if (rc == NGX_AGAIN) {
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
}
if (rc == NGX_DONE) {
ngx_http_lua_finalize_request(r, NGX_DONE);
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
}
/* rc == NGX_ERROR || rc >= NGX_OK */
if (ctx->entered_content_phase) {
ngx_http_lua_finalize_request(r, rc);
return NGX_DONE;
}
return rc;
}
static void
ngx_http_lua_flush_cleanup(void *data)
{
ngx_http_request_t *r;
ngx_event_t *wev;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *coctx = data;
coctx->flushing = 0;
r = coctx->data;
if (r == NULL) {
return;
}
wev = r->connection->write;
if (wev && wev->timer_set) {
ngx_del_timer(wev);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return;
}
ctx->flushing_coros--;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_
#define _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_output_api(lua_State *L);
size_t ngx_http_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i,
unsigned strict);
u_char *ngx_http_lua_copy_str_in_table(lua_State *L, int index, u_char *dst);
ngx_int_t ngx_http_lua_flush_resume_helper(ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx);
/* Get the maximum possible length, not the actual length */
static ngx_inline size_t
ngx_http_lua_get_num_len(lua_State *L, int idx)
{
double num;
num = (double) lua_tonumber(L, idx);
if (num == (double) (int32_t) num) {
return NGX_INT32_LEN;
}
return NGX_DOUBLE_LEN;
}
static ngx_inline u_char *
ngx_http_lua_write_num(lua_State *L, int idx, u_char *dst)
{
double num;
int n;
num = (double) lua_tonumber(L, idx);
/*
* luajit format number with only 14 significant digits.
* To be consistent with lujit, don't use (double) (long) below
* or integer greater than 99,999,999,999,999 will different from luajit.
*/
if (num == (double) (int32_t) num) {
dst = ngx_snprintf(dst, NGX_INT64_LEN, "%D", (int32_t) num);
} else {
/*
* The maximum number of significant digits is 14 in lua.
* Please refer to lj_strfmt.c for more details.
*/
n = snprintf((char *) dst, NGX_DOUBLE_LEN, "%.14g", num);
if (n < 0) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,
"snprintf(\"%f\") failed");
} else {
dst += n;
}
}
return dst;
}
#endif /* _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,106 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_pcrefix.h"
#include "stdio.h"
#if (NGX_PCRE)
static ngx_pool_t *ngx_http_lua_pcre_pool = NULL;
static void *(*old_pcre_malloc)(size_t);
static void (*old_pcre_free)(void *ptr);
/* XXX: work-around to nginx regex subsystem, must init a memory pool
* to use PCRE functions. As PCRE still has memory-leaking problems,
* and nginx overwrote pcre_malloc/free hooks with its own static
* functions, so nobody else can reuse nginx regex subsystem... */
static void *
ngx_http_lua_pcre_malloc(size_t size)
{
dd("lua pcre pool is %p", ngx_http_lua_pcre_pool);
if (ngx_http_lua_pcre_pool) {
return ngx_palloc(ngx_http_lua_pcre_pool, size);
}
fprintf(stderr, "error: lua pcre malloc failed due to empty pcre pool");
return NULL;
}
static void
ngx_http_lua_pcre_free(void *ptr)
{
dd("lua pcre pool is %p", ngx_http_lua_pcre_pool);
if (ngx_http_lua_pcre_pool) {
ngx_pfree(ngx_http_lua_pcre_pool, ptr);
return;
}
fprintf(stderr, "error: lua pcre free failed due to empty pcre pool");
}
ngx_pool_t *
ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool)
{
ngx_pool_t *old_pool;
if (pcre_malloc != ngx_http_lua_pcre_malloc) {
dd("overriding nginx pcre malloc and free");
ngx_http_lua_pcre_pool = pool;
old_pcre_malloc = pcre_malloc;
old_pcre_free = pcre_free;
pcre_malloc = ngx_http_lua_pcre_malloc;
pcre_free = ngx_http_lua_pcre_free;
return NULL;
}
dd("lua pcre pool was %p", ngx_http_lua_pcre_pool);
old_pool = ngx_http_lua_pcre_pool;
ngx_http_lua_pcre_pool = pool;
dd("lua pcre pool is %p", ngx_http_lua_pcre_pool);
return old_pool;
}
void
ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool)
{
dd("lua pcre pool was %p", ngx_http_lua_pcre_pool);
ngx_http_lua_pcre_pool = old_pool;
dd("lua pcre pool is %p", ngx_http_lua_pcre_pool);
if (old_pool == NULL) {
pcre_malloc = old_pcre_malloc;
pcre_free = old_pcre_free;
}
}
#endif /* NGX_PCRE */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_
#define _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_
#include "ngx_http_lua_common.h"
#if (NGX_PCRE)
ngx_pool_t *ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool);
void ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool);
#endif
#endif /* _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_common.h"
int
ngx_http_lua_ffi_get_phase(ngx_http_request_t *r, char **err)
{
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*err = "no request context";
return NGX_ERROR;
}
return ctx->context;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) by OpenResty Inc.
*/
#ifndef _NGX_HTTP_LUA_PIPE_H_INCLUDED_
#define _NGX_HTTP_LUA_PIPE_H_INCLUDED_
#include "ngx_http_lua_common.h"
typedef ngx_int_t (*ngx_http_lua_pipe_input_filter)(void *data, ssize_t bytes);
typedef struct {
ngx_connection_t *c;
ngx_http_lua_pipe_input_filter input_filter;
void *input_filter_ctx;
size_t rest;
ngx_chain_t *buf_in;
ngx_chain_t *bufs_in;
ngx_buf_t buffer;
ngx_err_t pipe_errno;
unsigned err_type:16;
unsigned eof:1;
} ngx_http_lua_pipe_ctx_t;
typedef struct ngx_http_lua_pipe_s ngx_http_lua_pipe_t;
typedef struct {
ngx_pid_t _pid;
ngx_msec_t write_timeout;
ngx_msec_t stdout_read_timeout;
ngx_msec_t stderr_read_timeout;
ngx_msec_t wait_timeout;
/* pipe hides the implementation from the Lua binding */
ngx_http_lua_pipe_t *pipe;
} ngx_http_lua_ffi_pipe_proc_t;
typedef int (*ngx_http_lua_pipe_retval_handler)(
ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L);
struct ngx_http_lua_pipe_s {
ngx_pool_t *pool;
ngx_chain_t *free_bufs;
ngx_rbtree_node_t *node;
int stdin_fd;
int stdout_fd;
int stderr_fd;
ngx_http_lua_pipe_ctx_t *stdin_ctx;
ngx_http_lua_pipe_ctx_t *stdout_ctx;
ngx_http_lua_pipe_ctx_t *stderr_ctx;
ngx_http_lua_pipe_retval_handler retval_handler;
ngx_http_cleanup_pt *cleanup;
ngx_http_request_t *r;
size_t buffer_size;
unsigned closed:1;
unsigned dead:1;
unsigned timeout:1;
unsigned merge_stderr:1;
};
typedef struct {
u_char color;
u_char reason_code;
int status;
ngx_http_lua_co_ctx_t *wait_co_ctx;
ngx_http_lua_ffi_pipe_proc_t *proc;
} ngx_http_lua_pipe_node_t;
typedef struct {
int signo;
char *signame;
} ngx_http_lua_pipe_signal_t;
#if !(NGX_WIN32) && defined(HAVE_SOCKET_CLOEXEC_PATCH)
#define HAVE_NGX_LUA_PIPE 1
void ngx_http_lua_pipe_init(void);
ngx_int_t ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle);
#endif
#endif /* _NGX_HTTP_LUA_PIPE_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,86 @@
/*
* automatically generated from the file dtrace/ngx_lua_provider.d by the
* gen-dtrace-probe-header tool in the nginx-devel-utils project:
* https://github.com/agentzh/nginx-devel-utils
*/
#ifndef _NGX_HTTP_LUA_PROBE_H_INCLUDED_
#define _NGX_HTTP_LUA_PROBE_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#if defined(NGX_DTRACE) && NGX_DTRACE
#include <ngx_dtrace_provider.h>
#define ngx_http_lua_probe_info(s) \
NGINX_LUA_HTTP_LUA_INFO(s)
#define ngx_http_lua_probe_register_preload_package(L, pkg) \
NGINX_LUA_HTTP_LUA_REGISTER_PRELOAD_PACKAGE(L, pkg)
#define ngx_http_lua_probe_req_socket_consume_preread(r, data, len) \
NGINX_LUA_HTTP_LUA_REQ_SOCKET_CONSUME_PREREAD(r, data, len)
#define ngx_http_lua_probe_user_coroutine_create(r, parent, child) \
NGINX_LUA_HTTP_LUA_USER_COROUTINE_CREATE(r, parent, child)
#define ngx_http_lua_probe_user_coroutine_resume(r, parent, child) \
NGINX_LUA_HTTP_LUA_USER_COROUTINE_RESUME(r, parent, child)
#define ngx_http_lua_probe_user_coroutine_yield(r, parent, child) \
NGINX_LUA_HTTP_LUA_USER_COROUTINE_YIELD(r, parent, child)
#define ngx_http_lua_probe_thread_yield(r, L) \
NGINX_LUA_HTTP_LUA_THREAD_YIELD(r, L)
#define ngx_http_lua_probe_socket_tcp_send_start(r, u, data, len) \
NGINX_LUA_HTTP_LUA_SOCKET_TCP_SEND_START(r, u, data, len)
#define ngx_http_lua_probe_socket_tcp_receive_done(r, u, data, len) \
NGINX_LUA_HTTP_LUA_SOCKET_TCP_RECEIVE_DONE(r, u, data, len)
#define ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data, \
len) \
NGINX_LUA_HTTP_LUA_SOCKET_TCP_SETKEEPALIVE_BUF_UNREAD(r, u, data, len)
#define ngx_http_lua_probe_user_thread_spawn(r, creator, newthread) \
NGINX_LUA_HTTP_LUA_USER_THREAD_SPAWN(r, creator, newthread)
#define ngx_http_lua_probe_thread_delete(r, thread, ctx) \
NGINX_LUA_HTTP_LUA_THREAD_DELETE(r, thread, ctx)
#define ngx_http_lua_probe_run_posted_thread(r, thread, status) \
NGINX_LUA_HTTP_LUA_RUN_POSTED_THREAD(r, thread, status)
#define ngx_http_lua_probe_coroutine_done(r, co, success) \
NGINX_LUA_HTTP_LUA_COROUTINE_DONE(r, co, success)
#define ngx_http_lua_probe_user_thread_wait(parent, child) \
NGINX_LUA_HTTP_LUA_USER_THREAD_WAIT(parent, child)
#else /* !(NGX_DTRACE) */
#define ngx_http_lua_probe_info(s)
#define ngx_http_lua_probe_register_preload_package(L, pkg)
#define ngx_http_lua_probe_req_socket_consume_preread(r, data, len)
#define ngx_http_lua_probe_user_coroutine_create(r, parent, child)
#define ngx_http_lua_probe_user_coroutine_resume(r, parent, child)
#define ngx_http_lua_probe_user_coroutine_yield(r, parent, child)
#define ngx_http_lua_probe_thread_yield(r, L)
#define ngx_http_lua_probe_socket_tcp_send_start(r, u, data, len)
#define ngx_http_lua_probe_socket_tcp_receive_done(r, u, data, len)
#define ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data, len)
#define ngx_http_lua_probe_user_thread_spawn(r, creator, newthread)
#define ngx_http_lua_probe_thread_delete(r, thread, ctx)
#define ngx_http_lua_probe_run_posted_thread(r, thread, status)
#define ngx_http_lua_probe_coroutine_done(r, co, success)
#define ngx_http_lua_probe_user_thread_wait(parent, child)
#endif
#endif /* _NGX_HTTP_LUA_PROBE_H_INCLUDED_ */

View File

@ -0,0 +1,602 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#if (NGX_PCRE)
#include "ngx_http_lua_pcrefix.h"
#include "ngx_http_lua_script.h"
#include "ngx_http_lua_util.h"
#if (PCRE_MAJOR >= 6)
# define LUA_HAVE_PCRE_DFA 1
#else
# define LUA_HAVE_PCRE_DFA 0
#endif
#define NGX_LUA_RE_MODE_DFA (1<<1)
#define NGX_LUA_RE_MODE_JIT (1<<2)
#define NGX_LUA_RE_NO_UTF8_CHECK (1<<4)
#define NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT (100)
#define NGX_LUA_RE_MIN_JIT_STACK_SIZE 32 * 1024
typedef struct {
ngx_pool_t *pool;
u_char *name_table;
int name_count;
int name_entry_size;
int ncaptures;
int *captures;
pcre *regex;
pcre_extra *regex_sd;
ngx_http_lua_complex_value_t *replace;
/* only for (stap) debugging, and may be an invalid pointer */
const u_char *pattern;
} ngx_http_lua_regex_t;
typedef struct {
ngx_str_t pattern;
ngx_pool_t *pool;
ngx_int_t options;
pcre *regex;
int captures;
ngx_str_t err;
} ngx_http_lua_regex_compile_t;
typedef struct {
ngx_http_request_t *request;
pcre *regex;
pcre_extra *regex_sd;
int ncaptures;
int *captures;
int captures_len;
uint8_t flags;
} ngx_http_lua_regex_ctx_t;
static void ngx_http_lua_regex_free_study_data(ngx_pool_t *pool,
pcre_extra *sd);
static ngx_int_t ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc);
#define ngx_http_lua_regex_exec(re, e, s, start, captures, size, opts) \
pcre_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \
captures, size)
#define ngx_http_lua_regex_dfa_exec(re, e, s, start, captures, size, ws, \
wscount, opts) \
pcre_dfa_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \
captures, size, ws, wscount)
static void
ngx_http_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd)
{
ngx_pool_t *old_pool;
old_pool = ngx_http_lua_pcre_malloc_init(pool);
#if LUA_HAVE_PCRE_JIT
pcre_free_study(sd);
#else
pcre_free(sd);
#endif
ngx_http_lua_pcre_malloc_done(old_pool);
}
static ngx_int_t
ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc)
{
int n, erroff;
char *p;
const char *errstr;
pcre *re;
ngx_pool_t *old_pool;
old_pool = ngx_http_lua_pcre_malloc_init(rc->pool);
re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
&errstr, &erroff, NULL);
ngx_http_lua_pcre_malloc_done(old_pool);
if (re == NULL) {
if ((size_t) erroff == rc->pattern.len) {
rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
"pcre_compile() failed: %s in \"%V\"",
errstr, &rc->pattern)
- rc->err.data;
} else {
rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
"pcre_compile() failed: %s in \"%V\" "
"at \"%s\"", errstr, &rc->pattern,
rc->pattern.data + erroff)
- rc->err.data;
}
return NGX_ERROR;
}
rc->regex = re;
#if 1
n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
if (n < 0) {
p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
goto failed;
}
#endif
return NGX_OK;
failed:
rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
- rc->err.data;
return NGX_OK;
}
ngx_int_t
ngx_http_lua_ffi_set_jit_stack_size(int size, u_char *errstr,
size_t *errstr_size)
{
#if LUA_HAVE_PCRE_JIT
ngx_http_lua_main_conf_t *lmcf;
ngx_pool_t *pool, *old_pool;
lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
ngx_http_lua_module);
ngx_http_lua_assert(lmcf != NULL);
if (size < NGX_LUA_RE_MIN_JIT_STACK_SIZE) {
size = NGX_LUA_RE_MIN_JIT_STACK_SIZE;
}
pool = lmcf->pool;
dd("server pool %p", lmcf->pool);
if (lmcf->jit_stack) {
old_pool = ngx_http_lua_pcre_malloc_init(pool);
pcre_jit_stack_free(lmcf->jit_stack);
ngx_http_lua_pcre_malloc_done(old_pool);
}
old_pool = ngx_http_lua_pcre_malloc_init(pool);
lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE,
size);
ngx_http_lua_pcre_malloc_done(old_pool);
if (lmcf->jit_stack == NULL) {
*errstr_size = ngx_snprintf(errstr, *errstr_size,
"pcre jit stack allocation failed")
- errstr;
return NGX_ERROR;
}
return NGX_OK;
#else /* LUA_HAVE_PCRE_JIT */
*errstr_size = ngx_snprintf(errstr, *errstr_size,
"no pcre jit support found")
- errstr;
return NGX_ERROR;
#endif /* LUA_HAVE_PCRE_JIT */
}
ngx_http_lua_regex_t *
ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len,
int flags, int pcre_opts, u_char *errstr,
size_t errstr_size)
{
int *cap = NULL, ovecsize;
u_char *p;
ngx_int_t rc;
const char *msg;
ngx_pool_t *pool, *old_pool;
pcre_extra *sd = NULL;
ngx_http_lua_regex_t *re;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_regex_compile_t re_comp;
pool = ngx_create_pool(512, ngx_cycle->log);
if (pool == NULL) {
msg = "no memory";
goto error;
}
pool->log = (ngx_log_t *) &ngx_cycle->new_log;
re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t));
if (re == NULL) {
ngx_destroy_pool(pool);
pool = NULL;
msg = "no memory";
goto error;
}
re->pool = pool;
re_comp.options = pcre_opts;
re_comp.pattern.data = (u_char *) pat;
re_comp.pattern.len = pat_len;
re_comp.err.len = errstr_size - 1;
re_comp.err.data = errstr;
re_comp.pool = pool;
old_pool = ngx_http_lua_pcre_malloc_init(pool);
rc = ngx_http_lua_regex_compile(&re_comp);
ngx_http_lua_pcre_malloc_done(old_pool);
if (rc != NGX_OK) {
re_comp.err.data[re_comp.err.len] = '\0';
msg = (char *) re_comp.err.data;
goto error;
}
lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
ngx_http_lua_module);
ngx_http_lua_assert(lmcf != NULL);
#if (LUA_HAVE_PCRE_JIT)
if (flags & NGX_LUA_RE_MODE_JIT) {
old_pool = ngx_http_lua_pcre_malloc_init(pool);
sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg);
ngx_http_lua_pcre_malloc_done(old_pool);
# if (NGX_DEBUG)
if (msg != NULL) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"pcre study failed with PCRE_STUDY_JIT_COMPILE: "
"%s (%p)", msg, sd);
}
if (sd != NULL) {
int jitted;
old_pool = ngx_http_lua_pcre_malloc_init(pool);
pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted);
ngx_http_lua_pcre_malloc_done(old_pool);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"pcre JIT compiling result: %d", jitted);
}
# endif /* !(NGX_DEBUG) */
} else {
old_pool = ngx_http_lua_pcre_malloc_init(pool);
sd = pcre_study(re_comp.regex, 0, &msg);
ngx_http_lua_pcre_malloc_done(old_pool);
}
if (sd && lmcf->jit_stack) {
pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack);
}
#endif /* LUA_HAVE_PCRE_JIT */
if (sd
&& lmcf && lmcf->regex_match_limit > 0
&& !(flags & NGX_LUA_RE_MODE_DFA))
{
sd->flags |= PCRE_EXTRA_MATCH_LIMIT;
sd->match_limit = lmcf->regex_match_limit;
}
if (flags & NGX_LUA_RE_MODE_DFA) {
ovecsize = 2;
re_comp.captures = 0;
} else {
ovecsize = (re_comp.captures + 1) * 3;
}
dd("allocating cap with size: %d", (int) ovecsize);
cap = ngx_palloc(pool, ovecsize * sizeof(int));
if (cap == NULL) {
msg = "no memory";
goto error;
}
if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT,
&re->name_count) != 0)
{
msg = "cannot acquire named subpattern count";
goto error;
}
if (re->name_count > 0) {
if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE,
&re->name_entry_size) != 0)
{
msg = "cannot acquire named subpattern entry size";
goto error;
}
if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE,
&re->name_table) != 0)
{
msg = "cannot acquire named subpattern table";
goto error;
}
}
re->regex = re_comp.regex;
re->regex_sd = sd;
re->ncaptures = re_comp.captures;
re->captures = cap;
re->replace = NULL;
/* only for (stap) debugging, the pointer might be invalid when the
* string is collected later on.... */
re->pattern = pat;
return re;
error:
p = ngx_snprintf(errstr, errstr_size - 1, "%s", msg);
*p = '\0';
if (sd) {
ngx_http_lua_regex_free_study_data(pool, sd);
}
if (pool) {
ngx_destroy_pool(pool);
}
return NULL;
}
int
ngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags,
const u_char *s, size_t len, int pos)
{
int rc, ovecsize, exec_opts, *cap;
ngx_str_t subj;
pcre_extra *sd;
cap = re->captures;
sd = re->regex_sd;
if (flags & NGX_LUA_RE_MODE_DFA) {
ovecsize = 2;
re->ncaptures = 0;
} else {
ovecsize = (re->ncaptures + 1) * 3;
}
if (flags & NGX_LUA_RE_NO_UTF8_CHECK) {
exec_opts = PCRE_NO_UTF8_CHECK;
} else {
exec_opts = 0;
}
subj.data = (u_char *) s;
subj.len = len;
if (flags & NGX_LUA_RE_MODE_DFA) {
#if LUA_HAVE_PCRE_DFA
int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT];
rc = ngx_http_lua_regex_dfa_exec(re->regex, sd, &subj,
(int) pos, cap, ovecsize, ws,
sizeof(ws)/sizeof(ws[0]), exec_opts);
#else
return PCRE_ERROR_BADOPTION;
#endif /* LUA_HAVE_PCRE_DFA */
} else {
rc = ngx_http_lua_regex_exec(re->regex, sd, &subj, (int) pos, cap,
ovecsize, exec_opts);
}
return rc;
}
void
ngx_http_lua_ffi_destroy_regex(ngx_http_lua_regex_t *re)
{
ngx_pool_t *old_pool;
dd("destroy regex called");
if (re == NULL || re->pool == NULL) {
return;
}
if (re->regex_sd) {
old_pool = ngx_http_lua_pcre_malloc_init(re->pool);
#if LUA_HAVE_PCRE_JIT
pcre_free_study(re->regex_sd);
#else
pcre_free(re->regex_sd);
#endif
ngx_http_lua_pcre_malloc_done(old_pool);
re->regex_sd = NULL;
}
ngx_destroy_pool(re->pool);
}
int
ngx_http_lua_ffi_compile_replace_template(ngx_http_lua_regex_t *re,
const u_char *replace_data, size_t replace_len)
{
ngx_int_t rc;
ngx_str_t tpl;
ngx_http_lua_complex_value_t *ctpl;
ngx_http_lua_compile_complex_value_t ccv;
ctpl = ngx_palloc(re->pool, sizeof(ngx_http_lua_complex_value_t));
if (ctpl == NULL) {
return NGX_ERROR;
}
if (replace_len != 0) {
/* copy the string buffer pointed to by tpl.data from Lua VM */
tpl.data = ngx_palloc(re->pool, replace_len + 1);
if (tpl.data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(tpl.data, replace_data, replace_len);
tpl.data[replace_len] = '\0';
} else {
tpl.data = (u_char *) replace_data;
}
tpl.len = replace_len;
ngx_memzero(&ccv, sizeof(ngx_http_lua_compile_complex_value_t));
ccv.pool = re->pool;
ccv.log = ngx_cycle->log;
ccv.value = &tpl;
ccv.complex_value = ctpl;
rc = ngx_http_lua_compile_complex_value(&ccv);
re->replace = ctpl;
return rc;
}
ngx_http_lua_script_engine_t *
ngx_http_lua_ffi_create_script_engine(void)
{
return ngx_calloc(sizeof(ngx_http_lua_script_engine_t), ngx_cycle->log);
}
void
ngx_http_lua_ffi_init_script_engine(ngx_http_lua_script_engine_t *e,
const unsigned char *subj, ngx_http_lua_regex_t *compiled, int count)
{
e->log = ngx_cycle->log;
e->ncaptures = count * 2;
e->captures = compiled->captures;
e->captures_data = (u_char *) subj;
}
void
ngx_http_lua_ffi_destroy_script_engine(ngx_http_lua_script_engine_t *e)
{
ngx_free(e);
}
size_t
ngx_http_lua_ffi_script_eval_len(ngx_http_lua_script_engine_t *e,
ngx_http_lua_complex_value_t *val)
{
size_t len;
ngx_http_lua_script_len_code_pt lcode;
e->ip = val->lengths;
len = 0;
while (*(uintptr_t *) e->ip) {
lcode = *(ngx_http_lua_script_len_code_pt *) e->ip;
len += lcode(e);
}
return len;
}
void
ngx_http_lua_ffi_script_eval_data(ngx_http_lua_script_engine_t *e,
ngx_http_lua_complex_value_t *val, u_char *dst)
{
ngx_http_lua_script_code_pt code;
e->ip = val->values;
e->pos = dst;
while (*(uintptr_t *) e->ip) {
code = *(ngx_http_lua_script_code_pt *) e->ip;
code(e);
}
}
uint32_t
ngx_http_lua_ffi_max_regex_cache_size(void)
{
ngx_http_lua_main_conf_t *lmcf;
lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
ngx_http_lua_module);
if (lmcf == NULL) {
return 0;
}
return (uint32_t) lmcf->regex_cache_max_entries;
}
const char *
ngx_http_lua_ffi_pcre_version(void)
{
return pcre_version();
}
#endif /* NGX_PCRE */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_
#define _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_
#include "ngx_http_lua_common.h"
void ngx_http_lua_inject_req_body_api(lua_State *L);
#endif /* _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,119 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_subrequest.h"
int
ngx_http_lua_ffi_req_get_method(ngx_http_request_t *r)
{
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
return r->method;
}
int
ngx_http_lua_ffi_req_get_method_name(ngx_http_request_t *r, u_char **name,
size_t *len)
{
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
*name = r->method_name.data;
*len = r->method_name.len;
return NGX_OK;
}
int
ngx_http_lua_ffi_req_set_method(ngx_http_request_t *r, int method)
{
if (r->connection->fd == (ngx_socket_t) -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
switch (method) {
case NGX_HTTP_GET:
r->method_name = ngx_http_lua_get_method;
break;
case NGX_HTTP_POST:
r->method_name = ngx_http_lua_post_method;
break;
case NGX_HTTP_PUT:
r->method_name = ngx_http_lua_put_method;
break;
case NGX_HTTP_HEAD:
r->method_name = ngx_http_lua_head_method;
break;
case NGX_HTTP_DELETE:
r->method_name = ngx_http_lua_delete_method;
break;
case NGX_HTTP_OPTIONS:
r->method_name = ngx_http_lua_options_method;
break;
case NGX_HTTP_MKCOL:
r->method_name = ngx_http_lua_mkcol_method;
break;
case NGX_HTTP_COPY:
r->method_name = ngx_http_lua_copy_method;
break;
case NGX_HTTP_MOVE:
r->method_name = ngx_http_lua_move_method;
break;
case NGX_HTTP_PROPFIND:
r->method_name = ngx_http_lua_propfind_method;
break;
case NGX_HTTP_PROPPATCH:
r->method_name = ngx_http_lua_proppatch_method;
break;
case NGX_HTTP_LOCK:
r->method_name = ngx_http_lua_lock_method;
break;
case NGX_HTTP_UNLOCK:
r->method_name = ngx_http_lua_unlock_method;
break;
case NGX_HTTP_PATCH:
r->method_name = ngx_http_lua_patch_method;
break;
case NGX_HTTP_TRACE:
r->method_name = ngx_http_lua_trace_method;
break;
default:
return NGX_DECLINED;
}
r->method = method;
return NGX_OK;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,381 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include "ngx_http_lua_rewriteby.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_cache.h"
static ngx_int_t ngx_http_lua_rewrite_by_chunk(lua_State *L,
ngx_http_request_t *r);
ngx_int_t
ngx_http_lua_rewrite_handler(ngx_http_request_t *r)
{
ngx_http_lua_loc_conf_t *llcf;
ngx_http_lua_ctx_t *ctx;
ngx_int_t rc;
ngx_http_lua_main_conf_t *lmcf;
/* XXX we need to take into account ngx_rewrite's location dump */
if (r->uri_changed) {
return NGX_DECLINED;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua rewrite handler, uri:\"%V\" c:%ud", &r->uri,
r->main->count);
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
if (!lmcf->postponed_to_rewrite_phase_end) {
ngx_http_core_main_conf_t *cmcf;
ngx_http_phase_handler_t tmp;
ngx_http_phase_handler_t *ph;
ngx_http_phase_handler_t *cur_ph;
ngx_http_phase_handler_t *last_ph;
lmcf->postponed_to_rewrite_phase_end = 1;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
cur_ph = &ph[r->phase_handler];
last_ph = &ph[cur_ph->next - 1];
#if 0
if (cur_ph == last_ph) {
dd("XXX our handler is already the last rewrite phase handler");
}
#endif
if (cur_ph < last_ph) {
dd("swapping the contents of cur_ph and last_ph...");
tmp = *cur_ph;
memmove(cur_ph, cur_ph + 1,
(last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t));
*last_ph = tmp;
r->phase_handler--; /* redo the current ph */
return NGX_DECLINED;
}
}
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->rewrite_handler == NULL) {
dd("no rewrite handler found");
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
dd("entered? %d", (int) ctx->entered_rewrite_phase);
if (ctx->entered_rewrite_phase) {
dd("rewriteby: calling wev handler");
rc = ctx->resume_handler(r);
dd("rewriteby: wev handler returns %d", (int) rc);
if (rc == NGX_OK) {
rc = NGX_DECLINED;
}
if (rc == NGX_DECLINED) {
if (r->header_sent) {
dd("header already sent");
/* response header was already generated in rewrite_by_lua*,
* so it is no longer safe to proceed to later phases
* which may generate responses again */
if (!ctx->eof) {
dd("eof not yet sent");
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
/* indicate last_buf */);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
}
return NGX_HTTP_OK;
}
r->write_event_handler = ngx_http_core_run_phases;
ctx->entered_rewrite_phase = 0;
return NGX_DECLINED;
}
return rc;
}
if (ctx->waiting_more_body) {
return NGX_DONE;
}
if (llcf->force_read_body && !ctx->read_body_done) {
r->request_body_in_single_buf = 1;
r->request_body_in_persistent_file = 1;
r->request_body_in_clean_file = 1;
rc = ngx_http_read_client_request_body(r,
ngx_http_lua_generic_phase_post_read);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
if (rc == NGX_AGAIN) {
ctx->waiting_more_body = 1;
return NGX_DONE;
}
}
dd("calling rewrite handler");
return llcf->rewrite_handler(r);
}
ngx_int_t
ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
ngx_http_lua_loc_conf_t *llcf;
dd("rewrite by lua inline");
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
llcf->rewrite_src.value.data,
llcf->rewrite_src.value.len,
&llcf->rewrite_src_ref,
llcf->rewrite_src_key,
(const char *)
llcf->rewrite_chunkname);
if (rc != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return ngx_http_lua_rewrite_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r)
{
lua_State *L;
ngx_int_t rc;
u_char *script_path;
ngx_http_lua_loc_conf_t *llcf;
ngx_str_t eval_src;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (ngx_http_complex_value(r, &llcf->rewrite_src, &eval_src) != NGX_OK) {
return NGX_ERROR;
}
script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
eval_src.len);
if (script_path == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
&llcf->rewrite_src_ref,
llcf->rewrite_src_key);
if (rc != NGX_OK) {
if (rc < NGX_HTTP_SPECIAL_RESPONSE) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return rc;
}
return ngx_http_lua_rewrite_by_chunk(L, r);
}
static ngx_int_t
ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r)
{
int co_ref;
lua_State *co;
ngx_int_t rc;
ngx_uint_t nreqs;
ngx_event_t *rev;
ngx_connection_t *c;
ngx_http_lua_ctx_t *ctx;
ngx_pool_cleanup_t *cln;
ngx_http_lua_loc_conf_t *llcf;
/* {{{ new coroutine to handle request */
co = ngx_http_lua_new_thread(r, L, &co_ref);
if (co == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"lua: failed to create new coroutine to handle request");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* move code closure to new coroutine */
lua_xmove(L, co, 1);
#ifndef OPENRESTY_LUAJIT
/* set closure's env table to new coroutine's globals table */
ngx_http_lua_get_globals_table(co);
lua_setfenv(co, -2);
#endif
/* save nginx request in coroutine globals table */
ngx_http_lua_set_req(co, r);
/* {{{ initialize request context */
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_lua_reset_ctx(r, L, ctx);
ctx->entered_rewrite_phase = 1;
ctx->cur_co_ctx = &ctx->entry_co_ctx;
ctx->cur_co_ctx->co = co;
ctx->cur_co_ctx->co_ref = co_ref;
#ifdef NGX_LUA_USE_ASSERT
ctx->cur_co_ctx->co_top = 1;
#endif
ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
/* }}} */
/* {{{ register nginx pool cleanup hooks */
if (ctx->cleanup == NULL) {
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cln->handler = ngx_http_lua_request_cleanup_handler;
cln->data = ctx;
ctx->cleanup = &cln->handler;
}
/* }}} */
ctx->context = NGX_HTTP_LUA_CONTEXT_REWRITE;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->check_client_abort) {
r->read_event_handler = ngx_http_lua_rd_check_broken_connection;
#if (NGX_HTTP_V2)
if (!r->stream) {
#endif
rev = r->connection->read;
if (!rev->active) {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
return NGX_ERROR;
}
}
#if (NGX_HTTP_V2)
}
#endif
} else {
r->read_event_handler = ngx_http_block_reading;
}
c = r->connection;
nreqs = c->requests;
rc = ngx_http_lua_run_thread(L, r, ctx, 0);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
if (rc == NGX_AGAIN) {
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);
} else if (rc == NGX_DONE) {
ngx_http_lua_finalize_request(r, NGX_DONE);
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);
}
if (rc == NGX_OK || rc == NGX_DECLINED) {
if (r->header_sent) {
dd("header already sent");
/* response header was already generated in rewrite_by_lua*,
* so it is no longer safe to proceed to later phases
* which may generate responses again */
if (!ctx->eof) {
dd("eof not yet sent");
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
/* indicate last_buf */);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
}
return NGX_HTTP_OK;
}
r->write_event_handler = ngx_http_core_run_phases;
ctx->entered_rewrite_phase = 0;
return NGX_DECLINED;
}
return rc;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_
#define _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_rewrite_handler(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r);
#endif /* _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,538 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_script.h"
static void *ngx_http_lua_script_add_code(ngx_array_t *codes, size_t size);
static size_t ngx_http_lua_script_copy_len_code(
ngx_http_lua_script_engine_t *e);
static void ngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e);
static ngx_int_t ngx_http_lua_script_add_copy_code(
ngx_http_lua_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last);
static ngx_int_t ngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc);
static ngx_int_t ngx_http_lua_script_add_capture_code(
ngx_http_lua_script_compile_t *sc, ngx_uint_t n);
static size_t ngx_http_lua_script_copy_capture_len_code(
ngx_http_lua_script_engine_t *e);
static void ngx_http_lua_script_copy_capture_code(
ngx_http_lua_script_engine_t *e);
static ngx_int_t ngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc);
static ngx_int_t ngx_http_lua_script_init_arrays(
ngx_http_lua_script_compile_t *sc);
ngx_int_t
ngx_http_lua_compile_complex_value(ngx_http_lua_compile_complex_value_t *ccv)
{
ngx_str_t *v;
ngx_uint_t i, n, nv;
ngx_array_t lengths, values, *pl, *pv;
ngx_http_lua_script_compile_t sc;
v = ccv->value;
nv = 0;
for (i = 0; i < v->len; i++) {
if (v->data[i] == '$') {
nv++;
}
}
ccv->complex_value->value = *v;
ccv->complex_value->lengths = NULL;
ccv->complex_value->values = NULL;
if (nv == 0) {
return NGX_OK;
}
n = nv * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t);
if (ngx_array_init(&lengths, ccv->pool, n, 1) != NGX_OK) {
return NGX_ERROR;
}
n = (nv * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t)
+ sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
if (ngx_array_init(&values, ccv->pool, n, 1) != NGX_OK) {
return NGX_ERROR;
}
pl = &lengths;
pv = &values;
ngx_memzero(&sc, sizeof(ngx_http_lua_script_compile_t));
sc.pool = ccv->pool;
sc.log = ccv->log;
sc.source = v;
sc.lengths = &pl;
sc.values = &pv;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_lua_script_compile(&sc) != NGX_OK) {
ngx_array_destroy(&lengths);
ngx_array_destroy(&values);
return NGX_ERROR;
}
ccv->complex_value->lengths = lengths.elts;
ccv->complex_value->values = values.elts;
return NGX_OK;
}
ngx_int_t
ngx_http_lua_complex_value(ngx_http_request_t *r, ngx_str_t *subj,
size_t offset, ngx_int_t count, int *cap,
ngx_http_lua_complex_value_t *val, luaL_Buffer *luabuf)
{
size_t len;
u_char *p;
ngx_http_lua_script_code_pt code;
ngx_http_lua_script_len_code_pt lcode;
ngx_http_lua_script_engine_t e;
if (val->lengths == NULL) {
luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);
luaL_addlstring(luabuf, (char *) val->value.data, val->value.len);
return NGX_OK;
}
ngx_memzero(&e, sizeof(ngx_http_lua_script_engine_t));
e.log = r->connection->log;
e.ncaptures = count * 2;
e.captures = cap;
e.captures_data = subj->data;
e.ip = val->lengths;
len = 0;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_lua_script_len_code_pt *) e.ip;
len += lcode(&e);
}
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
e.ip = val->values;
e.pos = p;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_lua_script_code_pt *) e.ip;
code((ngx_http_lua_script_engine_t *) &e);
}
luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);
luaL_addlstring(luabuf, (char *) p, len);
ngx_pfree(r->pool, p);
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc)
{
u_char ch;
ngx_str_t name;
ngx_uint_t i, bracket;
unsigned num_var;
ngx_uint_t n = 0;
if (ngx_http_lua_script_init_arrays(sc) != NGX_OK) {
return NGX_ERROR;
}
for (i = 0; i < sc->source->len; /* void */ ) {
name.len = 0;
if (sc->source->data[i] == '$') {
if (++i == sc->source->len) {
goto invalid_variable;
}
if (sc->source->data[i] == '$') {
name.data = &sc->source->data[i];
i++;
name.len++;
if (ngx_http_lua_script_add_copy_code(sc, &name,
(i == sc->source->len))
!= NGX_OK)
{
return NGX_ERROR;
}
continue;
}
if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {
num_var = 1;
n = 0;
} else {
num_var = 0;
}
if (sc->source->data[i] == '{') {
bracket = 1;
if (++i == sc->source->len) {
goto invalid_variable;
}
if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {
num_var = 1;
n = 0;
}
name.data = &sc->source->data[i];
} else {
bracket = 0;
name.data = &sc->source->data[i];
}
for ( /* void */ ; i < sc->source->len; i++, name.len++) {
ch = sc->source->data[i];
if (ch == '}' && bracket) {
i++;
bracket = 0;
break;
}
if (num_var) {
if (ch >= '0' && ch <= '9') {
n = n * 10 + (ch - '0');
continue;
}
break;
}
/* not a number variable like $1, $2, etc */
if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
{
continue;
}
break;
}
if (bracket) {
ngx_log_error(NGX_LOG_ERR, sc->log, 0,
"the closing bracket in \"%V\" "
"variable is missing", &name);
return NGX_ERROR;
}
if (name.len == 0) {
goto invalid_variable;
}
if (!num_var) {
ngx_log_error(NGX_LOG_ERR, sc->log, 0,
"attempt to use named capturing variable "
"\"%V\" (named captures not supported yet)",
&name);
return NGX_ERROR;
}
sc->variables++;
if (ngx_http_lua_script_add_capture_code(sc, n) != NGX_OK) {
return NGX_ERROR;
}
continue;
}
name.data = &sc->source->data[i];
while (i < sc->source->len) {
if (sc->source->data[i] == '$') {
break;
}
i++;
name.len++;
}
if (ngx_http_lua_script_add_copy_code(sc, &name, (i == sc->source->len))
!= NGX_OK)
{
return NGX_ERROR;
}
}
return ngx_http_lua_script_done(sc);
invalid_variable:
ngx_log_error(NGX_LOG_ERR, sc->log, 0,
"lua script: invalid capturing variable name found in \"%V\"",
sc->source);
return NGX_ERROR;
}
static ngx_int_t
ngx_http_lua_script_add_copy_code(ngx_http_lua_script_compile_t *sc,
ngx_str_t *value, ngx_uint_t last)
{
size_t size, len;
ngx_http_lua_script_copy_code_t *code;
len = value->len;
code = ngx_http_lua_script_add_code(*sc->lengths,
sizeof(ngx_http_lua_script_copy_code_t));
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_lua_script_code_pt) (void *)
ngx_http_lua_script_copy_len_code;
code->len = len;
size = (sizeof(ngx_http_lua_script_copy_code_t) + len +
sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
code = ngx_http_lua_script_add_code(*sc->values, size);
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_lua_script_copy_code;
code->len = len;
ngx_memcpy((u_char *) code + sizeof(ngx_http_lua_script_copy_code_t),
value->data, value->len);
return NGX_OK;
}
static size_t
ngx_http_lua_script_copy_len_code(ngx_http_lua_script_engine_t *e)
{
ngx_http_lua_script_copy_code_t *code;
code = (ngx_http_lua_script_copy_code_t *) e->ip;
e->ip += sizeof(ngx_http_lua_script_copy_code_t);
return code->len;
}
static void
ngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e)
{
u_char *p;
ngx_http_lua_script_copy_code_t *code;
code = (ngx_http_lua_script_copy_code_t *) e->ip;
p = e->pos;
if (!e->skip) {
e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_lua_script_copy_code_t),
code->len);
}
e->ip += sizeof(ngx_http_lua_script_copy_code_t)
+ ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0,
"lua script copy: \"%*s\"", e->pos - p, p);
}
static ngx_int_t
ngx_http_lua_script_add_capture_code(ngx_http_lua_script_compile_t *sc,
ngx_uint_t n)
{
ngx_http_lua_script_capture_code_t *code;
code = ngx_http_lua_script_add_code(*sc->lengths,
sizeof(ngx_http_lua_script_capture_code_t));
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_lua_script_code_pt) (void *)
ngx_http_lua_script_copy_capture_len_code;
code->n = 2 * n;
code = ngx_http_lua_script_add_code(*sc->values,
sizeof(ngx_http_lua_script_capture_code_t));
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_lua_script_copy_capture_code;
code->n = 2 * n;
return NGX_OK;
}
static size_t
ngx_http_lua_script_copy_capture_len_code(ngx_http_lua_script_engine_t *e)
{
int *cap;
ngx_uint_t n;
ngx_http_lua_script_capture_code_t *code;
code = (ngx_http_lua_script_capture_code_t *) e->ip;
e->ip += sizeof(ngx_http_lua_script_capture_code_t);
n = code->n;
if (n < e->ncaptures) {
cap = e->captures;
return cap[n + 1] - cap[n];
}
return 0;
}
static void
ngx_http_lua_script_copy_capture_code(ngx_http_lua_script_engine_t *e)
{
int *cap;
u_char *p, *pos;
ngx_uint_t n;
ngx_http_lua_script_capture_code_t *code;
code = (ngx_http_lua_script_capture_code_t *) e->ip;
e->ip += sizeof(ngx_http_lua_script_capture_code_t);
n = code->n;
pos = e->pos;
if (n < e->ncaptures) {
cap = e->captures;
p = e->captures_data;
e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0,
"lua script capture: \"%*s\"", e->pos - pos, pos);
}
static ngx_int_t
ngx_http_lua_script_init_arrays(ngx_http_lua_script_compile_t *sc)
{
ngx_uint_t n;
if (*sc->lengths == NULL) {
n = sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t);
*sc->lengths = ngx_array_create(sc->pool, n, 1);
if (*sc->lengths == NULL) {
return NGX_ERROR;
}
}
if (*sc->values == NULL) {
n = (sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t)
+ sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
*sc->values = ngx_array_create(sc->pool, n, 1);
if (*sc->values == NULL) {
return NGX_ERROR;
}
}
sc->variables = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc)
{
uintptr_t *code;
if (sc->complete_lengths) {
code = ngx_http_lua_script_add_code(*sc->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
if (sc->complete_values) {
code = ngx_http_lua_script_add_code(*sc->values, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
return NGX_OK;
}
static void *
ngx_http_lua_script_add_code(ngx_array_t *codes, size_t size)
{
return ngx_array_push_n(codes, size);
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_
#define _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_
#include "ngx_http_lua_common.h"
typedef struct {
ngx_log_t *log;
ngx_pool_t *pool;
ngx_str_t *source;
ngx_array_t **lengths;
ngx_array_t **values;
ngx_uint_t variables;
unsigned complete_lengths:1;
unsigned complete_values:1;
} ngx_http_lua_script_compile_t;
typedef struct {
ngx_str_t value;
void *lengths;
void *values;
} ngx_http_lua_complex_value_t;
typedef struct {
ngx_log_t *log;
ngx_pool_t *pool;
ngx_str_t *value;
ngx_http_lua_complex_value_t *complex_value;
} ngx_http_lua_compile_complex_value_t;
typedef struct {
u_char *ip;
u_char *pos;
ngx_str_t buf;
int *captures;
ngx_uint_t ncaptures;
u_char *captures_data;
unsigned skip:1;
ngx_log_t *log;
} ngx_http_lua_script_engine_t;
typedef void (*ngx_http_lua_script_code_pt) (ngx_http_lua_script_engine_t *e);
typedef size_t (*ngx_http_lua_script_len_code_pt)
(ngx_http_lua_script_engine_t *e);
typedef struct {
ngx_http_lua_script_code_pt code;
uintptr_t len;
} ngx_http_lua_script_copy_code_t;
typedef struct {
ngx_http_lua_script_code_pt code;
uintptr_t n;
} ngx_http_lua_script_capture_code_t;
ngx_int_t ngx_http_lua_compile_complex_value(
ngx_http_lua_compile_complex_value_t *ccv);
ngx_int_t ngx_http_lua_complex_value(ngx_http_request_t *r, ngx_str_t *subj,
size_t offset, ngx_int_t count, int *cap,
ngx_http_lua_complex_value_t *val, luaL_Buffer *luabuf);
#endif /* _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,580 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
* Copyright (C) cuiweixie
* I hereby assign copyright in this code to the lua-nginx-module project,
* to be licensed under the same terms as the rest of the code.
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_semaphore.h"
#include "ngx_http_lua_contentby.h"
ngx_int_t ngx_http_lua_sema_mm_init(ngx_conf_t *cf,
ngx_http_lua_main_conf_t *lmcf);
void ngx_http_lua_sema_mm_cleanup(void *data);
static ngx_http_lua_sema_t *ngx_http_lua_alloc_sema(void);
static void ngx_http_lua_free_sema(ngx_http_lua_sema_t *sem);
static ngx_int_t ngx_http_lua_sema_resume(ngx_http_request_t *r);
int ngx_http_lua_ffi_sema_new(ngx_http_lua_sema_t **psem,
int n, char **errmsg);
int ngx_http_lua_ffi_sema_post(ngx_http_lua_sema_t *sem, int n);
int ngx_http_lua_ffi_sema_wait(ngx_http_request_t *r,
ngx_http_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen);
static void ngx_http_lua_sema_cleanup(void *data);
static void ngx_http_lua_sema_handler(ngx_event_t *ev);
static void ngx_http_lua_sema_timeout_handler(ngx_event_t *ev);
void ngx_http_lua_ffi_sema_gc(ngx_http_lua_sema_t *sem);
enum {
SEMAPHORE_WAIT_SUCC = 0,
SEMAPHORE_WAIT_TIMEOUT = 1,
};
ngx_int_t
ngx_http_lua_sema_mm_init(ngx_conf_t *cf, ngx_http_lua_main_conf_t *lmcf)
{
ngx_http_lua_sema_mm_t *mm;
mm = ngx_palloc(cf->pool, sizeof(ngx_http_lua_sema_mm_t));
if (mm == NULL) {
return NGX_ERROR;
}
lmcf->sema_mm = mm;
mm->lmcf = lmcf;
ngx_queue_init(&mm->free_queue);
mm->cur_epoch = 0;
mm->total = 0;
mm->used = 0;
/* it's better to be 4096, but it needs some space for
* ngx_http_lua_sema_mm_block_t, one is enough, so it is 4095
*/
mm->num_per_block = 4095;
return NGX_OK;
}
static ngx_http_lua_sema_t *
ngx_http_lua_alloc_sema(void)
{
ngx_uint_t i, n;
ngx_queue_t *q;
ngx_http_lua_sema_t *sem, *iter;
ngx_http_lua_sema_mm_t *mm;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_sema_mm_block_t *block;
ngx_http_lua_assert(ngx_cycle && ngx_cycle->conf_ctx);
lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,
ngx_http_lua_module);
ngx_http_lua_assert(lmcf != NULL);
mm = lmcf->sema_mm;
if (!ngx_queue_empty(&mm->free_queue)) {
q = ngx_queue_head(&mm->free_queue);
ngx_queue_remove(q);
sem = ngx_queue_data(q, ngx_http_lua_sema_t, chain);
sem->block->used++;
ngx_memzero(&sem->sem_event, sizeof(ngx_event_t));
sem->sem_event.handler = ngx_http_lua_sema_handler;
sem->sem_event.data = sem;
sem->sem_event.log = ngx_cycle->log;
mm->used++;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"from head of free queue, alloc semaphore: %p", sem);
return sem;
}
/* free_queue is empty */
n = sizeof(ngx_http_lua_sema_mm_block_t)
+ mm->num_per_block * sizeof(ngx_http_lua_sema_t);
dd("block size: %d, item size: %d",
(int) sizeof(ngx_http_lua_sema_mm_block_t),
(int) sizeof(ngx_http_lua_sema_t));
block = ngx_alloc(n, ngx_cycle->log);
if (block == NULL) {
return NULL;
}
mm->cur_epoch++;
mm->total += mm->num_per_block;
mm->used++;
block->mm = mm;
block->epoch = mm->cur_epoch;
sem = (ngx_http_lua_sema_t *) (block + 1);
sem->block = block;
sem->block->used = 1;
ngx_memzero(&sem->sem_event, sizeof(ngx_event_t));
sem->sem_event.handler = ngx_http_lua_sema_handler;
sem->sem_event.data = sem;
sem->sem_event.log = ngx_cycle->log;
for (iter = sem + 1, i = 1; i < mm->num_per_block; i++, iter++) {
iter->block = block;
ngx_queue_insert_tail(&mm->free_queue, &iter->chain);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"new block, alloc semaphore: %p block: %p", sem, block);
return sem;
}
void
ngx_http_lua_sema_mm_cleanup(void *data)
{
ngx_uint_t i;
ngx_queue_t *q;
ngx_http_lua_sema_t *sem, *iter;
ngx_http_lua_sema_mm_t *mm;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_sema_mm_block_t *block;
lmcf = (ngx_http_lua_main_conf_t *) data;
mm = lmcf->sema_mm;
while (!ngx_queue_empty(&mm->free_queue)) {
q = ngx_queue_head(&mm->free_queue);
sem = ngx_queue_data(q, ngx_http_lua_sema_t, chain);
block = sem->block;
ngx_http_lua_assert(block != NULL);
if (block->used == 0) {
iter = (ngx_http_lua_sema_t *) (block + 1);
for (i = 0; i < block->mm->num_per_block; i++, iter++) {
ngx_queue_remove(&iter->chain);
}
dd("free sema block: %p at final", block);
ngx_free(block);
} else {
/* just return directly when some thing goes wrong */
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"lua sema mm: freeing a block %p that is still "
" used by someone", block);
return;
}
}
dd("lua sema mm cleanup done");
}
static void
ngx_http_lua_free_sema(ngx_http_lua_sema_t *sem)
{
ngx_http_lua_sema_t *iter;
ngx_uint_t i, mid_epoch;
ngx_http_lua_sema_mm_block_t *block;
ngx_http_lua_sema_mm_t *mm;
block = sem->block;
block->used--;
mm = block->mm;
mm->used--;
mid_epoch = mm->cur_epoch - ((mm->total / mm->num_per_block) >> 1);
if (block->epoch < mid_epoch) {
ngx_queue_insert_tail(&mm->free_queue, &sem->chain);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"add to free queue tail semaphore: %p epoch: %d"
"mid_epoch: %d cur_epoch: %d", sem, (int) block->epoch,
(int) mid_epoch, (int) mm->cur_epoch);
} else {
ngx_queue_insert_head(&mm->free_queue, &sem->chain);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"add to free queue head semaphore: %p epoch: %d"
"mid_epoch: %d cur_epoch: %d", sem, (int) block->epoch,
(int) mid_epoch, (int) mm->cur_epoch);
}
dd("used: %d", (int) block->used);
if (block->used == 0
&& mm->used <= (mm->total >> 1)
&& block->epoch < mid_epoch)
{
/* load <= 50% and it's on the older side */
iter = (ngx_http_lua_sema_t *) (block + 1);
for (i = 0; i < mm->num_per_block; i++, iter++) {
ngx_queue_remove(&iter->chain);
}
mm->total -= mm->num_per_block;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"free semaphore block: %p", block);
ngx_free(block);
}
}
static ngx_int_t
ngx_http_lua_sema_resume(ngx_http_request_t *r)
{
lua_State *vm;
ngx_connection_t *c;
ngx_int_t rc;
ngx_uint_t nreqs;
ngx_http_lua_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
return NGX_ERROR;
}
ctx->resume_handler = ngx_http_lua_wev_handler;
c = r->connection;
vm = ngx_http_lua_get_lua_vm(r, ctx);
nreqs = c->requests;
if (ctx->cur_co_ctx->sem_resume_status == SEMAPHORE_WAIT_SUCC) {
lua_pushboolean(ctx->cur_co_ctx->co, 1);
lua_pushnil(ctx->cur_co_ctx->co);
} else {
lua_pushboolean(ctx->cur_co_ctx->co, 0);
lua_pushliteral(ctx->cur_co_ctx->co, "timeout");
}
rc = ngx_http_lua_run_thread(vm, r, ctx, 2);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua run thread returned %d", rc);
if (rc == NGX_AGAIN) {
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
}
if (rc == NGX_DONE) {
ngx_http_lua_finalize_request(r, NGX_DONE);
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
}
/* rc == NGX_ERROR || rc >= NGX_OK */
if (ctx->entered_content_phase) {
ngx_http_lua_finalize_request(r, rc);
return NGX_DONE;
}
return rc;
}
int
ngx_http_lua_ffi_sema_new(ngx_http_lua_sema_t **psem,
int n, char **errmsg)
{
ngx_http_lua_sema_t *sem;
sem = ngx_http_lua_alloc_sema();
if (sem == NULL) {
*errmsg = "no memory";
return NGX_ERROR;
}
ngx_queue_init(&sem->wait_queue);
sem->resource_count = n;
sem->wait_count = 0;
*psem = sem;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"http lua semaphore new: %p, resources: %d",
sem, sem->resource_count);
return NGX_OK;
}
int
ngx_http_lua_ffi_sema_post(ngx_http_lua_sema_t *sem, int n)
{
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"http lua semaphore post: %p, n: %d, resources: %d",
sem, n, sem->resource_count);
sem->resource_count += n;
if (!ngx_queue_empty(&sem->wait_queue)) {
/* we need the extra parentheses around the first argument of
* ngx_post_event() just to work around macro issues in nginx
* cores older than nginx 1.7.12 (exclusive).
*/
ngx_post_event((&sem->sem_event), &ngx_posted_events);
}
return NGX_OK;
}
int
ngx_http_lua_ffi_sema_wait(ngx_http_request_t *r,
ngx_http_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen)
{
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *wait_co_ctx;
ngx_int_t rc;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"http lua semaphore wait: %p, timeout: %d, "
"resources: %d, event posted: %d",
sem, wait_ms, sem->resource_count,
#if (nginx_version >= 1007005)
(int) sem->sem_event.posted
#else
sem->sem_event.prev ? 1 : 0
#endif
);
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
*errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err;
return NGX_ERROR;
}
rc = ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE,
err, errlen);
if (rc != NGX_OK) {
return NGX_ERROR;
}
/* we keep the order, will first resume the thread waiting for the
* longest time in ngx_http_lua_sema_handler
*/
if (ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) {
sem->resource_count--;
return NGX_OK;
}
if (wait_ms == 0) {
return NGX_DECLINED;
}
sem->wait_count++;
wait_co_ctx = ctx->cur_co_ctx;
wait_co_ctx->sleep.handler = ngx_http_lua_sema_timeout_handler;
wait_co_ctx->sleep.data = ctx->cur_co_ctx;
wait_co_ctx->sleep.log = r->connection->log;
ngx_add_timer(&wait_co_ctx->sleep, (ngx_msec_t) wait_ms);
dd("ngx_http_lua_ffi_sema_wait add timer coctx:%p wait: %d(ms)",
wait_co_ctx, wait_ms);
ngx_queue_insert_tail(&sem->wait_queue, &wait_co_ctx->sem_wait_queue);
wait_co_ctx->data = sem;
wait_co_ctx->cleanup = ngx_http_lua_sema_cleanup;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"http lua semaphore wait yielding");
return NGX_AGAIN;
}
int
ngx_http_lua_ffi_sema_count(ngx_http_lua_sema_t *sem)
{
return sem->resource_count - sem->wait_count;
}
static void
ngx_http_lua_sema_cleanup(void *data)
{
ngx_http_lua_co_ctx_t *coctx = data;
ngx_queue_t *q;
ngx_http_lua_sema_t *sem;
sem = coctx->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"http lua semaphore cleanup");
if (coctx->sleep.timer_set) {
ngx_del_timer(&coctx->sleep);
}
q = &coctx->sem_wait_queue;
ngx_queue_remove(q);
sem->wait_count--;
coctx->cleanup = NULL;
}
static void
ngx_http_lua_sema_handler(ngx_event_t *ev)
{
ngx_http_lua_sema_t *sem;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *wait_co_ctx;
ngx_connection_t *c;
ngx_queue_t *q;
sem = ev->data;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"semaphore handler: wait queue: %sempty, resource count: %d",
ngx_queue_empty(&sem->wait_queue) ? "" : "not ",
sem->resource_count);
while (!ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) {
q = ngx_queue_head(&sem->wait_queue);
ngx_queue_remove(q);
sem->wait_count--;
wait_co_ctx = ngx_queue_data(q, ngx_http_lua_co_ctx_t, sem_wait_queue);
wait_co_ctx->cleanup = NULL;
if (wait_co_ctx->sleep.timer_set) {
ngx_del_timer(&wait_co_ctx->sleep);
}
r = ngx_http_lua_get_req(wait_co_ctx->co);
c = r->connection;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
ngx_http_lua_assert(ctx != NULL);
sem->resource_count--;
ctx->cur_co_ctx = wait_co_ctx;
wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_SUCC;
if (ctx->entered_content_phase) {
(void) ngx_http_lua_sema_resume(r);
} else {
ctx->resume_handler = ngx_http_lua_sema_resume;
ngx_http_core_run_phases(r);
}
ngx_http_run_posted_requests(c);
}
}
static void
ngx_http_lua_sema_timeout_handler(ngx_event_t *ev)
{
ngx_http_lua_co_ctx_t *wait_co_ctx;
ngx_http_request_t *r;
ngx_http_lua_ctx_t *ctx;
ngx_connection_t *c;
ngx_http_lua_sema_t *sem;
wait_co_ctx = ev->data;
wait_co_ctx->cleanup = NULL;
dd("ngx_http_lua_sema_timeout_handler timeout coctx:%p", wait_co_ctx);
sem = wait_co_ctx->data;
ngx_queue_remove(&wait_co_ctx->sem_wait_queue);
sem->wait_count--;
r = ngx_http_lua_get_req(wait_co_ctx->co);
c = r->connection;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
ngx_http_lua_assert(ctx != NULL);
ctx->cur_co_ctx = wait_co_ctx;
wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_TIMEOUT;
if (ctx->entered_content_phase) {
(void) ngx_http_lua_sema_resume(r);
} else {
ctx->resume_handler = ngx_http_lua_sema_resume;
ngx_http_core_run_phases(r);
}
ngx_http_run_posted_requests(c);
}
void
ngx_http_lua_ffi_sema_gc(ngx_http_lua_sema_t *sem)
{
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"in lua gc, semaphore %p", sem);
if (sem == NULL) {
return;
}
if (!ngx_terminate
&& !ngx_quit
&& !ngx_queue_empty(&sem->wait_queue))
{
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"in lua semaphore gc wait queue is"
" not empty while the semaphore %p is being "
"destroyed", sem);
}
if (sem->sem_event.posted) {
ngx_delete_posted_event(&sem->sem_event);
}
ngx_http_lua_free_sema(sem);
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
* Copyright (C) cuiweixie
* I hereby assign copyright in this code to the lua-nginx-module project,
* to be licensed under the same terms as the rest of the code.
*/
#ifndef _NGX_HTTP_LUA_SEMAPHORE_H_INCLUDED_
#define _NGX_HTTP_LUA_SEMAPHORE_H_INCLUDED_
#include "ngx_http_lua_common.h"
typedef struct ngx_http_lua_sema_mm_block_s {
ngx_uint_t used;
ngx_http_lua_sema_mm_t *mm;
ngx_uint_t epoch;
} ngx_http_lua_sema_mm_block_t;
struct ngx_http_lua_sema_mm_s {
ngx_queue_t free_queue;
ngx_uint_t total;
ngx_uint_t used;
ngx_uint_t num_per_block;
ngx_uint_t cur_epoch;
ngx_http_lua_main_conf_t *lmcf;
};
typedef struct ngx_http_lua_sema_s {
ngx_queue_t wait_queue;
ngx_queue_t chain;
ngx_event_t sem_event;
ngx_http_lua_sema_mm_block_t *block;
int resource_count;
unsigned wait_count;
} ngx_http_lua_sema_t;
void ngx_http_lua_sema_mm_cleanup(void *data);
ngx_int_t ngx_http_lua_sema_mm_init(ngx_conf_t *cf,
ngx_http_lua_main_conf_t *lmcf);
#endif /* _NGX_HTTP_LUA_SEMAPHORE_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,339 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include <nginx.h>
#include "ngx_http_lua_server_rewriteby.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_cache.h"
static ngx_int_t ngx_http_lua_server_rewrite_by_chunk(lua_State *L,
ngx_http_request_t *r);
ngx_int_t
ngx_http_lua_server_rewrite_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
lua_State *L;
ngx_http_lua_srv_conf_t *lscf;
ngx_http_lua_loc_conf_t *llcf;
ngx_http_lua_ctx_t *ctx;
/* XXX we need to take into account ngx_rewrite's location dump */
if (r->uri_changed) {
return NGX_DECLINED;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua server rewrite handler, uri:\"%V\" c:%ud", &r->uri,
r->main->count);
lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);
if (lscf->srv.server_rewrite_handler == NULL) {
dd("no rewrite handler found");
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
dd("entered? %d", (int) ctx->entered_server_rewrite_phase);
if (ctx->entered_server_rewrite_phase) {
dd("rewriteby: calling wev handler");
rc = ctx->resume_handler(r);
dd("rewriteby: wev handler returns %d", (int) rc);
if (rc == NGX_OK) {
rc = NGX_DECLINED;
}
if (rc == NGX_DECLINED) {
if (r->header_sent) {
dd("header already sent");
/* response header was already generated in rewrite_by_lua*,
* so it is no longer safe to proceed to later phases
* which may generate responses again */
if (!ctx->eof) {
dd("eof not yet sent");
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
/* indicate last_buf */);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
}
return NGX_HTTP_OK;
}
r->write_event_handler = ngx_http_core_run_phases;
ctx->entered_server_rewrite_phase = 0;
return NGX_DECLINED;
}
return rc;
}
if (ctx->waiting_more_body) {
return NGX_DONE;
}
/* TODO: lscf do not have force_read_body */
if (llcf->force_read_body && !ctx->read_body_done) {
r->request_body_in_single_buf = 1;
r->request_body_in_persistent_file = 1;
r->request_body_in_clean_file = 1;
rc = ngx_http_read_client_request_body(r,
ngx_http_lua_generic_phase_post_read);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
if (rc == NGX_AGAIN) {
ctx->waiting_more_body = 1;
return NGX_DONE;
}
}
dd("calling server rewrite handler");
return lscf->srv.server_rewrite_handler(r, lscf, L);
}
ngx_int_t
ngx_http_lua_server_rewrite_handler_inline(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L)
{
ngx_int_t rc;
dd("server_rewrite by lua inline");
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
lscf->srv.server_rewrite_src.value.data,
lscf->srv.server_rewrite_src.value.len,
&lscf->srv.server_rewrite_src_ref,
lscf->srv.server_rewrite_src_key,
(const char *)
lscf->srv.server_rewrite_chunkname);
if (rc != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return ngx_http_lua_server_rewrite_by_chunk(L, r);
}
ngx_int_t
ngx_http_lua_server_rewrite_handler_file(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L)
{
ngx_int_t rc;
u_char *script_path;
ngx_str_t eval_src;
if (ngx_http_complex_value(r, &lscf->srv.server_rewrite_src,
&eval_src) != NGX_OK)
{
return NGX_ERROR;
}
script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
eval_src.len);
if (script_path == NULL) {
return NGX_ERROR;
}
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
&lscf->srv.server_rewrite_src_ref,
lscf->srv.server_rewrite_src_key);
if (rc != NGX_OK) {
if (rc < NGX_HTTP_SPECIAL_RESPONSE) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return rc;
}
return ngx_http_lua_server_rewrite_by_chunk(L, r);
}
static ngx_int_t
ngx_http_lua_server_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r)
{
int co_ref;
lua_State *co;
ngx_int_t rc;
ngx_uint_t nreqs;
ngx_event_t *rev;
ngx_connection_t *c;
ngx_http_lua_ctx_t *ctx;
ngx_pool_cleanup_t *cln;
ngx_http_lua_loc_conf_t *llcf;
/* {{{ new coroutine to handle request */
co = ngx_http_lua_new_thread(r, L, &co_ref);
if (co == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"lua: failed to create new coroutine to handle request");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* move code closure to new coroutine */
lua_xmove(L, co, 1);
#ifndef OPENRESTY_LUAJIT
/* set closure's env table to new coroutine's globals table */
ngx_http_lua_get_globals_table(co);
lua_setfenv(co, -2);
#endif
/* save nginx request in coroutine globals table */
ngx_http_lua_set_req(co, r);
/* {{{ initialize request context */
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
dd("ctx = %p", ctx);
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_lua_reset_ctx(r, L, ctx);
ctx->entered_server_rewrite_phase = 1;
ctx->cur_co_ctx = &ctx->entry_co_ctx;
ctx->cur_co_ctx->co = co;
ctx->cur_co_ctx->co_ref = co_ref;
#ifdef NGX_LUA_USE_ASSERT
ctx->cur_co_ctx->co_top = 1;
#endif
ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
/* }}} */
/* {{{ register request cleanup hooks */
if (ctx->cleanup == NULL) {
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cln->handler = ngx_http_lua_request_cleanup_handler;
cln->data = ctx;
ctx->cleanup = &cln->handler;
}
/* }}} */
ctx->context = NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE;
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
if (llcf->check_client_abort) {
r->read_event_handler = ngx_http_lua_rd_check_broken_connection;
#if (NGX_HTTP_V2)
if (!r->stream) {
#endif
rev = r->connection->read;
if (!rev->active) {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
return NGX_ERROR;
}
}
#if (NGX_HTTP_V2)
}
#endif
} else {
r->read_event_handler = ngx_http_block_reading;
}
c = r->connection;
nreqs = c->requests;
rc = ngx_http_lua_run_thread(L, r, ctx, 0);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
if (rc == NGX_AGAIN) {
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);
} else if (rc == NGX_DONE) {
ngx_http_lua_finalize_request(r, NGX_DONE);
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);
}
if (rc == NGX_OK || rc == NGX_DECLINED) {
if (r->header_sent) {
dd("header already sent");
/* response header was already generated in rewrite_by_lua*,
* so it is no longer safe to proceed to later phases
* which may generate responses again */
if (!ctx->eof) {
dd("eof not yet sent");
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
/* indicate last_buf */);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
}
return NGX_HTTP_OK;
}
r->write_event_handler = ngx_http_core_run_phases;
ctx->entered_server_rewrite_phase = 0;
return NGX_DECLINED;
}
return rc;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_
#define _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_server_rewrite_handler(ngx_http_request_t *r);
ngx_int_t ngx_http_lua_server_rewrite_handler_inline(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L);
ngx_int_t ngx_http_lua_server_rewrite_handler_file(ngx_http_request_t *r,
ngx_http_lua_srv_conf_t *lscf, lua_State *L);
#endif /* _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,207 @@
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_setby.h"
#include "ngx_http_lua_exception.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_pcrefix.h"
#include "ngx_http_lua_log.h"
#include "ngx_http_lua_string.h"
#include "ngx_http_lua_misc.h"
#include "ngx_http_lua_consts.h"
#include "ngx_http_lua_shdict.h"
#include "ngx_http_lua_util.h"
static void ngx_http_lua_set_by_lua_env(lua_State *L, ngx_http_request_t *r,
size_t nargs, ngx_http_variable_value_t *args);
ngx_int_t
ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val,
ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *script)
{
size_t i;
ngx_int_t rc;
u_char *err_msg;
size_t len;
u_char *data;
#if (NGX_PCRE)
ngx_pool_t *old_pool;
#endif
dd("nargs: %d", (int) nargs);
dd("set Lua VM panic handler");
lua_atpanic(L, ngx_http_lua_atpanic);
NGX_LUA_EXCEPTION_TRY {
dd("initialize nginx context in Lua VM, code chunk at "
"stack top sp = 1");
ngx_http_lua_set_by_lua_env(L, r, nargs, args);
/* passing directive arguments to the user code */
for (i = 0; i < nargs; i++) {
lua_pushlstring(L, (const char *) args[i].data, args[i].len);
}
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif
lua_pushcfunction(L, ngx_http_lua_traceback);
lua_insert(L, 1); /* put it under chunk and args */
dd("protected call user code");
rc = lua_pcall(L, nargs, 1, 1);
dd("after protected call user code");
lua_remove(L, 1); /* remove traceback function */
#if (NGX_PCRE)
/* XXX: work-around to nginx regex subsystem */
ngx_http_lua_pcre_malloc_done(old_pool);
#endif
if (rc != 0) {
/* error occurred when running loaded code */
err_msg = (u_char *) lua_tolstring(L, -1, &len);
if (err_msg == NULL) {
err_msg = (u_char *) "unknown reason";
len = sizeof("unknown reason") - 1;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"failed to run set_by_lua*: %*s", len, err_msg);
lua_settop(L, 0); /* clear remaining elems on stack */
return NGX_ERROR;
}
data = (u_char *) lua_tolstring(L, -1, &len);
if (data) {
val->data = ngx_palloc(r->pool, len);
if (val->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(val->data, data, len);
val->len = len;
} else {
val->data = NULL;
val->len = 0;
}
} NGX_LUA_EXCEPTION_CATCH {
dd("nginx execution restored");
return NGX_ERROR;
}
/* clear Lua stack */
lua_settop(L, 0);
return NGX_OK;
}
void
ngx_http_lua_ffi_get_setby_param(ngx_http_request_t *r, int idx,
u_char **data_p, size_t *len_p)
{
int n;
ngx_http_variable_value_t *v;
ngx_http_lua_main_conf_t *lmcf;
idx--;
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
/* get number of args from lmcf */
n = lmcf->setby_nargs;
/* get args from lmcf */
v = lmcf->setby_args;
if (idx < 0 || idx > n - 1) {
*len_p = 0;
} else {
*data_p = v[idx].data;
*len_p = v[idx].len;
}
}
/**
* Set environment table for the given code closure.
*
* Before:
* | code closure | <- top
* | ... |
*
* After:
* | code closure | <- top
* | ... |
* */
static void
ngx_http_lua_set_by_lua_env(lua_State *L, ngx_http_request_t *r, size_t nargs,
ngx_http_variable_value_t *args)
{
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_set_req(L, r);
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
lmcf->setby_nargs = nargs;
lmcf->setby_args = args;
#ifndef OPENRESTY_LUAJIT
/**
* we want to create empty environment for current script
*
* newt = {}
* newt["_G"] = newt
* setmetatable(newt, {__index = _G})
*
* if a function or symbol is not defined in our env, __index will lookup
* in the global env.
*
* all variables created in the script-env will be thrown away at the end
* of the script run.
* */
ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
/* {{{ make new env inheriting main thread's globals table */
/* the metatable for the new env */
lua_createtable(L, 0 /* narr */, 1 /* nrec */);
ngx_http_lua_get_globals_table(L);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2); /* setmetatable(newt, {__index = _G}) */
/* }}} */
lua_setfenv(L, -2); /* set new running env for the code closure */
#endif
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

View File

@ -0,0 +1,14 @@
#ifndef _NGX_HTTP_LUA_SET_BY_H_INCLUDED_
#define _NGX_HTTP_LUA_SET_BY_H_INCLUDED_
#include "ngx_http_lua_common.h"
ngx_int_t ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r,
ngx_str_t *val, ngx_http_variable_value_t *args, size_t nargs,
ngx_str_t *script);
#endif /* _NGX_HTTP_LUA_SET_BY_H_INCLUDED_ */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More