diff --git a/.travis.yml b/.travis.yml index 304b2955..7ed3315a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ install: - git clone https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module - git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core + - git clone -b stream-module-ports https://github.com/thibaultcha/lua-resty-core.git ../lua-resty-core script: - sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT diff --git a/README.md b/README.md index 15f2cdac..7e160cab 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,18 @@ Name ngx_stream_lua_module - Embed the power of Lua into Nginx stream/TCP Servers. -This module is a core component of OpenResty. If you are using this module, then you are essentially using OpenResty :) +This module is a core component of OpenResty. If you are using this module, +then you are essentially using OpenResty. -*This module is not distributed with the Nginx source.* See [the installation instructions](#installation). +*This module is not distributed with the Nginx source.* See [the installation +instructions](#installation). Table of Contents ================= * [Name](#name) * [Status](#status) +* [Version](#version) * [Synopsis](#synopsis) * [Description](#description) * [Directives](#directives) @@ -34,6 +37,13 @@ Status Production ready. +Version +======= + +This document describes ngx_stream_lua +[v0.0.8](https://github.com/openresty/stream-lua-nginx-module/tags), which is not +released yet. + Synopsis ======== @@ -99,23 +109,26 @@ stream { } ``` +[Back to TOC](#table-of-contents) + Description =========== -This module is a core component of OpenResty. If you are using this module, then you are essentially using OpenResty :) +This is a port of the +[ngx_http_lua_module](https://github.com/openresty/lua-nginx-module#readme) to +the Nginx "stream" subsystem so as to support generic stream/TCP clients. -This is a port of the [ngx_http_lua_module](https://github.com/openresty/lua-nginx-module#readme) to the NGINX "stream" subsystem so -as to support generic stream/TCP clients in the downstream. - -Lua APIs and directive names remain the same as the `ngx_http_lua_module`. +The available Lua APIs and Nginx directives remain the same as those of the +ngx_http_lua module. [Back to TOC](#table-of-contents) Directives ---------- -The following directives are ported directly from `ngx_http_lua_module`. Please check the -documentation of `ngx_http_lua_module` for more details about their usage and behavior. +The following directives are ported directly from ngx_http_lua. Please check +the documentation of ngx_http_lua for more details about their usage and +behavior. * [lua_load_resty_core](https://github.com/openresty/lua-nginx-module#lua_load_resty_core) * [lua_code_cache](https://github.com/openresty/lua-nginx-module#lua_code_cache) @@ -155,11 +168,14 @@ documentation of `ngx_http_lua_module` for more details about their usage and be * [lua_capture_error_log](https://github.com/openresty/lua-nginx-module#lua_capture_error_log) * [preread_by_lua_no_postpone](#preread_by_lua_no_postpone) -The [send_timeout](http://nginx.org/r/send_timeout) directive in the Nginx "http" subsystem is missing in the "stream" subsystem. -So `ngx_stream_lua_module` uses the `lua_socket_send_timeout` for this purpose. +The [send_timeout](https://nginx.org/r/send_timeout) directive in the Nginx +"http" subsystem is missing in the "stream" subsystem. As such, +ngx_stream_lua_module uses the `lua_socket_send_timeout` directive for this +purpose instead. -**Note:** the lingering close directive that used to exist in older version of stream_lua_nginx_module has been removed and can -now be simulated with the newly added [tcpsock:shutdown](#tcpsockshutdown) method if necessary. +**Note:** the lingering close directive that used to exist in older version of +`stream_lua_nginx_module` has been removed and can now be simulated with the +newly added [tcpsock:shutdown](#tcpsockshutdown) API if necessary. [Back to TOC](#table-of-contents) @@ -256,7 +272,7 @@ lua_add_variable **context:** *stream* -Add the variable `$var` to the stream subsystem and makes it changeable. If `$var` already exists, +Add the variable `$var` to the "stream" subsystem and makes it changeable. If `$var` already exists, this directive will do nothing. By default, variables added using this directive are considered "not found" and reading them @@ -284,12 +300,12 @@ This directive was first introduced in the `v0.0.4` release. Nginx API for Lua ----------------- -Many Lua API functions are ported from the `ngx_http_lua_module`. Check out the official manual of -`ngx_http_lua_module` for more details on these Lua API functions. +Many Lua API functions are ported from ngx_http_lua. Check out the official +manual of ngx_http_lua for more details on these Lua API functions. * [ngx.var.VARIABLE](https://github.com/openresty/lua-nginx-module#ngxvarvariable) -This module fully supports the new variable subsystem inside the NGINX stream core. You may access any +This module fully supports the new variable subsystem inside the Nginx stream core. You may access any [built-in variables](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#variables) provided by the stream core or other stream modules. * [Core constants](https://github.com/openresty/lua-nginx-module#core-constants) @@ -304,7 +320,7 @@ other stream modules. * [ngx.balancer](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md) Only raw request sockets are supported, for obvious reasons. The `raw` argument value -is ignored and the raw request socket is always returned. Unlike `ngx_http_lua_module`, +is ignored and the raw request socket is always returned. Unlike ngx_http_lua, you can still call output API functions like `ngx.say`, `ngx.print`, and `ngx.flush` after acquiring the raw request socket via this function. @@ -316,6 +332,8 @@ packets back to the client using the downstream socket. The raw TCP sockets returned by this module will contain the following extra method: +[Back to TOC](#directives) + tcpsock:shutdown ---------------- @@ -494,9 +512,7 @@ TODO ==== * Add new directives `access_by_lua_block` and `access_by_lua_file`. -* Add `ngx.semaphore` API. -* Add support for [lua-resty-core](https://github.com/openresty/lua-resty-core). -* Add `lua_postpone_output` to emulate the [postpone_output](http://nginx.org/r/postpone_output) directive. +* Add `lua_postpone_output` to emulate the [postpone_output](https://nginx.org/r/postpone_output) directive. [Back to TOC](#table-of-contents) @@ -509,23 +525,38 @@ The latest version of this module is compatible with the following versions of N * 1.15.x (last tested: 1.15.8) * 1.13.x (last tested: 1.13.6) -Nginx cores older than 1.13.6 (exclusive) are *not* tested and may or may not work. Use at your own risk! +Nginx cores older than 1.13.6 (exclusive) are *not* tested and may or may not +work. Use at your own risk! [Back to TOC](#table-of-contents) Installation ============ -This module can be manually compiled into Nginx or OpenResty: +It is *highly* recommended to use [OpenResty releases](https://openresty.org) +which bundle Nginx, ngx_http_lua, ngx_stream_lua, (this module), LuaJIT, as +well as other powerful companion Nginx modules and Lua libraries. -1. Install LuaJIT 2.1 or Lua 5.1 (Lua 5.2+ are *not* supported yet). LuaJIT can be downloaded from the [the LuaJIT project website](http://luajit.org/download.html) and Lua 5.1, from the [Lua project website](http://www.lua.org/). Some distribution package managers also distribute LuaJIT and/or Lua. -1. Download the latest version of ngx_stream_lua [HERE](https://github.com/openresty/stream-lua-nginx-module/tags). -1. Download the latest supported version of NGINX [HERE](http://nginx.org/) (See [Nginx Compatibility](#nginx-compatibility)) or the OpenResty bundle from [HERE](https://openresty.org/). +It is discouraged to build this module with Nginx yourself since it is tricky +to set up exactly right. -Build the source of NGINX or OpenResty with this module, like below: +Note that Nginx, LuaJIT, and OpenSSL official releases have various limitations +and long standing bugs that can cause some of this module's features to be +disabled, not work properly, or run slower. Official OpenResty releases are +recommended because they bundle [OpenResty's optimized LuaJIT 2.1 fork](https://github.com/openresty/luajit2) and +[Nginx/OpenSSL +patches](https://github.com/openresty/openresty/tree/master/patches). + +Alternatively, ngx_stream_lua can be manually compiled into Nginx: + +1. LuaJIT can be downloaded from the [latest release of OpenResty's LuaJIT fork](https://github.com/openresty/luajit2/releases). The official LuaJIT 2.x releases are also supported, although performance will be significantly lower for reasons elaborated above +1. Download the latest version of ngx_stream_lua [HERE](https://github.com/openresty/stream-lua-nginx-module/tags) +1. Download the latest supported version of Nginx [HERE](https://nginx.org/) (See [Nginx Compatibility](#nginx-compatibility)) + +Build the source with this module: ```bash -wget 'http://nginx.org/download/nginx-1.13.6.tar.gz' +wget 'https://nginx.org/download/nginx-1.13.6.tar.gz' tar -xzvf nginx-1.13.6.tar.gz cd nginx-1.13.6/ @@ -533,10 +564,6 @@ cd nginx-1.13.6/ export LUAJIT_LIB=/path/to/luajit/lib export LUAJIT_INC=/path/to/luajit/include/luajit-2.1 -# or tell where to find Lua if using Lua instead: -#export LUA_LIB=/path/to/lua/lib -#export LUA_INC=/path/to/lua/include - # Here we assume Nginx is to be installed under /opt/nginx/. ./configure --prefix=/opt/nginx \ --with-ld-opt="-Wl,-rpath,/path/to/luajit-or-lua/lib" \ @@ -549,8 +576,9 @@ make -j4 make install ``` -You may use `--without-http` if you do not wish to use this module with the HTTP subsystem. -ngx_stream_lua will work perfectly fine without the presense of the HTTP subsystem. +You may use `--without-http` if you do not wish to use this module with the +"http" subsystem. ngx_stream_lua will work perfectly fine without the "http" +subsystem. [Back to TOC](#table-of-contents) @@ -576,7 +604,8 @@ The [openresty](https://groups.google.com/group/openresty) mailing list is for C Code Repository =============== -The code repository of this project is hosted on github at [openresty/stream-lua-nginx-module](https://github.com/openresty/stream-lua-nginx-module). +The code repository of this project is hosted on GitHub at +[openresty/stream-lua-nginx-module](https://github.com/openresty/stream-lua-nginx-module). [Back to TOC](#table-of-contents) @@ -601,6 +630,8 @@ to make code sharing between this module and [lua-nginx-module](https://github.c * `balancer_by_lua_*`, `preread_by_lua_*`, `log_by_lua_*` and `ssl_certby_lua*` phases support. * [`reqsock:peek`](#reqsockpeek) API support. +[Back to TOC](#table-of-contents) + Copyright and License ===================== diff --git a/config b/config index 25c8e954..f4fc7e40 100644 --- a/config +++ b/config @@ -1,15 +1,11 @@ -ngx_feature="Lua library" -ngx_feature_libs="-llua -lm" -ngx_feature_name= -ngx_feature_run=no -ngx_feature_incs="#include " -ngx_feature_path= -ngx_feature_test="#if LUA_VERSION_NUM != 501 -# error unsupported Lua language version -#endif -(void) luaL_newstate();" 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 @@ -20,13 +16,13 @@ if [ -n "$LUAJIT_INC" -o -n "$LUAJIT_LIB" ]; then ngx_lua_opt_I="-I$LUAJIT_INC" ngx_lua_opt_L="-L$LUAJIT_LIB" - # ensure that our -I$LUAJIT_INC and -L$LUAJIT_LIB is at the first. + # 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. + # LuaJIT's win32 build uses the library file name lua51.dll ngx_feature_libs="$ngx_lua_opt_L -llua51" . auto/feature @@ -35,23 +31,23 @@ if [ -n "$LUAJIT_INC" -o -n "$LUAJIT_LIB" ]; then 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 our -I$LUAJIT_INC and -L$LUAJIT_LIB is at the first. + # 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 -lm -ldl" + 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 -lm -ldl" + ngx_feature_libs="$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt" fi . auto/feature @@ -66,17 +62,18 @@ if [ -n "$LUAJIT_INC" -o -n "$LUAJIT_LIB" ]; then 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 our -I$LUAJIT_INC and -L$LUAJIT_LIB is at the first. + # 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 -lm" + 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 -lm" + ngx_feature_libs="$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt" fi . auto/feature @@ -89,7 +86,7 @@ if [ -n "$LUAJIT_INC" -o -n "$LUAJIT_LIB" ]; then if [ $ngx_found = no ]; then cat << END - $0: error: ngx_stream_lua_module requires the Lua or LuaJIT library and LUAJIT_LIB is defined as $LUAJIT_LIB and LUAJIT_INC (path for lua.h) $LUAJIT_INC, but we cannot find LuaJIT there. + $0: error: ngx_stream_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 @@ -98,7 +95,8 @@ END Darwin:*) case "$NGX_MACHINE" in amd64 | x86_64 | i386) - echo "adding extra linking options needed by LuaJIT" + 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" ;; @@ -111,196 +109,107 @@ END ;; esac else - if [ -n "$LUA_INC" -o -n "$LUA_LIB" ]; then - # explicitly set Lua paths - ngx_feature="Lua library in $LUA_LIB and $LUA_INC (specified by the LUA_LIB and LUA_INC env)" - ngx_feature_path="$LUA_INC" - ngx_lua_opt_I="-I$LUA_INC" - ngx_lua_opt_L="-L$LUA_LIB" - - # ensure that our -I$LUA_INC and -L$LUA_LIB is at the 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" - + # 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$LUA_LIB $ngx_lua_opt_L -llua -lm -ldl" + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lluajit-5.1 -lm" else - ngx_feature_libs="$ngx_lua_opt_L -llua -lm -ldl" + ngx_feature_libs="-L/usr/local/lib -lluajit-5.1 -lm" fi - . auto/feature + fi - # 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_path="$LUA_INC" - ngx_lua_opt_I="-I$LUA_INC" - ngx_lua_opt_L="-L$LUA_LIB" - - # ensure that our -I$LUA_INC and -L$LUA_LIB is at the 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$LUA_LIB $ngx_lua_opt_L -llua -lm" - else - ngx_feature_libs="$ngx_lua_opt_L -llua -lm" - fi - - . auto/feature - - # clean up - CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" - NGX_TEST_LD_OPT="$SAVED_NGX_TEST_LD_OPT" - fi - - if [ $ngx_found = no ]; then - cat << END - $0: error: ngx_stream_lua_module requires the Lua or LuaJIT library and LUA_LIB is defined as $LUA_LIB and LUA_INC (path for lua.h) is $LUA_INC, but we cannot find standard Lua there. -END - exit 1 + 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 - else - # auto-discovery - ngx_feature="Lua library" - ngx_feature_libs="-llua -lm" . auto/feature + fi - if [ $ngx_found = no ]; then - # OpenBSD-5.2 - ngx_feature="Lua library in /usr/local/" - ngx_feature_path="/usr/local/include/lua-5.1" - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -llua -lm" - else - ngx_feature_libs="-L/usr/local/lib -llua5.1 -lm" - fi - . auto/feature - fi - - if [ $ngx_found = no ]; then - # OpenBSD < 5.2 - ngx_feature="Lua library in /usr/local/" - ngx_feature_path="/usr/local/include" - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -llua -lm" - else - ngx_feature_libs="-L/usr/local/lib -llua -lm" - fi - . auto/feature - fi - - if [ $ngx_found = no ]; then - # NetBSD - ngx_feature="Lua library in /usr/pkg/" - ngx_feature_path="/usr/pkg/include/" - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lm -llua" - else - ngx_feature_libs="-L/usr/pkg/lib -lm -llua" - fi - . auto/feature - fi - - if [ $ngx_found = no ]; then - # MacPorts - ngx_feature="Lua library in /opt/local/" - ngx_feature_path="/opt/local/include" - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lm -llua" - else - ngx_feature_libs="-L/opt/local/lib -lm -llua" - fi - . auto/feature - fi - - if [ $ngx_found = no ]; then - # FreeBSD - ngx_feature="Lua library in /usr/local/*/lua51/" - ngx_feature_path="/usr/local/include/lua51" - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/local/lib/lua51 -L/usr/local/lib/lua51 -llua -lm" - else - ngx_feature_libs="-L/usr/local/lib/lua51 -llua -lm" - fi - . auto/feature - fi - - if [ $ngx_found = no ]; then - # Debian - ngx_feature="Lua library in /usr/" - ngx_feature_path="/usr/include/lua5.1" - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/lib -L/usr/lib -lm -llua5.1" - else - ngx_feature_libs="-L/usr/lib -lm -llua5.1" - fi - . auto/feature - fi - - 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" - 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" - 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" - 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 + 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 if [ $ngx_found = yes ]; then # this is a hack to persuade nginx's build system to favor - # the paths set by our user environments: + # the paths set by our user environment CFLAGS="$ngx_lua_opt_I $CFLAGS" NGX_LD_OPT="$ngx_lua_opt_L $NGX_LD_OPT" CORE_INCS="$CORE_INCS $ngx_feature_path" CORE_LIBS="$CORE_LIBS $ngx_feature_libs" else - cat << END - $0: error: ngx_stream_lua_module requires the Lua library. + cat << END + $0: error: ngx_stream_lua_module requires the LuaJIT library. END - exit 1 + exit 1 fi +# ---------------------------------------- + +ngx_feature="LuaJIT 2.x" +ngx_feature_run=no +ngx_feature_incs="#include " +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_stream_lua_module requires LuaJIT 2.x. +END + exit 1 +fi + +# ---------------------------------------- + +ngx_feature="LuaJIT has FFI" +ngx_feature_libs="$LUAJIT_LIB/libluajit-5.1.a $luajit_ld_opt" +ngx_feature_run=yes +ngx_feature_incs="#include + #include + #include " +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_stream_lua_module requires LuaJIT with FFI enabled. +END + exit 1 +fi + +# ---------------------------------------- + ngx_addon_name=ngx_stream_lua_module -STREAM_MODULES="$STREAM_MODULES ngx_stream_lua_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/src/ngx_stream_lua_api.c \ $ngx_addon_dir/src/ngx_stream_lua_request.c \ @@ -344,6 +253,7 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/src/ngx_stream_lua_semaphore.c \ $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.c \ $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.c \ + $ngx_addon_dir/src/ngx_stream_lua_input_filters.c \ " NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ @@ -362,20 +272,16 @@ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ $ngx_addon_dir/src/ngx_stream_lua_output.h \ $ngx_addon_dir/src/ngx_stream_lua_consts.h \ $ngx_addon_dir/src/ngx_stream_lua_log.h \ - $ngx_addon_dir/src/ngx_stream_lua_time.h \ $ngx_addon_dir/src/ngx_stream_lua_string.h \ $ngx_addon_dir/src/ngx_stream_lua_control.h \ $ngx_addon_dir/src/ngx_stream_lua_sleep.h \ $ngx_addon_dir/src/ngx_stream_lua_phase.h \ $ngx_addon_dir/src/ngx_stream_lua_ctx.h \ - $ngx_addon_dir/src/ngx_stream_lua_regex.h \ $ngx_addon_dir/src/ngx_stream_lua_script.h \ $ngx_addon_dir/src/ngx_stream_lua_shdict.h \ - $ngx_addon_dir/src/ngx_stream_lua_variable.h \ $ngx_addon_dir/src/ngx_stream_lua_timer.h \ $ngx_addon_dir/src/ngx_stream_lua_config.h \ $ngx_addon_dir/src/api/ngx_stream_lua_api.h \ - $ngx_addon_dir/src/ngx_stream_lua_worker.h \ $ngx_addon_dir/src/ngx_stream_lua_misc.h \ $ngx_addon_dir/src/ngx_stream_lua_initby.h \ $ngx_addon_dir/src/ngx_stream_lua_initworkerby.h \ @@ -389,8 +295,13 @@ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ $ngx_addon_dir/src/ngx_stream_lua_semaphore.h \ $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.h \ $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.h \ + $ngx_addon_dir/src/ngx_stream_lua_input_filters.h \ " +STREAM_MODULES="$STREAM_MODULES $ngx_addon_name" + +# ---------------------------------------- + ngx_feature="export symbols by default (-E)" ngx_feature_libs="-Wl,-E" ngx_feature_name= @@ -405,6 +316,8 @@ 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" @@ -420,13 +333,7 @@ if [ $ngx_found = yes ]; then CORE_LIBS="-Wl,--export-all-symbols $CORE_LIBS" fi -#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" - -USE_MD5=YES -USE_SHA1=YES - -CORE_INCS="$CORE_INCS $ngx_addon_dir/src/api" +# ---------------------------------------- ngx_feature="SO_PASSCRED" ngx_feature_libs= @@ -439,6 +346,8 @@ ngx_feature_test='setsockopt(1, SOL_SOCKET, SO_PASSCRED, NULL, 0);' . auto/feature +# ---------------------------------------- + ngx_feature="SA_RESTART" ngx_feature_libs= ngx_feature_name="NGX_STREAM_LUA_HAVE_SA_RESTART" @@ -450,6 +359,8 @@ ngx_feature_test='struct sigaction act; . auto/feature +# ---------------------------------------- + if [ $USE_PCRE = YES -o $PCRE != NONE ] && [ $PCRE != NO -a $PCRE != YES ]; then # force pcre_version symbol to be required when PCRE is statically linked case "$NGX_PLATFORM" in @@ -487,5 +398,15 @@ if [ $USE_PCRE = YES -o $PCRE != NONE ] && [ $PCRE != NO -a $PCRE != YES ]; then esac 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 -DLUA_DEFAULT_PATH='\"/usr/local/openresty/lualib/?.lua\"'" #CFLAGS=$"$CFLAGS -DLUA_DEFAULT_CPATH='\"/usr/local/openresty/lualib/?.so\"'" diff --git a/src/api/ngx_stream_lua_api.h b/src/api/ngx_stream_lua_api.h index 7437728d..0e5a18f4 100644 --- a/src/api/ngx_stream_lua_api.h +++ b/src/api/ngx_stream_lua_api.h @@ -44,6 +44,13 @@ typedef struct { } ngx_stream_lua_value_t; +typedef struct { + int len; + /* this padding hole on 64-bit systems is expected */ + u_char *data; +} ngx_stream_lua_ffi_str_t; + + lua_State *ngx_stream_lua_get_global_state(ngx_conf_t *cf); ngx_stream_lua_request_t *ngx_stream_lua_get_request(lua_State *L); @@ -54,7 +61,8 @@ ngx_int_t ngx_stream_lua_add_package_preload(ngx_conf_t *cf, ngx_int_t ngx_stream_lua_shared_dict_get(ngx_shm_zone_t *shm_zone, u_char *key_data, size_t key_len, ngx_stream_lua_value_t *value); -ngx_shm_zone_t *ngx_stream_lua_find_zone(u_char *name_data, size_t name_len); +ngx_shm_zone_t *ngx_stream_lua_find_zone(u_char *name_data, + size_t name_len); ngx_shm_zone_t *ngx_stream_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag); diff --git a/src/ngx_stream_lua_api.c b/src/ngx_stream_lua_api.c index 1f6914dd..622367ff 100644 --- a/src/ngx_stream_lua_api.c +++ b/src/ngx_stream_lua_api.c @@ -93,8 +93,8 @@ ngx_stream_lua_add_package_preload(ngx_conf_t *cf, const char *package, ngx_shm_zone_t * -ngx_stream_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, - void *tag) +ngx_stream_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, + size_t size, void *tag) { ngx_stream_lua_main_conf_t *lmcf; ngx_stream_lua_shm_zone_ctx_t *ctx; diff --git a/src/ngx_stream_lua_args.c b/src/ngx_stream_lua_args.c index 0950e0cd..0cdaef1d 100644 --- a/src/ngx_stream_lua_args.c +++ b/src/ngx_stream_lua_args.c @@ -104,10 +104,12 @@ ngx_stream_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max) } if (max > 0 && ++count == max) { - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, - "lua hit query args limit %d", max); + lua_pushliteral(L, "truncated"); - return 1; + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua hit query args limit %d", + max); + return 2; } } else { diff --git a/src/ngx_stream_lua_balancer.c b/src/ngx_stream_lua_balancer.c index d18b3920..aafe0eea 100644 --- a/src/ngx_stream_lua_balancer.c +++ b/src/ngx_stream_lua_balancer.c @@ -505,7 +505,6 @@ ngx_stream_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data) #endif - int ngx_stream_lua_ffi_balancer_set_current_peer(ngx_stream_lua_request_t *r, const u_char *addr, size_t addr_len, int port, char **err) @@ -648,7 +647,7 @@ ngx_stream_lua_ffi_balancer_set_more_tries(ngx_stream_lua_request_t *r, int count, char **err) { #if (HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH) - ngx_uint_t max_tries, total; + ngx_uint_t max_tries, total; #endif ngx_stream_lua_ctx_t *ctx; ngx_stream_upstream_t *u; @@ -752,3 +751,5 @@ ngx_stream_lua_ffi_balancer_get_last_failure(ngx_stream_lua_request_t *r, return bp->last_peer_state; } + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_cache.c b/src/ngx_stream_lua_cache.c index 88883a38..3f83d433 100644 --- a/src/ngx_stream_lua_cache.c +++ b/src/ngx_stream_lua_cache.c @@ -164,7 +164,8 @@ ngx_stream_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L, n = lua_gettop(L); - dd("XXX cache key: [%s]", cache_key); + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "looking up Lua code cache with key '%s'", cache_key); rc = ngx_stream_lua_cache_load_code(log, L, (char *) cache_key); if (rc == NGX_OK) { @@ -248,7 +249,8 @@ ngx_stream_lua_cache_loadfile(ngx_log_t *log, lua_State *L, dd("CACHE file key already pre-calculated"); } - dd("XXX cache key for file: [%s]", cache_key); + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, + "looking up Lua code cache with key '%s'", cache_key); rc = ngx_stream_lua_cache_load_code(log, L, (char *) cache_key); if (rc == NGX_OK) { diff --git a/src/ngx_stream_lua_common.h b/src/ngx_stream_lua_common.h index 18390cf0..05e625ae 100644 --- a/src/ngx_stream_lua_common.h +++ b/src/ngx_stream_lua_common.h @@ -17,11 +17,6 @@ #define _NGX_STREAM_LUA_COMMON_H_INCLUDED_ -#ifndef NGX_LUA_NO_FFI_API -#define NGX_LUA_NO_FFI_API -#endif - - #include #include #include @@ -51,7 +46,7 @@ #endif -#if !defined(nginx_version) || (nginx_version < 1013006) +#if !defined(nginx_version) || nginx_version < 1013006 #error at least nginx 1.13.6 is required but found an older version #endif @@ -63,13 +58,20 @@ #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_STREAM_LUA_USE_OCSP 1 #endif + + #ifndef NGX_HAVE_SHA1 -# if (nginx_version >= 1011002) +# if defined(nginx_version) && nginx_version >= 1011002 # define NGX_HAVE_SHA1 1 # endif #endif @@ -129,10 +131,8 @@ #define NGX_STREAM_LUA_CONTEXT_SSL_CERT 0x0040 -#ifndef NGX_LUA_NO_FFI_API #define NGX_STREAM_LUA_FFI_NO_REQ_CTX -100 #define NGX_STREAM_LUA_FFI_BAD_CONTEXT -101 -#endif #if (NGX_PTR_SIZE >= 8 && !defined(_WIN64)) @@ -178,8 +178,6 @@ struct ngx_stream_lua_main_conf_s { ngx_cycle_t *cycle; ngx_pool_t *pool; - ngx_flag_t load_resty_core; - ngx_int_t max_pending_timers; ngx_int_t pending_timers; @@ -389,6 +387,13 @@ struct ngx_stream_lua_co_ctx_s { 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 */ }; diff --git a/src/ngx_stream_lua_control.c b/src/ngx_stream_lua_control.c index 5574aa19..a8b63a01 100644 --- a/src/ngx_stream_lua_control.c +++ b/src/ngx_stream_lua_control.c @@ -26,7 +26,6 @@ -static int ngx_stream_lua_ngx_exit(lua_State *L); static int ngx_stream_lua_on_abort(lua_State *L); @@ -34,11 +33,6 @@ void ngx_stream_lua_inject_control_api(ngx_log_t *log, lua_State *L) { - /* ngx.exit */ - - lua_pushcfunction(L, ngx_stream_lua_ngx_exit); - lua_setfield(L, -2, "exit"); - /* ngx.on_abort */ lua_pushcfunction(L, ngx_stream_lua_on_abort); @@ -48,75 +42,6 @@ ngx_stream_lua_inject_control_api(ngx_log_t *log, lua_State *L) -static int -ngx_stream_lua_ngx_exit(lua_State *L) -{ - ngx_int_t rc; - ngx_stream_lua_request_t *r; - ngx_stream_lua_ctx_t *ctx; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one argument"); - } - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); - if (ctx == NULL) { - return luaL_error(L, "no request ctx found"); - } - - ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT - | NGX_STREAM_LUA_CONTEXT_TIMER - | NGX_STREAM_LUA_CONTEXT_BALANCER - | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_PREREAD - ); - - rc = (ngx_int_t) luaL_checkinteger(L, 1); - - if (ctx->context & NGX_STREAM_LUA_CONTEXT_SSL_CERT) - { - -#if (NGX_STREAM_SSL) - - ctx->exit_code = rc; - ctx->exited = 1; - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua exit with code %i", rc); - - - return lua_yield(L, 0); - -#else - - return luaL_error(L, "no SSL support"); - -#endif - } - - - dd("setting exit code: %d", (int) rc); - - ctx->exit_code = rc; - ctx->exited = 1; - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua exit with code %i", ctx->exit_code); - - if (ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) { - return 0; - } - - dd("calling yield"); - return lua_yield(L, 0); -} - - static int ngx_stream_lua_on_abort(lua_State *L) { @@ -175,7 +100,6 @@ ngx_stream_lua_on_abort(lua_State *L) } -#ifndef NGX_LUA_NO_FFI_API int ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, size_t *errlen) @@ -188,24 +112,19 @@ ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, return NGX_ERROR; } - if (ngx_stream_lua_ffi_check_context(ctx, NGX_STREAM_LUA_CONTEXT_REWRITE - | NGX_STREAM_LUA_CONTEXT_ACCESS - | NGX_STREAM_LUA_CONTEXT_CONTENT + + if (ngx_stream_lua_ffi_check_context(ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER - | NGX_STREAM_LUA_CONTEXT_HEADER_FILTER | NGX_STREAM_LUA_CONTEXT_BALANCER | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_SSL_SESS_STORE - | NGX_STREAM_LUA_CONTEXT_SSL_SESS_FETCH, + | NGX_STREAM_LUA_CONTEXT_PREREAD, err, errlen) != NGX_OK) { return NGX_ERROR; } - if (ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_SSL_SESS_STORE - | NGX_STREAM_LUA_CONTEXT_SSL_SESS_FETCH)) + if (ctx->context & NGX_STREAM_LUA_CONTEXT_SSL_CERT) { #if (NGX_STREAM_SSL) @@ -216,9 +135,6 @@ ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, "lua exit with code %d", status); - if (ctx->context == NGX_STREAM_LUA_CONTEXT_SSL_SESS_STORE) { - return NGX_DONE; - } return NGX_OK; @@ -236,14 +152,12 @@ ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, "lua exit with code %i", ctx->exit_code); - if (ctx->context & (NGX_STREAM_LUA_CONTEXT_HEADER_FILTER - | NGX_STREAM_LUA_CONTEXT_BALANCER)) - { + if (ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) { return NGX_DONE; } return NGX_OK; } -#endif /* NGX_LUA_NO_FFI_API */ + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_coroutine.c b/src/ngx_stream_lua_coroutine.c index c9cca72a..5cf84c06 100644 --- a/src/ngx_stream_lua_coroutine.c +++ b/src/ngx_stream_lua_coroutine.c @@ -33,6 +33,7 @@ static int ngx_stream_lua_coroutine_create(lua_State *L); +static int ngx_stream_lua_coroutine_wrap(lua_State *L); static int ngx_stream_lua_coroutine_resume(lua_State *L); static int ngx_stream_lua_coroutine_yield(lua_State *L); static int ngx_stream_lua_coroutine_status(lua_State *L); @@ -70,6 +71,45 @@ ngx_stream_lua_coroutine_create(lua_State *L) } +static int +ngx_stream_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_stream_lua_coroutine_resume(L); +} + + +static int +ngx_stream_lua_coroutine_wrap(lua_State *L) +{ + ngx_stream_lua_request_t *r; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx = NULL; + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + + ngx_stream_lua_coroutine_create_helper(L, r, ctx, &coctx); + + coctx->is_wrap = 1; + + lua_pushcclosure(L, ngx_stream_lua_coroutine_wrap_runner, 1); + + return 1; +} + + int ngx_stream_lua_coroutine_create_helper(lua_State *L, ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, @@ -259,7 +299,7 @@ ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) int rc; /* new coroutine table */ - lua_createtable(L, 0 /* narr */, 14 /* nrec */); + lua_createtable(L, 0 /* narr */, 16 /* nrec */); /* get old coroutine table */ lua_getglobal(L, "coroutine"); @@ -271,6 +311,9 @@ ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) 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"); @@ -286,6 +329,9 @@ ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_stream_lua_coroutine_create); lua_setfield(L, -2, "__create"); + lua_pushcfunction(L, ngx_stream_lua_coroutine_wrap); + lua_setfield(L, -2, "__wrap"); + lua_pushcfunction(L, ngx_stream_lua_coroutine_resume); lua_setfield(L, -2, "__resume"); @@ -300,7 +346,7 @@ ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) /* inject coroutine APIs */ { const char buf[] = - "local keys = {'create', 'yield', 'resume', 'status'}\n" + "local keys = {'create', 'yield', 'resume', 'status', 'wrap'}\n" #ifdef OPENRESTY_LUAJIT "local get_req = require 'thread.exdata'\n" #else @@ -330,24 +376,18 @@ ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) "return std(...)\n" "end\n" "end\n" - "local create, resume = coroutine.create, coroutine.resume\n" - "coroutine.wrap = function(f)\n" - "local co = create(f)\n" - "return function(...) return select(2, resume(co, ...)) end\n" - "end\n" - "package.loaded.coroutine = coroutine"; - + "package.loaded.coroutine = coroutine" #if 0 "debug.sethook(function () collectgarbage() end, 'rl', 1)" #endif ; - rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine.wrap"); + 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.wrap(): %i: %s", + "failed to load Lua code for coroutine_api: %i: %s", rc, lua_tostring(L, -1)); lua_pop(L, 1); @@ -357,7 +397,7 @@ ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) 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.wrap(): %i: %s", + "failed to run the Lua code for coroutine_api: %i: %s", rc, lua_tostring(L, -1)); lua_pop(L, 1); } diff --git a/src/ngx_stream_lua_ctx.c b/src/ngx_stream_lua_ctx.c index 4d360a4b..365404e2 100644 --- a/src/ngx_stream_lua_ctx.c +++ b/src/ngx_stream_lua_ctx.c @@ -33,71 +33,6 @@ static ngx_int_t ngx_stream_lua_ngx_ctx_add_cleanup(ngx_stream_lua_request_t *r, static void ngx_stream_lua_ngx_ctx_cleanup(void *data); -int -ngx_stream_lua_ngx_get_ctx(lua_State *L) -{ - ngx_stream_lua_request_t *r; - ngx_stream_lua_ctx_t *ctx; - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); - if (ctx == NULL) { - return luaL_error(L, "no request ctx found"); - } - - if (ctx->ctx_ref == LUA_NOREF) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua create ngx.ctx table for the current request"); - - lua_pushliteral(L, ngx_stream_lua_ctx_tables_key); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_createtable(L, 0 /* narr */, 4 /* nrec */); - lua_pushvalue(L, -1); - ctx->ctx_ref = luaL_ref(L, -3); - - if (ngx_stream_lua_ngx_ctx_add_cleanup(r, ctx->ctx_ref) != NGX_OK) { - return luaL_error(L, "no memory"); - } - - return 1; - } - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua fetching existing ngx.ctx table for the current " - "request"); - - lua_pushliteral(L, ngx_stream_lua_ctx_tables_key); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_rawgeti(L, -1, ctx->ctx_ref); - - return 1; -} - - -int -ngx_stream_lua_ngx_set_ctx(lua_State *L) -{ - ngx_stream_lua_request_t *r; - ngx_stream_lua_ctx_t *ctx; - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); - if (ctx == NULL) { - return luaL_error(L, "no request ctx found"); - } - - return ngx_stream_lua_ngx_set_ctx_helper(L, r, ctx, 3); -} - - int ngx_stream_lua_ngx_set_ctx_helper(lua_State *L, ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, int index) @@ -138,7 +73,6 @@ ngx_stream_lua_ngx_set_ctx_helper(lua_State *L, ngx_stream_lua_request_t *r, } -#ifndef NGX_LUA_NO_FFI_API int ngx_stream_lua_ffi_get_ctx_ref(ngx_stream_lua_request_t *r) { @@ -171,7 +105,6 @@ ngx_stream_lua_ffi_set_ctx_ref(ngx_stream_lua_request_t *r, int ref) return NGX_OK; } -#endif /* NGX_LUA_NO_FFI_API */ static ngx_int_t diff --git a/src/ngx_stream_lua_ctx.h b/src/ngx_stream_lua_ctx.h index 9f751e7b..f8dfb86d 100644 --- a/src/ngx_stream_lua_ctx.h +++ b/src/ngx_stream_lua_ctx.h @@ -20,8 +20,6 @@ #include "ngx_stream_lua_common.h" -int ngx_stream_lua_ngx_get_ctx(lua_State *L); -int ngx_stream_lua_ngx_set_ctx(lua_State *L); int ngx_stream_lua_ngx_set_ctx_helper(lua_State *L, ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, int index); diff --git a/src/ngx_stream_lua_directive.c b/src/ngx_stream_lua_directive.c index d32edeae..5580106f 100644 --- a/src/ngx_stream_lua_directive.c +++ b/src/ngx_stream_lua_directive.c @@ -177,6 +177,19 @@ ngx_stream_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +char * +ngx_stream_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "lua_load_resty_core is deprecated (the lua-resty-core " + "library is required since " + "ngx_stream_lua v0.0.8)"); + + return NGX_CONF_OK; +} + + char * ngx_stream_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -748,11 +761,12 @@ ngx_stream_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len, found: - ngx_snprintf(out, len, "=%*s(%*s:%d)%Z", - tag_len, tag, cf->conf_file->file.name.data - + cf->conf_file->file.name.len - p, - p, cf->conf_file->line); - *chunkname_len = len; + p = ngx_snprintf(out, len, "=%*s(%*s:%d)%Z", + tag_len, tag, cf->conf_file->file.name.data + + cf->conf_file->file.name.len - p, + p, cf->conf_file->line); + + *chunkname_len = p - out - 1; /* exclude the trailing '\0' byte */ return out; } @@ -935,12 +949,12 @@ ngx_stream_lua_conf_read_lua_token(ngx_conf_t *cf, ngx_uint_t start_line; ngx_str_t *word; ngx_buf_t *b; -#if nginx_version >= 1009002 +#if defined(nginx_version) && nginx_version >= 1009002 ngx_buf_t *dump; #endif b = cf->conf_file->buffer; -#if nginx_version >= 1009002 +#if defined(nginx_version) && nginx_version >= 1009002 dump = cf->conf_file->dump; #endif start = b->pos; @@ -1011,7 +1025,7 @@ ngx_stream_lua_conf_read_lua_token(ngx_conf_t *cf, b->last = b->start + len + n; start = b->start; -#if nginx_version >= 1009002 +#if defined(nginx_version) && nginx_version >= 1009002 if (dump) { dump->last = ngx_cpymem(dump->last, b->start + len, size); } diff --git a/src/ngx_stream_lua_directive.h b/src/ngx_stream_lua_directive.h index f28b6b65..6b36bb13 100644 --- a/src/ngx_stream_lua_directive.h +++ b/src/ngx_stream_lua_directive.h @@ -45,6 +45,8 @@ char *ngx_stream_lua_init_worker_by_lua_block(ngx_conf_t *cf, char *ngx_stream_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char *ngx_stream_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_stream_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); char * diff --git a/src/ngx_stream_lua_initworkerby.c b/src/ngx_stream_lua_initworkerby.c index d92d4c6d..8370240a 100644 --- a/src/ngx_stream_lua_initworkerby.c +++ b/src/ngx_stream_lua_initworkerby.c @@ -24,6 +24,7 @@ #include "ngx_stream_lua_contentby.h" + static u_char *ngx_stream_lua_log_init_worker_error(ngx_log_t *log, u_char *buf, size_t len); @@ -68,6 +69,8 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) # endif ) { + /* disable init_worker_by_lua* and destroy lua VM in cache processes */ + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, "lua close the global Lua VM %p in the " "cache helper process %P", lmcf->lua, ngx_pid); @@ -77,9 +80,11 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) return NGX_OK; } + + #endif /* NGX_WIN32 */ -#if NGX_STREAM_LUA_HAVE_SA_RESTART +#if (NGX_STREAM_LUA_HAVE_SA_RESTART) if (lmcf->set_sa_restart) { ngx_stream_lua_set_sa_restart(ngx_cycle->log); } @@ -91,7 +96,6 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) conf_ctx = (ngx_stream_conf_ctx_t *) cycle->conf_ctx[ngx_stream_module.index]; - stream_ctx.main_conf = conf_ctx->main_conf; top_clcf = conf_ctx->srv_conf[ngx_stream_core_module.ctx_index]; top_llcf = conf_ctx->srv_conf[ngx_stream_lua_module.ctx_index]; @@ -120,12 +124,8 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) ngx_memcpy(fake_cycle, cycle, sizeof(ngx_cycle_t)); -#if defined(nginx_version) && nginx_version >= 9007 - ngx_queue_init(&fake_cycle->reusable_connections_queue); -#endif - if (ngx_array_init(&fake_cycle->listening, cycle->pool, cycle->listening.nelts || 1, sizeof(ngx_listening_t)) @@ -134,8 +134,6 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) goto failed; } -#if defined(nginx_version) && nginx_version >= 1003007 - if (ngx_array_init(&fake_cycle->paths, cycle->pool, cycle->paths.nelts || 1, sizeof(ngx_path_t *)) != NGX_OK) @@ -143,8 +141,6 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) goto failed; } -#endif - part = &cycle->open_files.part; ofile = part->elts; @@ -204,6 +200,12 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) return NGX_ERROR; } + stream_ctx.main_conf = ngx_pcalloc(conf.pool, + sizeof(void *) * ngx_stream_max_module); + if (stream_ctx.main_conf == NULL) { + return NGX_ERROR; + } + #if defined(nginx_version) && nginx_version >= 1009011 modules = cycle->modules; #else @@ -217,6 +219,21 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) module = modules[i]->ctx; + if (module->create_main_conf) { + cur = module->create_main_conf(&conf); + if (cur == NULL) { + return NGX_ERROR; + } + + if (ngx_modules[i]->index == ngx_stream_lua_module.index) { + ngx_memcpy(cur, + conf_ctx->main_conf[ngx_stream_lua_module.ctx_index], + sizeof(ngx_stream_lua_main_conf_t)); + } + + stream_ctx.main_conf[modules[i]->ctx_index] = cur; + } + if (module->create_srv_conf) { cur = module->create_srv_conf(&conf); if (cur == NULL) { @@ -281,25 +298,10 @@ ngx_stream_lua_init_worker(ngx_cycle_t *cycle) clcf->resolver_timeout = top_clcf->resolver_timeout; } -#if defined(nginx_version) && nginx_version >= 1003014 - -# if nginx_version >= 1009000 - +#if defined(nginx_version) && nginx_version >= 1009000 ngx_set_connection_log(s->connection, clcf->error_log); -# else - - -# endif - #else - - c->log->file = clcf->error_log->file; - - if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - c->log->log_level = clcf->error_log->log_level; - } - #endif ctx = ngx_stream_lua_create_ctx(s); diff --git a/src/ngx_stream_lua_input_filters.c b/src/ngx_stream_lua_input_filters.c new file mode 100644 index 00000000..159185cc --- /dev/null +++ b/src/ngx_stream_lua_input_filters.c @@ -0,0 +1,146 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_input_filters.c.tt2 + */ + + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t +ngx_stream_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_stream_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_stream_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_stream_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_STREAM, log, 0, + "stream 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_STREAM, log, 0, + "stream lua read partial line data: %*s", + dst - begin, begin); +#endif + + buf_in->buf->last = dst; + + return NGX_AGAIN; +} diff --git a/src/ngx_stream_lua_input_filters.h b/src/ngx_stream_lua_input_filters.h new file mode 100644 index 00000000..e65d572f --- /dev/null +++ b/src/ngx_stream_lua_input_filters.h @@ -0,0 +1,37 @@ + +/* + * !!! DO NOT EDIT DIRECTLY !!! + * This file was automatically generated from the following template: + * + * src/subsys/ngx_subsys_lua_input_filters.h.tt2 + */ + + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_ +#define _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_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_stream_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log); + +ngx_int_t ngx_stream_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_stream_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log); + + +#endif /* _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_log.c b/src/ngx_stream_lua_log.c index 17097080..c6e9c4cf 100644 --- a/src/ngx_stream_lua_log.c +++ b/src/ngx_stream_lua_log.c @@ -203,7 +203,7 @@ log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level, *p++ = ':'; p = ngx_snprintf(p, NGX_INT_T_LEN, "%d", - ar.currentline ? ar.currentline : ar.linedefined); + ar.currentline > 0 ? ar.currentline : ar.linedefined); *p++ = ':'; *p++ = ' '; diff --git a/src/ngx_stream_lua_misc.c b/src/ngx_stream_lua_misc.c index fab5902a..1e4bd8ae 100644 --- a/src/ngx_stream_lua_misc.c +++ b/src/ngx_stream_lua_misc.c @@ -20,106 +20,15 @@ #include "ngx_stream_lua_misc.h" -#include "ngx_stream_lua_ctx.h" #include "ngx_stream_lua_util.h" -static int ngx_stream_lua_ngx_get(lua_State *L); -static int ngx_stream_lua_ngx_set(lua_State *L); - -void -ngx_stream_lua_inject_misc_api(lua_State *L) -{ - /* ngx. getter and setter */ - lua_createtable(L, 0, 2); /* metatable for .ngx */ - lua_pushcfunction(L, ngx_stream_lua_ngx_get); - lua_setfield(L, -2, "__index"); - lua_pushcfunction(L, ngx_stream_lua_ngx_set); - lua_setfield(L, -2, "__newindex"); - lua_setmetatable(L, -2); -} - - - - -static int -ngx_stream_lua_ngx_get(lua_State *L) -{ - int status; - ngx_stream_lua_request_t *r; - u_char *p; - size_t len; - ngx_stream_lua_ctx_t *ctx; - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - lua_pushnil(L); - return 1; - } - - ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); - if (ctx == NULL) { - lua_pushnil(L); - return 1; - } - - p = (u_char *) luaL_checklstring(L, -1, &len); - - dd("ngx get %s", p); - - if (len == sizeof("status") - 1 - && ngx_strncmp(p, "status", sizeof("status") - 1) == 0) - { - ngx_stream_lua_check_fake_request(L, r); - - /* same as $status */ - - status = r->session->status; - - lua_pushinteger(L, status); - return 1; - } - - if (len == sizeof("ctx") - 1 - && ngx_strncmp(p, "ctx", sizeof("ctx") - 1) == 0) - { - return ngx_stream_lua_ngx_get_ctx(L); - } - - - dd("key %s not matched", p); - - lua_pushnil(L); - return 1; -} - - -static int -ngx_stream_lua_ngx_set(lua_State *L) +int +ngx_stream_lua_ffi_get_resp_status(ngx_stream_lua_request_t *r) { - ngx_stream_lua_request_t *r; - u_char *p; - size_t len; - - /* we skip the first argument that is the table */ - p = (u_char *) luaL_checklstring(L, 2, &len); - - - if (len == sizeof("ctx") - 1 - && ngx_strncmp(p, "ctx", sizeof("ctx") - 1) == 0) - { - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - return ngx_stream_lua_ngx_set_ctx(L); - } - - lua_rawset(L, -3); - return 0; + return r->session->status; } diff --git a/src/ngx_stream_lua_misc.h b/src/ngx_stream_lua_misc.h index d7a772dc..64f99e15 100644 --- a/src/ngx_stream_lua_misc.h +++ b/src/ngx_stream_lua_misc.h @@ -20,8 +20,6 @@ #include "ngx_stream_lua_common.h" -void ngx_stream_lua_inject_misc_api(lua_State *L); - #endif /* _NGX_STREAM_LUA_MISC_H_INCLUDED_ */ diff --git a/src/ngx_stream_lua_module.c b/src/ngx_stream_lua_module.c index b4047b43..6034add7 100644 --- a/src/ngx_stream_lua_module.c +++ b/src/ngx_stream_lua_module.c @@ -59,7 +59,7 @@ static ngx_conf_post_t ngx_stream_lua_lowat_post = -#if (NGX_STREAM_SSL) && defined(nginx_version) && nginx_version >= 1001013 +#if (NGX_STREAM_SSL) static ngx_conf_bitmask_t ngx_stream_lua_ssl_protocols[] = { { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, @@ -82,9 +82,9 @@ static ngx_command_t ngx_stream_lua_cmds[] = { { ngx_string("lua_load_resty_core"), NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + ngx_stream_lua_load_resty_core, NGX_STREAM_MAIN_CONF_OFFSET, - offsetof(ngx_stream_lua_main_conf_t, load_resty_core), + 0, NULL }, { ngx_string("lua_max_running_timers"), @@ -362,8 +362,6 @@ static ngx_command_t ngx_stream_lua_cmds[] = { #if (NGX_STREAM_SSL) -# if defined(nginx_version) && nginx_version >= 1001013 - { ngx_string("lua_ssl_protocols"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, @@ -371,8 +369,6 @@ static ngx_command_t ngx_stream_lua_cmds[] = { offsetof(ngx_stream_lua_srv_conf_t, ssl_protocols), &ngx_stream_lua_ssl_protocols }, -# endif - { ngx_string("lua_ssl_ciphers"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -437,7 +433,7 @@ static ngx_command_t ngx_stream_lua_cmds[] = { ngx_stream_module_t ngx_stream_lua_module_ctx = { - NULL, /* preconfiguration */ + NULL, /* preconfiguration */ ngx_stream_lua_init, /* postconfiguration */ ngx_stream_lua_create_main_conf, /* create main configuration */ @@ -453,14 +449,14 @@ ngx_module_t ngx_stream_lua_module = { NGX_MODULE_V1, &ngx_stream_lua_module_ctx, /* module context */ ngx_stream_lua_cmds, /* module directives */ - NGX_STREAM_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ ngx_stream_lua_init_worker, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ NGX_MODULE_V1_PADDING }; @@ -470,12 +466,13 @@ ngx_stream_lua_init(ngx_conf_t *cf) { ngx_int_t rc; volatile ngx_cycle_t *saved_cycle; - ngx_stream_lua_main_conf_t *lmcf; ngx_array_t *arr; + ngx_pool_cleanup_t *cln; + ngx_stream_handler_pt *h; + ngx_stream_lua_main_conf_t *lmcf; ngx_stream_core_main_conf_t *cmcf; - ngx_pool_cleanup_t *cln; if (ngx_process == NGX_PROCESS_SIGNALLER || ngx_test_config) { return NGX_OK; @@ -535,6 +532,7 @@ ngx_stream_lua_init(ngx_conf_t *cf) cln->handler = ngx_stream_lua_sema_mm_cleanup; + if (lmcf->lua == NULL) { dd("initializing lua vm"); @@ -552,14 +550,33 @@ ngx_stream_lua_init(ngx_conf_t *cf) #endif - lmcf->lua = ngx_stream_lua_init_vm(NULL, cf->cycle, cf->pool, lmcf, - cf->log, NULL); - if (lmcf->lua == NULL) { - ngx_conf_log_error(NGX_LOG_ERR, cf, 0, - "failed to initialize Lua VM"); - return NGX_ERROR; + rc = ngx_stream_lua_init_vm(&lmcf->lua, NULL, cf->cycle, cf->pool, + lmcf, cf->log, NULL); + if (rc != NGX_OK) { + if (rc == NGX_DECLINED) { + ngx_stream_lua_assert(lmcf->lua != NULL); + + ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, + "failed to load the 'resty.core' module " + "(https://github.com/openresty/lua-resty" + "-core); ensure you are using an OpenResty " + "release from https://openresty.org/en/" + "download.html (reason: %s)", + lua_tostring(lmcf->lua, -1)); + + } else { + /* rc == NGX_ERROR */ + ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, + "failed to initialize Lua VM"); + } + + return NGX_ERROR; } + /* rc == NGX_OK */ + + ngx_stream_lua_assert(lmcf->lua != NULL); + if (!lmcf->requires_shm && lmcf->init_handler) { saved_cycle = ngx_cycle; ngx_cycle = cf->cycle; @@ -647,7 +664,6 @@ ngx_stream_lua_create_main_conf(ngx_conf_t *cf) */ lmcf->pool = cf->pool; - lmcf->load_resty_core = NGX_CONF_UNSET; lmcf->max_pending_timers = NGX_CONF_UNSET; lmcf->max_running_timers = NGX_CONF_UNSET; #if (NGX_PCRE) @@ -679,10 +695,6 @@ ngx_stream_lua_init_main_conf(ngx_conf_t *cf, void *conf) { ngx_stream_lua_main_conf_t *lmcf = conf; - if (lmcf->load_resty_core == NGX_CONF_UNSET) { - lmcf->load_resty_core = 1; - } - #if (NGX_PCRE) if (lmcf->regex_cache_max_entries == NGX_CONF_UNSET) { lmcf->regex_cache_max_entries = 1024; @@ -775,9 +787,10 @@ ngx_stream_lua_create_srv_conf(ngx_conf_t *cf) static char * ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { +#if (NGX_STREAM_SSL) ngx_stream_lua_srv_conf_t *prev = parent; ngx_stream_lua_srv_conf_t *conf = child; -#if (NGX_STREAM_SSL) + ngx_stream_ssl_conf_t *sscf; dd("merge srv conf"); @@ -825,15 +838,11 @@ ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) #if (NGX_STREAM_SSL) -# if defined(nginx_version) && nginx_version >= 1001013 - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 |NGX_SSL_TLSv1_2); -# endif - ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); @@ -924,26 +933,13 @@ ngx_stream_lua_set_ssl(ngx_conf_t *cf, ngx_stream_lua_srv_conf_t *lscf) return NGX_ERROR; } - if (lscf->ssl_trusted_certificate.len) { - -#if defined(nginx_version) && nginx_version >= 1003007 - - if (ngx_ssl_trusted_certificate(cf, lscf->ssl, - &lscf->ssl_trusted_certificate, - lscf->ssl_verify_depth) - != NGX_OK) - { - return NGX_ERROR; - } - -#else - - ngx_log_error(NGX_LOG_CRIT, cf->log, 0, "at least nginx 1.3.7 is " - "required for the \"lua_ssl_trusted_certificate\" " - "directive"); + if (lscf->ssl_trusted_certificate.len + && ngx_ssl_trusted_certificate(cf, lscf->ssl, + &lscf->ssl_trusted_certificate, + lscf->ssl_verify_depth) + != NGX_OK) + { return NGX_ERROR; - -#endif } dd("ssl crl: %.*s", (int) lscf->ssl_crl.len, lscf->ssl_crl.data); diff --git a/src/ngx_stream_lua_phase.c b/src/ngx_stream_lua_phase.c index 26a292d7..b1c6a2d4 100644 --- a/src/ngx_stream_lua_phase.c +++ b/src/ngx_stream_lua_phase.c @@ -23,8 +23,6 @@ #include "ngx_stream_lua_ctx.h" -static int ngx_stream_lua_ngx_get_phase(lua_State *L); - static int ngx_stream_lua_ngx_get_phase(lua_State *L) diff --git a/src/ngx_stream_lua_regex.c b/src/ngx_stream_lua_regex.c index 7a6e91bf..de71b269 100644 --- a/src/ngx_stream_lua_regex.c +++ b/src/ngx_stream_lua_regex.c @@ -20,7 +20,6 @@ #if (NGX_PCRE) -#include "ngx_stream_lua_regex.h" #include "ngx_stream_lua_pcrefix.h" #include "ngx_stream_lua_script.h" #include "ngx_stream_lua_util.h" @@ -33,10 +32,8 @@ #endif -#define NGX_LUA_RE_COMPILE_ONCE (1<<0) #define NGX_LUA_RE_MODE_DFA (1<<1) #define NGX_LUA_RE_MODE_JIT (1<<2) -#define NGX_LUA_RE_MODE_DUPNAMES (1<<3) #define NGX_LUA_RE_NO_UTF8_CHECK (1<<4) #define NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT (100) @@ -75,7 +72,6 @@ typedef struct { typedef struct { - ngx_stream_lua_cleanup_pt *cleanup; ngx_stream_lua_request_t *request; pcre *regex; @@ -87,25 +83,10 @@ typedef struct { } ngx_stream_lua_regex_ctx_t; -static int ngx_stream_lua_ngx_re_gmatch_iterator(lua_State *L); -static ngx_uint_t ngx_stream_lua_ngx_re_parse_opts(lua_State *L, - ngx_stream_lua_regex_compile_t *re, ngx_str_t *opts, int narg); -static int ngx_stream_lua_ngx_re_sub_helper(lua_State *L, unsigned global); -static int ngx_stream_lua_ngx_re_match_helper(lua_State *L, int wantcaps); -static int ngx_stream_lua_ngx_re_find(lua_State *L); -static int ngx_stream_lua_ngx_re_match(lua_State *L); -static int ngx_stream_lua_ngx_re_gmatch(lua_State *L); -static int ngx_stream_lua_ngx_re_sub(lua_State *L); -static int ngx_stream_lua_ngx_re_gsub(lua_State *L); static void ngx_stream_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd); static ngx_int_t ngx_stream_lua_regex_compile( ngx_stream_lua_regex_compile_t *rc); -static void ngx_stream_lua_ngx_re_gmatch_cleanup(void *data); -static int ngx_stream_lua_ngx_re_gmatch_gc(lua_State *L); -static void ngx_stream_lua_re_collect_named_captures(lua_State *L, - int res_tb_idx, u_char *name_table, int name_count, int name_entry_size, - unsigned flags, ngx_str_t *subj); #define ngx_stream_lua_regex_exec(re, e, s, start, captures, size, \ @@ -120,1901 +101,6 @@ static void ngx_stream_lua_re_collect_named_captures(lua_State *L, captures, size, ws, wscount) -static int -ngx_stream_lua_ngx_re_match(lua_State *L) -{ - return ngx_stream_lua_ngx_re_match_helper(L, 1 /* want captures */); -} - - -static int -ngx_stream_lua_ngx_re_find(lua_State *L) -{ - return ngx_stream_lua_ngx_re_match_helper(L, 0 /* want captures */); -} - - -static int -ngx_stream_lua_ngx_re_match_helper(lua_State *L, int wantcaps) -{ - /* u_char *p; */ - int res_tb_idx = 0; - ngx_stream_lua_request_t *r; - ngx_str_t subj; - ngx_str_t pat; - ngx_str_t opts; - const char *msg; - ngx_int_t rc; - ngx_uint_t n; - int i; - ngx_int_t pos = 0; - int nargs; - int *cap = NULL; - int ovecsize; - int has_ctx = 0; - ngx_uint_t flags; - ngx_pool_t *pool, *old_pool; - u_char errstr[NGX_MAX_CONF_ERRSTR + 1]; - pcre_extra *sd = NULL; - int name_entry_size = 0, name_count; - u_char *name_table = NULL; - int exec_opts; - int group_id = 0; - - ngx_stream_lua_regex_t *re; - ngx_stream_lua_main_conf_t *lmcf; - ngx_stream_lua_regex_compile_t re_comp; - - nargs = lua_gettop(L); - - if (nargs != 2 && nargs != 3 && nargs != 4 && nargs != 5) { - return luaL_error(L, "expecting 2, 3, 4 or 5 arguments, " - "but got %d", nargs); - } - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len); - pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); - - ngx_memzero(&re_comp, sizeof(ngx_stream_lua_regex_compile_t)); - - if (nargs >= 3) { - opts.data = (u_char *) luaL_checklstring(L, 3, &opts.len); - - if (nargs >= 4) { - if (!lua_isnil(L, 4)) { - luaL_checktype(L, 4, LUA_TTABLE); - has_ctx = 1; - - lua_getfield(L, 4, "pos"); - if (lua_isnumber(L, -1)) { - pos = (ngx_int_t) lua_tointeger(L, -1); - if (pos <= 0) { - pos = 0; - - } else { - pos--; /* 1-based on the Lua land */ - } - - } else if (lua_isnil(L, -1)) { - pos = 0; - - } else { - msg = lua_pushfstring(L, "bad pos field type in the ctx " - "table argument: %s", - luaL_typename(L, -1)); - - return luaL_argerror(L, 4, msg); - } - - lua_pop(L, 1); - } - } - - } else { - opts.data = (u_char *) ""; - opts.len = 0; - } - - if (nargs == 5) { - if (wantcaps) { - luaL_checktype(L, 5, LUA_TTABLE); - res_tb_idx = 5; - -#if 0 - /* clear the Lua table */ - lua_pushnil(L); - while (lua_next(L, res_tb_idx) != 0) { - lua_pop(L, 1); - lua_pushvalue(L, -1); - lua_pushnil(L); - lua_rawset(L, res_tb_idx); - } -#endif - - } else { - group_id = luaL_checkint(L, 5); - if (group_id < 0) { - group_id = 0; - } - } - } - - re_comp.options = 0; - - flags = ngx_stream_lua_ngx_re_parse_opts(L, &re_comp, &opts, 3); - - lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); - - if (flags & NGX_LUA_RE_COMPILE_ONCE) { - pool = lmcf->pool; - - dd("server pool %p", lmcf->pool); - - lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( - regex_cache_key)); - lua_rawget(L, LUA_REGISTRYINDEX); /* table */ - - lua_pushliteral(L, "m"); - lua_pushvalue(L, 2); /* table regex */ - - dd("options size: %d", (int) sizeof(re_comp.options)); - - lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options)); - /* table regex opts */ - - lua_concat(L, 3); /* table key */ - lua_pushvalue(L, -1); /* table key key */ - - dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)), - lua_tostring(L, -1)); - - lua_rawget(L, -3); /* table key re */ - re = lua_touserdata(L, -1); - - lua_pop(L, 1); /* table key */ - - if (re) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua regex cache hit for match regex \"%s\" with " - "options \"%s\"", pat.data, opts.data); - - lua_pop(L, 2); - - dd("restoring regex %p, ncaptures %d, captures %p", re->regex, - re->ncaptures, re->captures); - - re_comp.regex = re->regex; - sd = re->regex_sd; - re_comp.captures = re->ncaptures; - cap = re->captures; - - if (flags & NGX_LUA_RE_MODE_DFA) { - ovecsize = 2; - - } else { - ovecsize = (re->ncaptures + 1) * 3; - } - - goto exec; - } - - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua regex cache miss for match regex \"%s\" " - "with options \"%s\"", pat.data, opts.data); - - if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) { - - if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) { - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, - "lua exceeding regex cache max entries (%i)", - lmcf->regex_cache_max_entries); - - lmcf->regex_cache_entries++; - } - - pool = r->pool; - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - } - - } else { - pool = r->pool; - } - - dd("pool %p, r pool %p", pool, r->pool); - - re_comp.pattern = pat; - re_comp.err.len = NGX_MAX_CONF_ERRSTR; - re_comp.err.data = errstr; - re_comp.pool = pool; - - ngx_log_debug5(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua compiling match regex \"%s\" with options \"%s\" " - "(compile once: %d) (dfa mode: %d) (jit mode: %d)", - pat.data, opts.data, - (flags & NGX_LUA_RE_COMPILE_ONCE) != 0, - (flags & NGX_LUA_RE_MODE_DFA) != 0, - (flags & NGX_LUA_RE_MODE_JIT) != 0); - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - rc = ngx_stream_lua_regex_compile(&re_comp); - - ngx_stream_lua_pcre_malloc_done(old_pool); - - if (rc != NGX_OK) { - dd("compile failed"); - - lua_pushnil(L); - if (!wantcaps) { - lua_pushnil(L); - } - lua_pushlstring(L, (char *) re_comp.err.data, re_comp.err.len); - return wantcaps ? 2 : 3; - } - -#if (LUA_HAVE_PCRE_JIT) - - if (flags & NGX_LUA_RE_MODE_JIT) { - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); - - if (sd && lmcf->jit_stack) { - pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack); - } - - ngx_stream_lua_pcre_malloc_done(old_pool); - -# if (NGX_DEBUG) - dd("sd = %p", sd); - - if (msg != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre study failed with PCRE_STUDY_JIT_COMPILE: " - "%s (%p)", msg, sd); - } - - if (sd != NULL) { - int jitted; - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); - - ngx_stream_lua_pcre_malloc_done(old_pool); - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre JIT compiling result: %d", jitted); - } -# endif /* !(NGX_DEBUG) */ - - } else { - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - sd = pcre_study(re_comp.regex, 0, &msg); - - ngx_stream_lua_pcre_malloc_done(old_pool); - -# if (NGX_DEBUG) - dd("sd = %p", sd); - - if (msg != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre_study failed with PCRE_STUDY_JIT_COMPILE: " - "%s (%p)", msg, sd); - } -# endif /* NGX_DEBUG */ - } - -#else /* !(LUA_HAVE_PCRE_JIT) */ - - if (flags & NGX_LUA_RE_MODE_JIT) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "your pcre build does not have JIT support and " - "the \"j\" regex option is ignored"); - } - -#endif /* LUA_HAVE_PCRE_JIT */ - - if (sd && lmcf->regex_match_limit > 0) { - sd->flags |= PCRE_EXTRA_MATCH_LIMIT; - sd->match_limit = lmcf->regex_match_limit; - } - - dd("compile done, captures %d", (int) re_comp.captures); - - 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) { - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - msg = "no memory"; - goto error; - } - - if (flags & NGX_LUA_RE_COMPILE_ONCE) { - - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua saving compiled regex (%d captures) into the cache " - "(entries %i)", re_comp.captures, - lmcf->regex_cache_entries); - - re = ngx_palloc(pool, sizeof(ngx_stream_lua_regex_t)); - if (re == NULL) { - msg = "no memory"; - goto error; - } - - dd("saving regex %p, ncaptures %d, captures %p", re_comp.regex, - re_comp.captures, cap); - - re->regex = re_comp.regex; - re->regex_sd = sd; - re->ncaptures = re_comp.captures; - re->captures = cap; - re->replace = NULL; - - lua_pushlightuserdata(L, re); /* table key value */ - lua_rawset(L, -3); /* table */ - lua_pop(L, 1); - - if (lmcf) { - lmcf->regex_cache_entries++; - } - } - -exec: - - if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT, - &name_count) != 0) - { - msg = "cannot acquire named subpattern count"; - goto error; - } - - if (name_count > 0) { - if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE, - &name_entry_size) != 0) - { - msg = "cannot acquire named subpattern entry size"; - goto error; - } - - if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE, - &name_table) != 0) - { - msg = "cannot acquire named subpattern table"; - goto error; - } - } - - if (flags & NGX_LUA_RE_NO_UTF8_CHECK) { - exec_opts = PCRE_NO_UTF8_CHECK; - - } else { - exec_opts = 0; - } - - if (flags & NGX_LUA_RE_MODE_DFA) { - -#if LUA_HAVE_PCRE_DFA - - int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; - rc = ngx_stream_lua_regex_dfa_exec(re_comp.regex, sd, &subj, - (int) pos, cap, ovecsize, ws, - sizeof(ws)/sizeof(ws[0]), exec_opts); - -#else /* LUA_HAVE_PCRE_DFA */ - - msg = "at least pcre 6.0 is required for the DFA mode"; - goto error; - -#endif /* LUA_HAVE_PCRE_DFA */ - - } else { - rc = ngx_stream_lua_regex_exec(re_comp.regex, sd, &subj, (int) pos, cap, - ovecsize, exec_opts); - } - - if (rc == NGX_REGEX_NO_MATCHED) { - ngx_log_debug3(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "regex \"%V\" not matched on string \"%V\" starting " - "from %i", &pat, &subj, pos); - - if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (sd) { - ngx_stream_lua_regex_free_study_data(pool, sd); - } - - ngx_pfree(pool, re_comp.regex); - ngx_pfree(pool, cap); - } - - lua_pushnil(L); - return 1; - } - - if (rc < 0) { - msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d", (int) rc); - goto error; - } - - if (rc == 0) { - if (flags & NGX_LUA_RE_MODE_DFA) { - rc = 1; - - } else { - msg = "capture size too small"; - goto error; - } - } - - dd("rc = %d", (int) rc); - - if (has_ctx) { /* having ctx table */ - pos = cap[1]; - lua_pushinteger(L, (lua_Integer) (pos + 1)); - lua_setfield(L, 4, "pos"); - } - - if (!wantcaps) { - if (group_id > re_comp.captures) { - lua_pushnil(L); - lua_pushnil(L); - lua_pushliteral(L, "nth out of bound"); - return 3; - } - - if (group_id >= rc) { - lua_pushnil(L); - lua_pushnil(L); - return 2; - } - - { - int from, to; - - from = cap[group_id * 2] + 1; - to = cap[group_id * 2 + 1]; - if (from < 0 || to < 0) { - lua_pushnil(L); - lua_pushnil(L); - return 2; - } - - lua_pushinteger(L, from); - lua_pushinteger(L, to); - return 2; - } - } - - if (res_tb_idx == 0) { - lua_createtable(L, re_comp.captures || 1 /* narr */, - name_count /* nrec */); - res_tb_idx = lua_gettop(L); - } - - for (i = 0, n = 0; i <= re_comp.captures; i++, n += 2) { - dd("capture %d: %d %d", i, cap[n], cap[n + 1]); - if (i >= rc || cap[n] < 0) { - lua_pushboolean(L, 0); - - } else { - lua_pushlstring(L, (char *) &subj.data[cap[n]], - cap[n + 1] - cap[n]); - - dd("pushing capture %s at %d", lua_tostring(L, -1), (int) i); - } - - lua_rawseti(L, res_tb_idx, (int) i); - } - - if (name_count > 0) { - ngx_stream_lua_re_collect_named_captures(L, res_tb_idx, name_table, - name_count, name_entry_size, - flags, &subj); - } - - if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { - - if (sd) { - ngx_stream_lua_regex_free_study_data(pool, sd); - } - - ngx_pfree(pool, re_comp.regex); - ngx_pfree(pool, cap); - } - - return 1; - -error: - - if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (sd) { - ngx_stream_lua_regex_free_study_data(pool, sd); - } - - if (re_comp.regex) { - ngx_pfree(pool, re_comp.regex); - } - - if (cap) { - ngx_pfree(pool, cap); - } - } - - lua_pushnil(L); - if (!wantcaps) { - lua_pushnil(L); - } - lua_pushstring(L, msg); - return wantcaps ? 2 : 3; -} - - -static int -ngx_stream_lua_ngx_re_gmatch(lua_State *L) -{ - ngx_stream_lua_request_t *r; - ngx_str_t subj; - ngx_str_t pat; - ngx_str_t opts; - int ovecsize; - const char *msg; - int nargs; - ngx_int_t flags; - int *cap = NULL; - ngx_int_t rc; - ngx_pool_t *pool, *old_pool; - u_char errstr[NGX_MAX_CONF_ERRSTR + 1]; - pcre_extra *sd = NULL; - - ngx_stream_lua_regex_t *re; - ngx_stream_lua_regex_ctx_t *ctx; - ngx_stream_lua_main_conf_t *lmcf; - ngx_stream_lua_cleanup_t *cln; - ngx_stream_lua_regex_compile_t re_comp; - - nargs = lua_gettop(L); - - if (nargs != 2 && nargs != 3) { - return luaL_error(L, "expecting two or three arguments, but got %d", - nargs); - } - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len); - pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); - - if (nargs == 3) { - opts.data = (u_char *) luaL_checklstring(L, 3, &opts.len); - lua_pop(L, 1); - - } else { - opts.data = (u_char *) ""; - opts.len = 0; - } - - /* stack: subj regex */ - - re_comp.options = 0; - - flags = ngx_stream_lua_ngx_re_parse_opts(L, &re_comp, &opts, 3); - - lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); - - if (flags & NGX_LUA_RE_COMPILE_ONCE) { - pool = lmcf->pool; - - dd("server pool %p", lmcf->pool); - - lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( - regex_cache_key)); - lua_rawget(L, LUA_REGISTRYINDEX); /* table */ - - lua_pushliteral(L, "m"); - lua_pushvalue(L, 2); /* table regex */ - - dd("options size: %d", (int) sizeof(re_comp.options)); - - lua_pushlstring(L, (char *) &re_comp.options, - sizeof(re_comp.options)); /* table regex opts */ - - lua_concat(L, 3); /* table key */ - lua_pushvalue(L, -1); /* table key key */ - - dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)), - lua_tostring(L, -1)); - - lua_rawget(L, -3); /* table key re */ - re = lua_touserdata(L, -1); - - lua_pop(L, 1); /* table key */ - - if (re) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua regex cache hit for match regex \"%s\" " - "with options \"%s\"", pat.data, opts.data); - - lua_pop(L, 2); - - dd("restoring regex %p, ncaptures %d, captures %p", re->regex, - re->ncaptures, re->captures); - - re_comp.regex = re->regex; - sd = re->regex_sd; - re_comp.captures = re->ncaptures; - cap = re->captures; - - if (flags & NGX_LUA_RE_MODE_DFA) { - ovecsize = 2; - - } else { - ovecsize = (re->ncaptures + 1) * 3; - } - - goto compiled; - } - - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua regex cache miss for match regex \"%s\" " - "with options \"%s\"", pat.data, opts.data); - - if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) { - - if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) { - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, - "lua exceeding regex cache max entries (%i)", - lmcf->regex_cache_max_entries); - - lmcf->regex_cache_entries++; - } - - pool = r->pool; - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - } - - } else { - pool = r->pool; - } - - re_comp.pattern = pat; - re_comp.err.len = NGX_MAX_CONF_ERRSTR; - re_comp.err.data = errstr; - re_comp.pool = pool; - - ngx_log_debug5(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua compiling gmatch regex \"%s\" with options \"%s\" " - "(compile once: %d) (dfa mode: %d) (jit mode: %d)", - pat.data, opts.data, - (flags & NGX_LUA_RE_COMPILE_ONCE) != 0, - (flags & NGX_LUA_RE_MODE_DFA) != 0, - (flags & NGX_LUA_RE_MODE_JIT) != 0); - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - rc = ngx_stream_lua_regex_compile(&re_comp); - - ngx_stream_lua_pcre_malloc_done(old_pool); - - if (rc != NGX_OK) { - dd("compile failed"); - - lua_pushnil(L); - lua_pushlstring(L, (char *) re_comp.err.data, re_comp.err.len); - return 2; - } - -#if LUA_HAVE_PCRE_JIT - - if (flags & NGX_LUA_RE_MODE_JIT) { - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); - - if (sd && lmcf->jit_stack) { - pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack); - } - - ngx_stream_lua_pcre_malloc_done(old_pool); - -# if (NGX_DEBUG) - dd("sd = %p", sd); - - if (msg != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre_study failed with PCRE_STUDY_JIT_COMPILE: " - "%s (%p)", msg, sd); - } - - if (sd != NULL) { - int jitted; - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); - - ngx_stream_lua_pcre_malloc_done(old_pool); - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre JIT compiling result: %d", jitted); - } -# endif /* NGX_DEBUG */ - - } else { - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - sd = pcre_study(re_comp.regex, 0, &msg); - - ngx_stream_lua_pcre_malloc_done(old_pool); - -# if (NGX_DEBUG) - dd("sd = %p", sd); - - if (msg != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre study failed with PCRE_STUDY_JIT_COMPILE: " - "%s (%p)", msg, sd); - } -# endif /* NGX_DEBUG */ - } - -#else /* LUA_HAVE_PCRE_JIT */ - - if (flags & NGX_LUA_RE_MODE_JIT) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "your pcre build does not have JIT support and " - "the \"j\" regex option is ignored"); - } - -#endif /* LUA_HAVE_PCRE_JIT */ - - if (sd && lmcf->regex_match_limit > 0) { - sd->flags |= PCRE_EXTRA_MATCH_LIMIT; - sd->match_limit = lmcf->regex_match_limit; - } - - dd("compile done, captures %d", re_comp.captures); - - if (flags & NGX_LUA_RE_MODE_DFA) { - ovecsize = 2; - re_comp.captures = 0; - - } else { - ovecsize = (re_comp.captures + 1) * 3; - } - - cap = ngx_palloc(pool, ovecsize * sizeof(int)); - if (cap == NULL) { - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - msg = "no memory"; - goto error; - } - - if (flags & NGX_LUA_RE_COMPILE_ONCE) { - - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua saving compiled regex (%d captures) into the cache " - "(entries %i)", re_comp.captures, - lmcf->regex_cache_entries); - - re = ngx_palloc(pool, sizeof(ngx_stream_lua_regex_t)); - if (re == NULL) { - msg = "no memory"; - goto error; - } - - dd("saving regex %p, ncaptures %d, captures %p", re_comp.regex, - re_comp.captures, cap); - - re->regex = re_comp.regex; - re->regex_sd = sd; - re->ncaptures = re_comp.captures; - re->captures = cap; - re->replace = NULL; - - lua_pushlightuserdata(L, re); /* table key value */ - lua_rawset(L, -3); /* table */ - lua_pop(L, 1); - - if (lmcf) { - lmcf->regex_cache_entries++; - } - } - -compiled: - - lua_settop(L, 1); - - ctx = lua_newuserdata(L, sizeof(ngx_stream_lua_regex_ctx_t)); - - ctx->request = r; - ctx->regex = re_comp.regex; - ctx->regex_sd = sd; - ctx->ncaptures = re_comp.captures; - ctx->captures = cap; - ctx->captures_len = ovecsize; - ctx->flags = (uint8_t) flags; - - if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { - lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ - lua_pushcfunction(L, ngx_stream_lua_ngx_re_gmatch_gc); - lua_setfield(L, -2, "__gc"); - lua_setmetatable(L, -2); - - cln = ngx_stream_lua_cleanup_add(r, 0); - if (cln == NULL) { - msg = "no memory"; - goto error; - } - - cln->handler = ngx_stream_lua_ngx_re_gmatch_cleanup; - cln->data = ctx; - ctx->cleanup = &cln->handler; - - } else { - ctx->cleanup = NULL; - } - - lua_pushinteger(L, 0); - - /* upvalues in order: subj ctx offset */ - lua_pushcclosure(L, ngx_stream_lua_ngx_re_gmatch_iterator, 3); - - return 1; - -error: - - if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (sd) { - ngx_stream_lua_regex_free_study_data(pool, sd); - } - - if (re_comp.regex) { - ngx_pfree(pool, re_comp.regex); - } - - if (cap) { - ngx_pfree(pool, cap); - } - } - - lua_pushnil(L); - lua_pushstring(L, msg); - return 2; -} - - -static int -ngx_stream_lua_ngx_re_gmatch_iterator(lua_State *L) -{ - ngx_stream_lua_request_t *r; - int *cap; - ngx_int_t rc; - ngx_uint_t n; - int i; - ngx_str_t subj; - int offset; - const char *msg = NULL; - int name_entry_size = 0, name_count; - u_char *name_table = NULL; - int exec_opts; - - ngx_stream_lua_regex_ctx_t *ctx; - - /* upvalues in order: subj ctx offset */ - - subj.data = (u_char *) lua_tolstring(L, lua_upvalueindex(1), &subj.len); - ctx = (ngx_stream_lua_regex_ctx_t *) lua_touserdata(L, lua_upvalueindex(2)); - offset = (int) lua_tointeger(L, lua_upvalueindex(3)); - - if (offset < 0) { - lua_pushnil(L); - return 1; - } - - cap = ctx->captures; - - dd("offset %d, r %p, subj %s", (int) offset, ctx->request, subj.data); - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - if (r != ctx->request || r->pool != ctx->request->pool) { - return luaL_error(L, "attempt to use ngx.re.gmatch iterator in a " - "request that did not create it"); - } - - dd("regex exec..."); - - if (pcre_fullinfo(ctx->regex, NULL, PCRE_INFO_NAMECOUNT, - &name_count) != 0) - { - msg = "cannot acquire named subpattern count"; - goto error; - } - - if (name_count > 0) { - if (pcre_fullinfo(ctx->regex, NULL, PCRE_INFO_NAMEENTRYSIZE, - &name_entry_size) != 0) - { - msg = "cannot acquire named subpattern entry size"; - goto error; - } - - if (pcre_fullinfo(ctx->regex, NULL, PCRE_INFO_NAMETABLE, - &name_table) != 0) - { - msg = "cannot acquire named subpattern table"; - goto error; - } - } - - if (ctx->flags & NGX_LUA_RE_NO_UTF8_CHECK) { - exec_opts = PCRE_NO_UTF8_CHECK; - - } else { - exec_opts = 0; - } - - if (ctx->flags & NGX_LUA_RE_MODE_DFA) { - -#if LUA_HAVE_PCRE_DFA - - int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; - - rc = ngx_stream_lua_regex_dfa_exec(ctx->regex, ctx->regex_sd, &subj, - offset, cap, ctx->captures_len, ws, - sizeof(ws)/sizeof(ws[0]), exec_opts); - -#else /* LUA_HAVE_PCRE_DFA */ - msg = "at least pcre 6.0 is required for the DFA mode"; - goto error; - -#endif /* LUA_HAVE_PCRE_DFA */ - - } else { - rc = ngx_stream_lua_regex_exec(ctx->regex, ctx->regex_sd, &subj, - offset, cap, ctx->captures_len, - exec_opts); - } - - if (rc == NGX_REGEX_NO_MATCHED) { - /* set upvalue "offset" to -1 */ - lua_pushinteger(L, -1); - lua_replace(L, lua_upvalueindex(3)); - - if (!(ctx->flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (ctx->regex_sd) { - ngx_stream_lua_regex_free_study_data(r->pool, ctx->regex_sd); - ctx->regex_sd = NULL; - } - - ngx_pfree(r->pool, cap); - } - - lua_pushnil(L); - return 1; - } - - if (rc < 0) { - msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d", (int) rc); - goto error; - } - - if (rc == 0) { - if (ctx->flags & NGX_LUA_RE_MODE_DFA) { - rc = 1; - - } else { - goto error; - } - } - - dd("rc = %d", (int) rc); - - lua_createtable(L, ctx->ncaptures || 1 /* narr */, name_count /* nrec */); - - for (i = 0, n = 0; i <= ctx->ncaptures; i++, n += 2) { - dd("capture %d: %d %d", i, cap[n], cap[n + 1]); - if (i >= rc || cap[n] < 0) { - lua_pushboolean(L, 0); - - } else { - lua_pushlstring(L, (char *) &subj.data[cap[n]], - cap[n + 1] - cap[n]); - - dd("pushing capture %s at %d", lua_tostring(L, -1), (int) i); - } - - lua_rawseti(L, -2, (int) i); - } - - if (name_count > 0) { - ngx_stream_lua_re_collect_named_captures(L, lua_gettop(L), name_table, - name_count, name_entry_size, - ctx->flags, &subj); - } - - offset = cap[1]; - if (offset == cap[0]) { - offset++; - } - - if (offset > (ssize_t) subj.len) { - offset = -1; - - if (!(ctx->flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (ctx->regex_sd) { - ngx_stream_lua_regex_free_study_data(r->pool, ctx->regex_sd); - ctx->regex_sd = NULL; - } - - ngx_pfree(r->pool, cap); - } - } - - lua_pushinteger(L, offset); - lua_replace(L, lua_upvalueindex(3)); - - return 1; - -error: - - lua_pushinteger(L, -1); - lua_replace(L, lua_upvalueindex(3)); - - if (!(ctx->flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (ctx->regex_sd) { - ngx_stream_lua_regex_free_study_data(r->pool, ctx->regex_sd); - ctx->regex_sd = NULL; - } - - ngx_pfree(r->pool, cap); - } - - lua_pushnil(L); - lua_pushstring(L, msg); - return 2; -} - - -static ngx_uint_t -ngx_stream_lua_ngx_re_parse_opts(lua_State *L, - ngx_stream_lua_regex_compile_t *re, ngx_str_t *opts, int narg) -{ - u_char *p; - const char *msg; - ngx_uint_t flags; - - flags = 0; - p = opts->data; - - while (*p != '\0') { - switch (*p) { - case 'i': - re->options |= NGX_REGEX_CASELESS; - break; - - case 's': - re->options |= PCRE_DOTALL; - break; - - case 'm': - re->options |= PCRE_MULTILINE; - break; - - case 'u': - re->options |= PCRE_UTF8; - break; - - case 'U': - re->options |= PCRE_UTF8; - flags |= NGX_LUA_RE_NO_UTF8_CHECK; - break; - - case 'x': - re->options |= PCRE_EXTENDED; - break; - - case 'o': - flags |= NGX_LUA_RE_COMPILE_ONCE; - break; - - case 'j': - flags |= NGX_LUA_RE_MODE_JIT; - break; - - case 'd': - flags |= NGX_LUA_RE_MODE_DFA; - break; - - case 'a': - re->options |= PCRE_ANCHORED; - break; - -#if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 12) - case 'D': - re->options |= PCRE_DUPNAMES; - flags |= NGX_LUA_RE_MODE_DUPNAMES; - break; - - case 'J': - re->options |= PCRE_JAVASCRIPT_COMPAT; - break; -#endif - - default: - msg = lua_pushfstring(L, "unknown flag \"%c\" (flags \"%s\")", - *p, opts->data); - return luaL_argerror(L, narg, msg); - } - - p++; - } - - /* pcre does not support JIT for DFA mode yet, - * so if DFA mode is specified, we turn off JIT automatically - * */ - if ((flags & NGX_LUA_RE_MODE_JIT) && (flags & NGX_LUA_RE_MODE_DFA)) { - flags &= ~NGX_LUA_RE_MODE_JIT; - } - - return flags; -} - - -static int -ngx_stream_lua_ngx_re_sub(lua_State *L) -{ - return ngx_stream_lua_ngx_re_sub_helper(L, 0 /* global */); -} - - -static int -ngx_stream_lua_ngx_re_gsub(lua_State *L) -{ - return ngx_stream_lua_ngx_re_sub_helper(L, 1 /* global */); -} - - -static int -ngx_stream_lua_ngx_re_sub_helper(lua_State *L, unsigned global) -{ - ngx_stream_lua_request_t *r; - ngx_str_t subj; - ngx_str_t pat; - ngx_str_t opts; - ngx_str_t tpl; - ngx_pool_t *pool, *old_pool; - const char *msg; - ngx_int_t rc; - ngx_uint_t n; - ngx_int_t i; - int nargs; - int *cap = NULL; - int ovecsize; - int type; - unsigned func; - int offset; - int cp_offset; - size_t count; - luaL_Buffer luabuf; - ngx_int_t flags; - u_char *p; - u_char errstr[NGX_MAX_CONF_ERRSTR + 1]; - pcre_extra *sd = NULL; - int name_entry_size = 0, name_count; - u_char *name_table = NULL; - int exec_opts; - - ngx_stream_lua_main_conf_t *lmcf; - ngx_stream_lua_regex_t *re; - ngx_stream_lua_regex_compile_t re_comp; - ngx_stream_lua_complex_value_t *ctpl = NULL; - ngx_stream_lua_compile_complex_value_t ccv; - - nargs = lua_gettop(L); - - if (nargs != 3 && nargs != 4) { - return luaL_error(L, "expecting three or four arguments, but got %d", - nargs); - } - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len); - pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); - - func = 0; - - type = lua_type(L, 3); - switch (type) { - case LUA_TFUNCTION: - func = 1; - tpl.len = 0; - tpl.data = (u_char *) ""; - break; - - case LUA_TNUMBER: - case LUA_TSTRING: - tpl.data = (u_char *) lua_tolstring(L, 3, &tpl.len); - break; - - default: - msg = lua_pushfstring(L, "string, number, or function expected, " - "got %s", lua_typename(L, type)); - return luaL_argerror(L, 3, msg); - } - - ngx_memzero(&re_comp, sizeof(ngx_stream_lua_regex_compile_t)); - - if (nargs == 4) { - opts.data = (u_char *) luaL_checklstring(L, 4, &opts.len); - lua_pop(L, 1); - - } else { /* nargs == 3 */ - opts.data = (u_char *) ""; - opts.len = 0; - } - - /* stack: subj regex repl */ - - re_comp.options = 0; - - flags = ngx_stream_lua_ngx_re_parse_opts(L, &re_comp, &opts, 4); - - lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module); - - if (flags & NGX_LUA_RE_COMPILE_ONCE) { - pool = lmcf->pool; - - dd("server pool %p", lmcf->pool); - - lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( - regex_cache_key)); - lua_rawget(L, LUA_REGISTRYINDEX); /* table */ - - lua_pushliteral(L, "s"); - lua_pushinteger(L, tpl.len); - lua_pushliteral(L, ":"); - lua_pushvalue(L, 2); - - if (tpl.len != 0) { - lua_pushvalue(L, 3); - } - - dd("options size: %d", (int) sizeof(re_comp.options)); - - lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options)); - /* table regex opts */ - - if (tpl.len == 0) { - lua_concat(L, 5); /* table key */ - - } else { - lua_concat(L, 6); /* table key */ - } - - lua_pushvalue(L, -1); /* table key key */ - - dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)), - lua_tostring(L, -1)); - - lua_rawget(L, -3); /* table key re */ - re = lua_touserdata(L, -1); - - lua_pop(L, 1); /* table key */ - - if (re) { - ngx_log_debug3(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua regex cache hit for sub regex \"%s\" with " - "options \"%s\" and replace \"%s\"", - pat.data, opts.data, - func ? (u_char *) "" : tpl.data); - - lua_pop(L, 2); - - dd("restoring regex %p, ncaptures %d, captures %p", re->regex, - re->ncaptures, re->captures); - - re_comp.regex = re->regex; - sd = re->regex_sd; - re_comp.captures = re->ncaptures; - cap = re->captures; - ctpl = re->replace; - - if (flags & NGX_LUA_RE_MODE_DFA) { - ovecsize = 2; - - } else { - ovecsize = (re->ncaptures + 1) * 3; - } - - goto exec; - } - - ngx_log_debug4(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua regex cache miss for %ssub regex \"%s\" with " - "options \"%s\" and replace \"%s\"", - global ? "g" : "", pat.data, opts.data, - func ? (u_char *) "" : tpl.data); - - if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) { - - if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) { - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, - "lua exceeding regex cache max entries (%i)", - lmcf->regex_cache_max_entries); - - lmcf->regex_cache_entries++; - } - - pool = r->pool; - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - } - - } else { - pool = r->pool; - } - - re_comp.pattern = pat; - re_comp.err.len = NGX_MAX_CONF_ERRSTR; - re_comp.err.data = errstr; - re_comp.pool = pool; - - dd("compiling regex"); - - ngx_log_debug6(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua compiling %ssub regex \"%s\" with options \"%s\" " - "(compile once: %d) (dfa mode: %d) (jit mode: %d)", - global ? "g" : "", pat.data, opts.data, - (flags & NGX_LUA_RE_COMPILE_ONCE) != 0, - (flags & NGX_LUA_RE_MODE_DFA) != 0, - (flags & NGX_LUA_RE_MODE_JIT) != 0); - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - rc = ngx_stream_lua_regex_compile(&re_comp); - - ngx_stream_lua_pcre_malloc_done(old_pool); - - if (rc != NGX_OK) { - dd("compile failed"); - - lua_pushnil(L); - lua_pushnil(L); - lua_pushlstring(L, (char *) re_comp.err.data, re_comp.err.len); - return 3; - } - -#if LUA_HAVE_PCRE_JIT - - if (flags & NGX_LUA_RE_MODE_JIT) { - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); - - ngx_stream_lua_pcre_malloc_done(old_pool); - -# if (NGX_DEBUG) - dd("sd = %p", sd); - - if (msg != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre study failed with PCRE_STUDY_JIT_COMPILE: " - "%s (%p)", msg, sd); - } - - if (sd != NULL) { - int jitted; - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); - - ngx_stream_lua_pcre_malloc_done(old_pool); - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre JIT compiling result: %d", jitted); - } -# endif /* NGX_DEBUG */ - - } else { - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - sd = pcre_study(re_comp.regex, 0, &msg); - - ngx_stream_lua_pcre_malloc_done(old_pool); - -# if (NGX_DEBUG) - dd("sd = %p", sd); - - if (msg != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "pcre_study failed with PCRE_STUDY_JIT_COMPILE: " - "%s (%p)", msg, sd); - } -# endif /* NGX_DEBUG */ - } - -#else /* LUA_HAVE_PCRE_JIT */ - - if (flags & NGX_LUA_RE_MODE_JIT) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "your pcre build does not have JIT support and " - "the \"j\" regex option is ignored"); - } - -#endif /* LUA_HAVE_PCRE_JIT */ - - if (sd && lmcf->regex_match_limit > 0) { - sd->flags |= PCRE_EXTRA_MATCH_LIMIT; - sd->match_limit = lmcf->regex_match_limit; - } - - dd("compile done, captures %d", re_comp.captures); - - if (flags & NGX_LUA_RE_MODE_DFA) { - ovecsize = 2; - re_comp.captures = 0; - - } else { - ovecsize = (re_comp.captures + 1) * 3; - } - - cap = ngx_palloc(pool, ovecsize * sizeof(int)); - if (cap == NULL) { - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - msg = "no memory"; - goto error; - } - - if (func) { - ctpl = NULL; - - } else { - ctpl = ngx_palloc(pool, sizeof(ngx_stream_lua_complex_value_t)); - if (ctpl == NULL) { - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - msg = "no memory"; - goto error; - } - - if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) { - /* copy the string buffer pointed to by tpl.data from Lua VM */ - p = ngx_palloc(pool, tpl.len + 1); - if (p == NULL) { - flags &= ~NGX_LUA_RE_COMPILE_ONCE; - msg = "no memory"; - goto error; - } - - ngx_memcpy(p, tpl.data, tpl.len); - p[tpl.len] = '\0'; - - tpl.data = p; - } - - ngx_memzero(&ccv, sizeof(ngx_stream_lua_compile_complex_value_t)); - ccv.pool = pool; - ccv.log = r->connection->log; - ccv.value = &tpl; - ccv.complex_value = ctpl; - - if (ngx_stream_lua_compile_complex_value(&ccv) != NGX_OK) { - ngx_pfree(pool, cap); - ngx_pfree(pool, ctpl); - - if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) { - ngx_pfree(pool, tpl.data); - } - - if (sd) { - ngx_stream_lua_regex_free_study_data(pool, sd); - } - - ngx_pfree(pool, re_comp.regex); - - lua_pushnil(L); - lua_pushnil(L); - lua_pushliteral(L, "failed to compile the replacement template"); - return 3; - } - } - - if (flags & NGX_LUA_RE_COMPILE_ONCE) { - - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua saving compiled sub regex (%d captures) into " - "the cache (entries %i)", re_comp.captures, - lmcf->regex_cache_entries); - - re = ngx_palloc(pool, sizeof(ngx_stream_lua_regex_t)); - if (re == NULL) { - msg = "no memory"; - goto error; - } - - dd("saving regex %p, ncaptures %d, captures %p", re_comp.regex, - re_comp.captures, cap); - - re->regex = re_comp.regex; - re->regex_sd = sd; - re->ncaptures = re_comp.captures; - re->captures = cap; - re->replace = ctpl; - - lua_pushlightuserdata(L, re); /* table key value */ - lua_rawset(L, -3); /* table */ - lua_pop(L, 1); - - if (lmcf) { - lmcf->regex_cache_entries++; - } - } - -exec: - - count = 0; - offset = 0; - cp_offset = 0; - - if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT, - &name_count) != 0) - { - msg = "cannot acquire named subpattern count"; - goto error; - } - - if (name_count > 0) { - if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE, - &name_entry_size) != 0) - { - msg = "cannot acquire named subpattern entry size"; - goto error; - } - - if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE, - &name_table) != 0) - { - msg = "cannot acquire named subpattern table"; - goto error; - } - } - - if (flags & NGX_LUA_RE_NO_UTF8_CHECK) { - exec_opts = PCRE_NO_UTF8_CHECK; - - } else { - exec_opts = 0; - } - - for (;;) { - if (flags & NGX_LUA_RE_MODE_DFA) { - -#if LUA_HAVE_PCRE_DFA - - int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; - rc = ngx_stream_lua_regex_dfa_exec(re_comp.regex, sd, &subj, - offset, cap, ovecsize, ws, - sizeof(ws)/sizeof(ws[0]), - exec_opts); - -#else /* LUA_HAVE_PCRE_DFA */ - - msg = "at least pcre 6.0 is required for the DFA mode"; - goto error; - -#endif /* LUA_HAVE_PCRE_DFA */ - - } else { - rc = ngx_stream_lua_regex_exec(re_comp.regex, sd, &subj, - offset, cap, ovecsize, - exec_opts); - } - - if (rc == NGX_REGEX_NO_MATCHED) { - break; - } - - if (rc < 0) { - msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d", - (int) rc); - goto error; - } - - if (rc == 0) { - if (flags & NGX_LUA_RE_MODE_DFA) { - rc = 1; - - } else { - msg = "capture size too small"; - goto error; - } - } - - dd("rc = %d", (int) rc); - - count++; - - if (count == 1) { - luaL_buffinit(L, &luabuf); - } - - if (func) { - lua_pushvalue(L, 3); - - lua_createtable(L, re_comp.captures || 1 /* narr */, - name_count /* nrec */); - - for (i = 0, n = 0; i <= re_comp.captures; i++, n += 2) { - dd("capture %d: %d %d", (int) i, cap[n], cap[n + 1]); - if (i >= rc || cap[n] < 0) { - lua_pushboolean(L, 0); - - } else { - lua_pushlstring(L, (char *) &subj.data[cap[n]], - cap[n + 1] - cap[n]); - - dd("pushing capture %s at %d", lua_tostring(L, -1), - (int) i); - } - - lua_rawseti(L, -2, (int) i); - } - - if (name_count > 0) { - ngx_stream_lua_re_collect_named_captures(L, lua_gettop(L), - name_table, - name_count, - name_entry_size, - flags, &subj); - } - - dd("stack size at call: %d", lua_gettop(L)); - - lua_call(L, 1 /* nargs */, 1 /* nresults */); - type = lua_type(L, -1); - switch (type) { - case LUA_TNUMBER: - case LUA_TSTRING: - tpl.data = (u_char *) lua_tolstring(L, -1, &tpl.len); - break; - - default: - msg = lua_pushfstring(L, "string or number expected to be " - "returned by the replace " - "function, got %s", - lua_typename(L, type)); - return luaL_argerror(L, 3, msg); - } - - lua_insert(L, 1); - - luaL_addlstring(&luabuf, (char *) &subj.data[cp_offset], - cap[0] - cp_offset); - - luaL_addlstring(&luabuf, (char *) tpl.data, tpl.len); - - lua_remove(L, 1); - - cp_offset = cap[1]; - offset = cp_offset; - if (offset == cap[0]) { - offset++; - if (offset > (ssize_t) subj.len) { - break; - } - } - - if (global) { - continue; - } - - break; - } - - rc = ngx_stream_lua_complex_value(r, &subj, cp_offset, rc, cap, ctpl, - &luabuf); - - if (rc != NGX_OK) { - msg = lua_pushfstring(L, "failed to eval the template for " - "replacement: \"%s\"", tpl.data); - goto error; - } - - cp_offset = cap[1]; - offset = cp_offset; - if (offset == cap[0]) { - offset++; - if (offset > (ssize_t) subj.len) { - break; - } - } - - if (global) { - continue; - } - - break; - } - - if (count == 0) { - dd("no match, just the original subject"); - lua_settop(L, 1); - - } else { - if (offset < (int) subj.len) { - dd("adding trailer: %s (len %d)", &subj.data[cp_offset], - (int) (subj.len - cp_offset)); - - - luaL_addlstring(&luabuf, (char *) &subj.data[cp_offset], - subj.len - cp_offset); - } - - luaL_pushresult(&luabuf); - - dd("the dst string: %s", lua_tostring(L, -1)); - } - - if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (sd) { - ngx_stream_lua_regex_free_study_data(pool, sd); - } - - if (re_comp.regex) { - ngx_pfree(pool, re_comp.regex); - } - - if (ctpl) { - ngx_pfree(pool, ctpl); - } - - if (cap) { - ngx_pfree(pool, cap); - } - } - - lua_pushinteger(L, count); - return 2; - -error: - - if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { - if (sd) { - ngx_stream_lua_regex_free_study_data(pool, sd); - } - - if (re_comp.regex) { - ngx_pfree(pool, re_comp.regex); - } - - if (ctpl) { - ngx_pfree(pool, ctpl); - } - - if (cap) { - ngx_pfree(pool, cap); - } - } - - lua_pushnil(L); - lua_pushnil(L); - lua_pushstring(L, msg); - return 3; -} - - -ngx_int_t -ngx_stream_lua_ffi_set_jit_stack_size(int size, u_char *errstr, - size_t *errstr_size) -{ -#if LUA_HAVE_PCRE_JIT - - ngx_stream_lua_main_conf_t *lmcf; - ngx_pool_t *pool, *old_pool; - - lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, - ngx_stream_lua_module); - - 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_stream_lua_pcre_malloc_init(pool); - - pcre_jit_stack_free(lmcf->jit_stack); - - ngx_stream_lua_pcre_malloc_done(old_pool); - } - - old_pool = ngx_stream_lua_pcre_malloc_init(pool); - - lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE, - size); - - ngx_stream_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 */ -} - - -void -ngx_stream_lua_inject_regex_api(lua_State *L) -{ - /* ngx.re */ - - lua_createtable(L, 0, 5 /* nrec */); /* .re */ - - lua_pushcfunction(L, ngx_stream_lua_ngx_re_find); - lua_setfield(L, -2, "find"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_re_match); - lua_setfield(L, -2, "match"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_re_gmatch); - lua_setfield(L, -2, "gmatch"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_re_sub); - lua_setfield(L, -2, "sub"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_re_gsub); - lua_setfield(L, -2, "gsub"); - - lua_setfield(L, -2, "re"); -} - - static void ngx_stream_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd) { @@ -2086,104 +172,57 @@ ngx_stream_lua_regex_compile(ngx_stream_lua_regex_compile_t *rc) } -static void -ngx_stream_lua_ngx_re_gmatch_cleanup(void *data) +ngx_int_t +ngx_stream_lua_ffi_set_jit_stack_size(int size, u_char *errstr, + size_t *errstr_size) { - ngx_stream_lua_regex_ctx_t *ctx = data; +#if LUA_HAVE_PCRE_JIT - if (ctx) { - if (ctx->regex_sd) { - ngx_stream_lua_regex_free_study_data(ctx->request->pool, - ctx->regex_sd); - ctx->regex_sd = NULL; - } + ngx_stream_lua_main_conf_t *lmcf; + ngx_pool_t *pool, *old_pool; - if (ctx->cleanup) { - *ctx->cleanup = NULL; - ctx->cleanup = NULL; - } + lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, + ngx_stream_lua_module); - ctx->request = NULL; + if (size < NGX_LUA_RE_MIN_JIT_STACK_SIZE) { + size = NGX_LUA_RE_MIN_JIT_STACK_SIZE; } - return; -} + pool = lmcf->pool; + dd("server pool %p", lmcf->pool); -static int -ngx_stream_lua_ngx_re_gmatch_gc(lua_State *L) -{ - ngx_stream_lua_regex_ctx_t *ctx; + if (lmcf->jit_stack) { + old_pool = ngx_stream_lua_pcre_malloc_init(pool); - ctx = lua_touserdata(L, 1); + pcre_jit_stack_free(lmcf->jit_stack); - if (ctx && ctx->cleanup) { - ngx_stream_lua_ngx_re_gmatch_cleanup(ctx); + ngx_stream_lua_pcre_malloc_done(old_pool); } - return 0; -} - - -static void -ngx_stream_lua_re_collect_named_captures(lua_State *L, int res_tb_idx, - u_char *name_table, int name_count, int name_entry_size, unsigned flags, - ngx_str_t *subj) -{ - int i, n; - size_t len; - u_char *name_entry; - char *name; - - for (i = 0; i < name_count; i++) { - dd("top: %d", lua_gettop(L)); - - name_entry = &name_table[i * name_entry_size]; - n = (name_entry[0] << 8) | name_entry[1]; - name = (char *) &name_entry[2]; - - lua_rawgeti(L, -1, n); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - continue; - } - - if (flags & NGX_LUA_RE_MODE_DUPNAMES) { - /* unmatched groups are not stored in tables in DUPNAMES mode */ - if (!lua_toboolean(L, -1)) { - lua_pop(L, 1); - continue; - } + old_pool = ngx_stream_lua_pcre_malloc_init(pool); - lua_getfield(L, -2, name); /* big_tb cap small_tb */ + lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE, + size); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); + ngx_stream_lua_pcre_malloc_done(old_pool); - /* assuming named submatches are usually unique */ - lua_createtable(L, 1 /* narr */, 0 /* nrec */); - lua_pushstring(L, name); - lua_pushvalue(L, -2); /* big_tb cap small_tb key small_tb */ - lua_rawset(L, res_tb_idx); /* big_tb cap small_tb */ - len = 0; + if (lmcf->jit_stack == NULL) { + *errstr_size = ngx_snprintf(errstr, *errstr_size, + "pcre jit stack allocation failed") + - errstr; + return NGX_ERROR; + } - } else { - len = lua_objlen(L, -1); - } + return NGX_OK; - lua_pushvalue(L, -2); /* big_tb cap small_tb cap */ - lua_rawseti(L, -2, (int) len + 1); /* big_tb cap small_tb */ - lua_pop(L, 2); +#else /* LUA_HAVE_PCRE_JIT */ - } else { - lua_pushstring(L, name); /* big_tb cap key */ - lua_pushvalue(L, -2); /* big_tb cap key cap */ - lua_rawset(L, res_tb_idx); /* big_tb cap */ - lua_pop(L, 1); - } + *errstr_size = ngx_snprintf(errstr, *errstr_size, + "no pcre jit support found") - errstr; + return NGX_ERROR; - dd("top 2: %d", lua_gettop(L)); - } +#endif /* LUA_HAVE_PCRE_JIT */ } @@ -2555,4 +594,5 @@ ngx_stream_lua_ffi_max_regex_cache_size(void) #endif /* NGX_PCRE */ + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_regex.h b/src/ngx_stream_lua_regex.h deleted file mode 100644 index 707a0679..00000000 --- a/src/ngx_stream_lua_regex.h +++ /dev/null @@ -1,32 +0,0 @@ - -/* - * !!! DO NOT EDIT DIRECTLY !!! - * This file was automatically generated from the following template: - * - * src/subsys/ngx_subsys_lua_regex.h.tt2 - */ - - -/* - * Copyright (C) Yichun Zhang (agentzh) - */ - - -#ifndef _NGX_STREAM_LUA_REGEX_H_INCLUDED_ -#define _NGX_STREAM_LUA_REGEX_H_INCLUDED_ - - -#include "ngx_stream_lua_common.h" -#include "ngx_stream_lua_script.h" - - -#if (NGX_PCRE) -void ngx_stream_lua_inject_regex_api(lua_State *L); -ngx_int_t ngx_stream_lua_ffi_set_jit_stack_size(int size, u_char *errstr, - size_t *errstr_size); -#endif - - -#endif /* _NGX_STREAM_LUA_REGEX_H_INCLUDED_ */ - -/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_semaphore.c b/src/ngx_stream_lua_semaphore.c index c9b1ecd8..70084a2a 100644 --- a/src/ngx_stream_lua_semaphore.c +++ b/src/ngx_stream_lua_semaphore.c @@ -15,8 +15,6 @@ */ - - #ifndef DDEBUG #define DDEBUG 0 #endif @@ -93,6 +91,8 @@ ngx_stream_lua_alloc_sema(void) lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle, ngx_stream_lua_module); + ngx_stream_lua_assert(lmcf != NULL); + mm = lmcf->sema_mm; if (!ngx_queue_empty(&mm->free_queue)) { @@ -179,6 +179,8 @@ ngx_stream_lua_sema_mm_cleanup(void *data) sem = ngx_queue_data(q, ngx_stream_lua_sema_t, chain); block = sem->block; + ngx_stream_lua_assert(block != NULL); + if (block->used == 0) { iter = (ngx_stream_lua_sema_t *) (block + 1); @@ -372,7 +374,7 @@ ngx_stream_lua_ffi_sema_wait(ngx_stream_lua_request_t *r, "stream lua semaphore wait: %p, timeout: %d, " "resources: %d, event posted: %d", sem, wait_ms, sem->resource_count, -#if (nginx_version >= 1007005) +#if defined(nginx_version) && nginx_version >= 1007005 (int) sem->sem_event.posted #else sem->sem_event.prev ? 1 : 0 @@ -573,5 +575,4 @@ ngx_stream_lua_ffi_sema_gc(ngx_stream_lua_sema_t *sem) } - /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_shdict.c b/src/ngx_stream_lua_shdict.c index e1029561..6b137f55 100644 --- a/src/ngx_stream_lua_shdict.c +++ b/src/ngx_stream_lua_shdict.c @@ -23,23 +23,11 @@ #include "ngx_stream_lua_api.h" -static int ngx_stream_lua_shdict_set(lua_State *L); -static int ngx_stream_lua_shdict_safe_set(lua_State *L); -static int ngx_stream_lua_shdict_get(lua_State *L); -static int ngx_stream_lua_shdict_get_stale(lua_State *L); -static int ngx_stream_lua_shdict_get_helper(lua_State *L, int get_stale); static int ngx_stream_lua_shdict_expire(ngx_stream_lua_shdict_ctx_t *ctx, ngx_uint_t n); static ngx_int_t ngx_stream_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, u_char *kdata, size_t klen, ngx_stream_lua_shdict_node_t **sdp); -static int ngx_stream_lua_shdict_set_helper(lua_State *L, int flags); -static int ngx_stream_lua_shdict_add(lua_State *L); -static int ngx_stream_lua_shdict_safe_add(lua_State *L); -static int ngx_stream_lua_shdict_replace(lua_State *L); -static int ngx_stream_lua_shdict_incr(lua_State *L); -static int ngx_stream_lua_shdict_delete(lua_State *L); -static int ngx_stream_lua_shdict_flush_all(lua_State *L); static int ngx_stream_lua_shdict_flush_expired(lua_State *L); static int ngx_stream_lua_shdict_get_keys(lua_State *L); static int ngx_stream_lua_shdict_lpush(lua_State *L); @@ -137,9 +125,7 @@ ngx_stream_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data) ngx_sprintf(ctx->shpool->log_ctx, " in lua_shared_dict zone \"%V\"%Z", &shm_zone->shm.name); -#if defined(nginx_version) && nginx_version >= 1005013 ctx->shpool->log_nomem = 0; -#endif return NGX_OK; } @@ -346,34 +332,7 @@ ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf, lua_State *L) lua_createtable(L, 0, lmcf->shdict_zones->nelts /* nrec */); /* ngx.shared */ - lua_createtable(L, 0 /* narr */, 18 /* nrec */); /* shared mt */ - - lua_pushcfunction(L, ngx_stream_lua_shdict_get); - lua_setfield(L, -2, "get"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_get_stale); - lua_setfield(L, -2, "get_stale"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_set); - lua_setfield(L, -2, "set"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_safe_set); - lua_setfield(L, -2, "safe_set"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_add); - lua_setfield(L, -2, "add"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_safe_add); - lua_setfield(L, -2, "safe_add"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_replace); - lua_setfield(L, -2, "replace"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_incr); - lua_setfield(L, -2, "incr"); - - lua_pushcfunction(L, ngx_stream_lua_shdict_delete); - lua_setfield(L, -2, "delete"); + lua_createtable(L, 0 /* narr */, 22 /* nrec */); /* shared mt */ lua_pushcfunction(L, ngx_stream_lua_shdict_lpush); lua_setfield(L, -2, "lpush"); @@ -390,9 +349,6 @@ ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf, lua_State *L) lua_pushcfunction(L, ngx_stream_lua_shdict_llen); lua_setfield(L, -2, "llen"); - lua_pushcfunction(L, ngx_stream_lua_shdict_flush_all); - lua_setfield(L, -2, "flush_all"); - lua_pushcfunction(L, ngx_stream_lua_shdict_flush_expired); lua_setfield(L, -2, "flush_expired"); @@ -404,1236 +360,249 @@ ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf, lua_State *L) zone = lmcf->shdict_zones->elts; - for (i = 0; i < lmcf->shdict_zones->nelts; i++) { - ctx = zone[i]->data; - - lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); - /* shared mt key */ - - lua_createtable(L, 1 /* narr */, 0 /* nrec */); - /* table of zone[i] */ - zone_udata = lua_newuserdata(L, sizeof(ngx_shm_zone_t *)); - /* shared mt key ud */ - *zone_udata = zone[i]; - lua_rawseti(L, -2, SHDICT_USERDATA_INDEX); /* {zone[i]} */ - lua_pushvalue(L, -3); /* shared mt key ud mt */ - lua_setmetatable(L, -2); /* shared mt key ud */ - lua_rawset(L, -4); /* shared mt */ - } - - lua_pop(L, 1); /* shared */ - - } else { - lua_newtable(L); /* ngx.shared */ - } - - lua_setfield(L, -2, "shared"); -} - - -static int -ngx_stream_lua_shdict_get(lua_State *L) -{ - return ngx_stream_lua_shdict_get_helper(L, 0 /* stale */); -} - - -static int -ngx_stream_lua_shdict_get_stale(lua_State *L) -{ - return ngx_stream_lua_shdict_get_helper(L, 1 /* stale */); -} - - -static ngx_inline ngx_shm_zone_t * -ngx_stream_lua_shdict_get_zone(lua_State *L, int index) -{ - ngx_shm_zone_t *zone; - ngx_shm_zone_t **zone_udata; - - lua_rawgeti(L, index, SHDICT_USERDATA_INDEX); - zone_udata = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (zone_udata == NULL) { - return NULL; - } - - zone = *zone_udata; - return zone; -} - - -static int -ngx_stream_lua_shdict_get_helper(lua_State *L, int get_stale) -{ - int n; - ngx_str_t name; - ngx_str_t key; - uint32_t hash; - ngx_int_t rc; - ngx_str_t value; - int value_type; - double num; - u_char c; - ngx_shm_zone_t *zone; - uint32_t user_flags = 0; - - ngx_stream_lua_shdict_ctx_t *ctx; - ngx_stream_lua_shdict_node_t *sd; - - n = lua_gettop(L); - - if (n != 2) { - return luaL_error(L, "expecting exactly two arguments, " - "but only seen %d", n); - } - - if (lua_type(L, 1) != LUA_TTABLE) { - return luaL_error(L, "bad \"zone\" argument"); - } - - zone = ngx_stream_lua_shdict_get_zone(L, 1); - if (zone == NULL) { - return luaL_error(L, "bad \"zone\" argument"); - } - - ctx = zone->data; - name = ctx->name; - - if (lua_isnil(L, 2)) { - lua_pushnil(L); - lua_pushliteral(L, "nil key"); - return 2; - } - - key.data = (u_char *) luaL_checklstring(L, 2, &key.len); - - if (key.len == 0) { - lua_pushnil(L); - lua_pushliteral(L, "empty key"); - return 2; - } - - if (key.len > 65535) { - lua_pushnil(L); - lua_pushliteral(L, "key too long"); - return 2; - } - - hash = ngx_crc32_short(key.data, key.len); - -#if (NGX_DEBUG) - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "fetching key \"%V\" in shared dict \"%V\"", &key, &name); -#endif /* NGX_DEBUG */ - - ngx_shmtx_lock(&ctx->shpool->mutex); - -#if 1 - if (!get_stale) { - ngx_stream_lua_shdict_expire(ctx, 1); - } -#endif - - rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); - - dd("shdict lookup returns %d", (int) rc); - - if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - lua_pushnil(L); - return 1; - } - - /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */ - - value_type = sd->value_type; - - dd("data: %p", sd->data); - dd("key len: %d", (int) sd->key_len); - - value.data = sd->data + sd->key_len; - value.len = (size_t) sd->value_len; - - switch (value_type) { - - case SHDICT_TSTRING: - - lua_pushlstring(L, (char *) value.data, value.len); - break; - - case SHDICT_TNUMBER: - - if (value.len != sizeof(double)) { - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - return luaL_error(L, "bad lua number value size found for key %s " - "in shared_dict %s: %lu", key.data, name.data, - (unsigned long) value.len); - } - - ngx_memcpy(&num, value.data, sizeof(double)); - - lua_pushnumber(L, num); - break; - - case SHDICT_TBOOLEAN: - - if (value.len != sizeof(u_char)) { - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - return luaL_error(L, "bad lua boolean value size found for key %s " - "in shared_dict %s: %lu", key.data, name.data, - (unsigned long) value.len); - } - - c = *value.data; - - lua_pushboolean(L, c ? 1 : 0); - break; - - case SHDICT_TLIST: - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushnil(L); - lua_pushliteral(L, "value is a list"); - return 2; - - default: - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - return luaL_error(L, "bad value type found for key %s in " - "shared_dict %s: %d", key.data, name.data, - value_type); - } - - user_flags = sd->user_flags; - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - if (get_stale) { - - /* always return value, flags, stale */ - - if (user_flags) { - lua_pushinteger(L, (lua_Integer) user_flags); - - } else { - lua_pushnil(L); - } - - lua_pushboolean(L, rc == NGX_DONE); - return 3; - } - - if (user_flags) { - lua_pushinteger(L, (lua_Integer) user_flags); - return 2; - } - - return 1; -} - - -static int -ngx_stream_lua_shdict_delete(lua_State *L) -{ - int n; - - n = lua_gettop(L); - - if (n != 2) { - return luaL_error(L, "expecting 2 arguments, " - "but only seen %d", n); - } - - lua_pushnil(L); - - return ngx_stream_lua_shdict_set_helper(L, 0); -} - - -static int -ngx_stream_lua_shdict_flush_all(lua_State *L) -{ - ngx_queue_t *q; - int n; - ngx_shm_zone_t *zone; - - ngx_stream_lua_shdict_node_t *sd; - ngx_stream_lua_shdict_ctx_t *ctx; - - n = lua_gettop(L); - - if (n != 1) { - return luaL_error(L, "expecting 1 argument, but seen %d", n); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - zone = ngx_stream_lua_shdict_get_zone(L, 1); - if (zone == NULL) { - return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); - } - - ctx = zone->data; - - ngx_shmtx_lock(&ctx->shpool->mutex); - - for (q = ngx_queue_head(&ctx->sh->lru_queue); - q != ngx_queue_sentinel(&ctx->sh->lru_queue); - q = ngx_queue_next(q)) - { - sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); - sd->expires = 1; - } - - ngx_stream_lua_shdict_expire(ctx, 0); - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - return 0; -} - - -static int -ngx_stream_lua_shdict_flush_expired(lua_State *L) -{ - ngx_queue_t *q, *prev, *list_queue, *lq; - ngx_shm_zone_t *zone; - ngx_time_t *tp; - int freed = 0; - int attempts = 0; - ngx_rbtree_node_t *node; - uint64_t now; - int n; - - ngx_stream_lua_shdict_node_t *sd; - ngx_stream_lua_shdict_ctx_t *ctx; - ngx_stream_lua_shdict_list_node_t *lnode; - - n = lua_gettop(L); - - if (n != 1 && n != 2) { - return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - zone = ngx_stream_lua_shdict_get_zone(L, 1); - if (zone == NULL) { - return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); - } - - if (n == 2) { - attempts = luaL_checkint(L, 2); - } - - ctx = zone->data; - - ngx_shmtx_lock(&ctx->shpool->mutex); - - if (ngx_queue_empty(&ctx->sh->lru_queue)) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - lua_pushnumber(L, 0); - return 1; - } - - tp = ngx_timeofday(); - - now = (uint64_t) tp->sec * 1000 + tp->msec; - - q = ngx_queue_last(&ctx->sh->lru_queue); - - while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { - prev = ngx_queue_prev(q); - - sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); - - if (sd->expires != 0 && sd->expires <= now) { - - if (sd->value_type == SHDICT_TLIST) { - list_queue = ngx_stream_lua_shdict_get_list_head(sd, - sd->key_len); - - for (lq = ngx_queue_head(list_queue); - lq != ngx_queue_sentinel(list_queue); - lq = ngx_queue_next(lq)) - { - lnode = ngx_queue_data(lq, - ngx_stream_lua_shdict_list_node_t, - queue); - - ngx_slab_free_locked(ctx->shpool, lnode); - } - } - - ngx_queue_remove(q); - - node = (ngx_rbtree_node_t *) - ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); - - ngx_rbtree_delete(&ctx->sh->rbtree, node); - ngx_slab_free_locked(ctx->shpool, node); - freed++; - - if (attempts && freed == attempts) { - break; - } - } - - q = prev; - } - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushnumber(L, freed); - return 1; -} - - -/* - * This trades CPU for memory. This is potentially slow. O(2n) - */ - -static int -ngx_stream_lua_shdict_get_keys(lua_State *L) -{ - ngx_queue_t *q, *prev; - ngx_shm_zone_t *zone; - ngx_time_t *tp; - int total = 0; - int attempts = 1024; - uint64_t now; - int n; - - ngx_stream_lua_shdict_node_t *sd; - ngx_stream_lua_shdict_ctx_t *ctx; - - n = lua_gettop(L); - - if (n != 1 && n != 2) { - return luaL_error(L, "expecting 1 or 2 argument(s), " - "but saw %d", n); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - zone = ngx_stream_lua_shdict_get_zone(L, 1); - if (zone == NULL) { - return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); - } - - if (n == 2) { - attempts = luaL_checkint(L, 2); - } - - ctx = zone->data; - - ngx_shmtx_lock(&ctx->shpool->mutex); - - if (ngx_queue_empty(&ctx->sh->lru_queue)) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - lua_createtable(L, 0, 0); - return 1; - } - - tp = ngx_timeofday(); - - now = (uint64_t) tp->sec * 1000 + tp->msec; - - /* first run through: get total number of elements we need to allocate */ - - q = ngx_queue_last(&ctx->sh->lru_queue); - - while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { - prev = ngx_queue_prev(q); - - sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); - - if (sd->expires == 0 || sd->expires > now) { - total++; - if (attempts && total == attempts) { - break; - } - } - - q = prev; - } - - lua_createtable(L, total, 0); - - /* second run through: add keys to table */ - - total = 0; - q = ngx_queue_last(&ctx->sh->lru_queue); - - while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { - prev = ngx_queue_prev(q); - - sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); - - if (sd->expires == 0 || sd->expires > now) { - lua_pushlstring(L, (char *) sd->data, sd->key_len); - lua_rawseti(L, -2, ++total); - if (attempts && total == attempts) { - break; - } - } - - q = prev; - } - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - /* table is at top of stack */ - return 1; -} - - -static int -ngx_stream_lua_shdict_add(lua_State *L) -{ - return ngx_stream_lua_shdict_set_helper(L, NGX_STREAM_LUA_SHDICT_ADD); -} - - -static int -ngx_stream_lua_shdict_safe_add(lua_State *L) -{ - return ngx_stream_lua_shdict_set_helper(L, - NGX_STREAM_LUA_SHDICT_ADD - |NGX_STREAM_LUA_SHDICT_SAFE_STORE); -} - - -static int -ngx_stream_lua_shdict_replace(lua_State *L) -{ - return ngx_stream_lua_shdict_set_helper(L, NGX_STREAM_LUA_SHDICT_REPLACE); -} - - -static int -ngx_stream_lua_shdict_set(lua_State *L) -{ - return ngx_stream_lua_shdict_set_helper(L, 0); -} - - -static int -ngx_stream_lua_shdict_safe_set(lua_State *L) -{ - return ngx_stream_lua_shdict_set_helper(L, - NGX_STREAM_LUA_SHDICT_SAFE_STORE); -} - - -static int -ngx_stream_lua_shdict_set_helper(lua_State *L, int flags) -{ - int i, n; - ngx_str_t key; - uint32_t hash; - ngx_int_t rc; - ngx_str_t value; - int value_type; - double num; - u_char c; - lua_Number exptime = 0; - u_char *p; - ngx_rbtree_node_t *node; - ngx_time_t *tp; - ngx_shm_zone_t *zone; - int forcible = 0; - /* indicates whether to foricibly override other - * valid entries */ - int32_t user_flags = 0; - ngx_queue_t *queue, *q; - - ngx_stream_lua_shdict_ctx_t *ctx; - ngx_stream_lua_shdict_node_t *sd; - - n = lua_gettop(L); - - if (n != 3 && n != 4 && n != 5) { - return luaL_error(L, "expecting 3, 4 or 5 arguments, " - "but only seen %d", n); - } - - if (lua_type(L, 1) != LUA_TTABLE) { - return luaL_error(L, "bad \"zone\" argument"); - } - - zone = ngx_stream_lua_shdict_get_zone(L, 1); - if (zone == NULL) { - return luaL_error(L, "bad \"zone\" argument"); - } - - ctx = zone->data; - - if (lua_isnil(L, 2)) { - lua_pushnil(L); - lua_pushliteral(L, "nil key"); - return 2; - } - - key.data = (u_char *) luaL_checklstring(L, 2, &key.len); - - if (key.len == 0) { - lua_pushnil(L); - lua_pushliteral(L, "empty key"); - return 2; - } - - if (key.len > 65535) { - lua_pushnil(L); - lua_pushliteral(L, "key too long"); - return 2; - } - - hash = ngx_crc32_short(key.data, key.len); - - value_type = lua_type(L, 3); - - switch (value_type) { - - case SHDICT_TSTRING: - value.data = (u_char *) lua_tolstring(L, 3, &value.len); - break; - - case SHDICT_TNUMBER: - value.len = sizeof(double); - num = lua_tonumber(L, 3); - value.data = (u_char *) # - break; - - case SHDICT_TBOOLEAN: - value.len = sizeof(u_char); - c = lua_toboolean(L, 3) ? 1 : 0; - value.data = &c; - break; - - case LUA_TNIL: - if (flags & (NGX_STREAM_LUA_SHDICT_ADD|NGX_STREAM_LUA_SHDICT_REPLACE)) { - lua_pushnil(L); - lua_pushliteral(L, "attempt to add or replace nil values"); - return 2; - } - - ngx_str_null(&value); - break; - - default: - lua_pushnil(L); - lua_pushliteral(L, "bad value type"); - return 2; - } - - if (n >= 4) { - exptime = luaL_checknumber(L, 4); - if (exptime < 0) { - return luaL_error(L, "bad \"exptime\" argument"); - } - } - - if (n == 5) { - user_flags = (uint32_t) luaL_checkinteger(L, 5); - } - - ngx_shmtx_lock(&ctx->shpool->mutex); - -#if 1 - ngx_stream_lua_shdict_expire(ctx, 1); -#endif - - rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); - - dd("shdict lookup returned %d", (int) rc); - - if (flags & NGX_STREAM_LUA_SHDICT_REPLACE) { - - if (rc == NGX_DECLINED || rc == NGX_DONE) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 0); - lua_pushliteral(L, "not found"); - lua_pushboolean(L, forcible); - return 3; - } - - /* rc == NGX_OK */ - - goto replace; - } - - if (flags & NGX_STREAM_LUA_SHDICT_ADD) { - - if (rc == NGX_OK) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 0); - lua_pushliteral(L, "exists"); - lua_pushboolean(L, forcible); - return 3; - } - - if (rc == NGX_DONE) { - /* exists but expired */ - - dd("go to replace"); - goto replace; - } - - /* rc == NGX_DECLINED */ - - dd("go to insert"); - goto insert; - } - - if (rc == NGX_OK || rc == NGX_DONE) { - - if (value_type == LUA_TNIL) { - goto remove; - } - -replace: - - if (value.data - && value.len == (size_t) sd->value_len - && sd->value_type != SHDICT_TLIST) - { - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict set: found old entry and value " - "size matched, reusing it"); - - ngx_queue_remove(&sd->queue); - ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); - - sd->key_len = (u_short) key.len; - - if (exptime > 0) { - tp = ngx_timeofday(); - sd->expires = (uint64_t) tp->sec * 1000 + tp->msec - + (uint64_t) (exptime * 1000); - - } else { - sd->expires = 0; - } - - sd->user_flags = user_flags; - - sd->value_len = (uint32_t) value.len; - - dd("setting value type to %d", value_type); - - sd->value_type = (uint8_t) value_type; - - p = ngx_copy(sd->data, key.data, key.len); - ngx_memcpy(p, value.data, value.len); - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 1); - lua_pushnil(L); - lua_pushboolean(L, forcible); - return 3; - } - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict set: found old entry but value size " - "NOT matched, removing it first"); - -remove: - - if (sd->value_type == SHDICT_TLIST) { - queue = ngx_stream_lua_shdict_get_list_head(sd, key.len); - - for (q = ngx_queue_head(queue); - q != ngx_queue_sentinel(queue); - q = ngx_queue_next(q)) - { - p = (u_char *) ngx_queue_data(q, - ngx_stream_lua_shdict_list_node_t, - queue); - - ngx_slab_free_locked(ctx->shpool, p); - } - } - - ngx_queue_remove(&sd->queue); - - node = (ngx_rbtree_node_t *) - ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); - - ngx_rbtree_delete(&ctx->sh->rbtree, node); - - ngx_slab_free_locked(ctx->shpool, node); - - } - -insert: - - /* rc == NGX_DECLINED or value size unmatch */ - - if (value.data == NULL) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 1); - lua_pushnil(L); - lua_pushboolean(L, 0); - return 3; - } - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict set: creating a new entry"); - - n = offsetof(ngx_rbtree_node_t, color) - + offsetof(ngx_stream_lua_shdict_node_t, data) - + key.len - + value.len; - - dd("overhead = %d", (int) (offsetof(ngx_rbtree_node_t, color) - + offsetof(ngx_stream_lua_shdict_node_t, data))); - - node = ngx_slab_alloc_locked(ctx->shpool, n); - - if (node == NULL) { - - if (flags & NGX_STREAM_LUA_SHDICT_SAFE_STORE) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 0); - lua_pushliteral(L, "no memory"); - return 2; - } - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict set: overriding non-expired items " - "due to memory shortage for entry \"%V\"", &key); - - for (i = 0; i < 30; i++) { - if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) { - break; - } - - forcible = 1; - - node = ngx_slab_alloc_locked(ctx->shpool, n); - if (node != NULL) { - goto allocated; - } - } - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 0); - lua_pushliteral(L, "no memory"); - lua_pushboolean(L, forcible); - return 3; - } - -allocated: - - sd = (ngx_stream_lua_shdict_node_t *) &node->color; - - node->key = hash; - sd->key_len = (u_short) key.len; - - if (exptime > 0) { - tp = ngx_timeofday(); - sd->expires = (uint64_t) tp->sec * 1000 + tp->msec - + (uint64_t) (exptime * 1000); - - } else { - sd->expires = 0; - } - - sd->user_flags = user_flags; - - sd->value_len = (uint32_t) value.len; - - dd("setting value type to %d", value_type); - - sd->value_type = (uint8_t) value_type; - - p = ngx_copy(sd->data, key.data, key.len); - ngx_memcpy(p, value.data, value.len); - - ngx_rbtree_insert(&ctx->sh->rbtree, node); - - ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 1); - lua_pushnil(L); - lua_pushboolean(L, forcible); - return 3; -} - - -static int -ngx_stream_lua_shdict_incr(lua_State *L) -{ - int i, n; - ngx_str_t key; - uint32_t hash; - ngx_int_t rc; - double num; - double init = 0; - u_char *p; - ngx_shm_zone_t *zone; - double value; - ngx_rbtree_node_t *node; - /* indicates whether to foricibly override other - * valid entries */ - int forcible = 0; - ngx_queue_t *queue, *q; - - ngx_stream_lua_shdict_ctx_t *ctx; - ngx_stream_lua_shdict_node_t *sd; - - n = lua_gettop(L); - - if (n != 3 && n != 4) { - return luaL_error(L, "expecting 3 or 4 arguments, but only seen %d", n); - } - - if (lua_type(L, 1) != LUA_TTABLE) { - return luaL_error(L, "bad \"zone\" argument"); - } - - zone = ngx_stream_lua_shdict_get_zone(L, 1); - if (zone == NULL) { - return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); - } - - ctx = zone->data; - - if (lua_isnil(L, 2)) { - lua_pushnil(L); - lua_pushliteral(L, "nil key"); - return 2; - } - - key.data = (u_char *) luaL_checklstring(L, 2, &key.len); - - if (key.len == 0) { - lua_pushnil(L); - lua_pushliteral(L, "empty key"); - return 2; - } - - if (key.len > 65535) { - lua_pushnil(L); - lua_pushliteral(L, "key too long"); - return 2; - } - - hash = ngx_crc32_short(key.data, key.len); - - value = luaL_checknumber(L, 3); - - if (n == 4) { - init = luaL_checknumber(L, 4); - } - - dd("looking up key %.*s in shared dict %.*s", (int) key.len, key.data, - (int) ctx->name.len, ctx->name.data); - - ngx_shmtx_lock(&ctx->shpool->mutex); - -#if 1 - ngx_stream_lua_shdict_expire(ctx, 1); -#endif - - rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); - - dd("shdict lookup returned %d", (int) rc); - - if (rc == NGX_DECLINED || rc == NGX_DONE) { - - if (n == 3) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushnil(L); - lua_pushliteral(L, "not found"); - return 2; - } - - /* add value */ - num = value + init; - - if (rc == NGX_DONE) { - - /* found an expired item */ - - if ((size_t) sd->value_len == sizeof(double) - && sd->value_type != SHDICT_TLIST) - { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict incr: found old entry and " - "value size matched, reusing it"); - - ngx_queue_remove(&sd->queue); - ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); - - dd("go to setvalue"); - goto setvalue; - } - - dd("go to remove"); - goto remove; - } - - dd("go to insert"); - goto insert; - } - - /* rc == NGX_OK */ - - if (sd->value_type != SHDICT_TNUMBER || sd->value_len != sizeof(double)) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushnil(L); - lua_pushliteral(L, "not a number"); - return 2; - } - - ngx_queue_remove(&sd->queue); - ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); - - dd("setting value type to %d", (int) sd->value_type); - - p = sd->data + key.len; + for (i = 0; i < lmcf->shdict_zones->nelts; i++) { + ctx = zone[i]->data; - ngx_memcpy(&num, p, sizeof(double)); - num += value; + lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); + /* shared mt key */ - ngx_memcpy(p, (double *) &num, sizeof(double)); + lua_createtable(L, 1 /* narr */, 0 /* nrec */); + /* table of zone[i] */ + zone_udata = lua_newuserdata(L, sizeof(ngx_shm_zone_t *)); + /* shared mt key ud */ + *zone_udata = zone[i]; + lua_rawseti(L, -2, SHDICT_USERDATA_INDEX); /* {zone[i]} */ + lua_pushvalue(L, -3); /* shared mt key ud mt */ + lua_setmetatable(L, -2); /* shared mt key ud */ + lua_rawset(L, -4); /* shared mt */ + } - ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pop(L, 1); /* shared */ - lua_pushnumber(L, num); - lua_pushnil(L); - return 2; + } else { + lua_newtable(L); /* ngx.shared */ + } -remove: + lua_setfield(L, -2, "shared"); +} - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict incr: found old entry but value size " - "NOT matched, removing it first"); - if (sd->value_type == SHDICT_TLIST) { - queue = ngx_stream_lua_shdict_get_list_head(sd, key.len); +static ngx_inline ngx_shm_zone_t * +ngx_stream_lua_shdict_get_zone(lua_State *L, int index) +{ + ngx_shm_zone_t *zone; + ngx_shm_zone_t **zone_udata; - for (q = ngx_queue_head(queue); - q != ngx_queue_sentinel(queue); - q = ngx_queue_next(q)) - { - p = (u_char *) ngx_queue_data(q, - ngx_stream_lua_shdict_list_node_t, - queue); + lua_rawgeti(L, index, SHDICT_USERDATA_INDEX); + zone_udata = lua_touserdata(L, -1); + lua_pop(L, 1); - ngx_slab_free_locked(ctx->shpool, p); - } + if (zone_udata == NULL) { + return NULL; } - ngx_queue_remove(&sd->queue); - - node = (ngx_rbtree_node_t *) - ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); - - ngx_rbtree_delete(&ctx->sh->rbtree, node); + zone = *zone_udata; + return zone; +} - ngx_slab_free_locked(ctx->shpool, node); -insert: +static int +ngx_stream_lua_shdict_flush_expired(lua_State *L) +{ + ngx_queue_t *q, *prev, *list_queue, *lq; + ngx_shm_zone_t *zone; + ngx_time_t *tp; + int freed = 0; + int attempts = 0; + ngx_rbtree_node_t *node; + uint64_t now; + int n; - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict incr: creating a new entry"); + ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_list_node_t *lnode; - n = offsetof(ngx_rbtree_node_t, color) - + offsetof(ngx_stream_lua_shdict_node_t, data) - + key.len - + sizeof(double); + n = lua_gettop(L); - node = ngx_slab_alloc_locked(ctx->shpool, n); + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n); + } - if (node == NULL) { + luaL_checktype(L, 1, LUA_TTABLE); - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict incr: overriding non-expired items " - "due to memory shortage for entry \"%V\"", &key); + zone = ngx_stream_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } - for (i = 0; i < 30; i++) { - if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) { - break; - } + if (n == 2) { + attempts = luaL_checkint(L, 2); + } - forcible = 1; + ctx = zone->data; - node = ngx_slab_alloc_locked(ctx->shpool, n); - if (node != NULL) { - goto allocated; - } - } + ngx_shmtx_lock(&ctx->shpool->mutex); + if (ngx_queue_empty(&ctx->sh->lru_queue)) { ngx_shmtx_unlock(&ctx->shpool->mutex); - - lua_pushboolean(L, 0); - lua_pushliteral(L, "no memory"); - lua_pushboolean(L, forcible); - return 3; + lua_pushnumber(L, 0); + return 1; } -allocated: + tp = ngx_timeofday(); - sd = (ngx_stream_lua_shdict_node_t *) &node->color; + now = (uint64_t) tp->sec * 1000 + tp->msec; - node->key = hash; + q = ngx_queue_last(&ctx->sh->lru_queue); - sd->key_len = (u_short) key.len; + while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { + prev = ngx_queue_prev(q); - sd->value_len = (uint32_t) sizeof(double); + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); - ngx_rbtree_insert(&ctx->sh->rbtree, node); + if (sd->expires != 0 && sd->expires <= now) { - ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + if (sd->value_type == SHDICT_TLIST) { + list_queue = ngx_stream_lua_shdict_get_list_head(sd, + sd->key_len); -setvalue: + for (lq = ngx_queue_head(list_queue); + lq != ngx_queue_sentinel(list_queue); + lq = ngx_queue_next(lq)) + { + lnode = ngx_queue_data(lq, + ngx_stream_lua_shdict_list_node_t, + queue); - sd->user_flags = 0; + ngx_slab_free_locked(ctx->shpool, lnode); + } + } - sd->expires = 0; + ngx_queue_remove(q); - dd("setting value type to %d", LUA_TNUMBER); + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); - sd->value_type = (uint8_t) LUA_TNUMBER; + ngx_rbtree_delete(&ctx->sh->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); + freed++; - p = ngx_copy(sd->data, key.data, key.len); - ngx_memcpy(p, (double *) &num, sizeof(double)); + if (attempts && freed == attempts) { + break; + } + } + + q = prev; + } ngx_shmtx_unlock(&ctx->shpool->mutex); - lua_pushnumber(L, num); - lua_pushnil(L); - lua_pushboolean(L, forcible); - return 3; + lua_pushnumber(L, freed); + return 1; } -ngx_int_t -ngx_stream_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data, - size_t key_len, ngx_stream_lua_value_t *value) +/* + * This trades CPU for memory. This is potentially slow. O(2n) + */ + +static int +ngx_stream_lua_shdict_get_keys(lua_State *L) { - u_char *data; - size_t len; - uint32_t hash; - ngx_int_t rc; + ngx_queue_t *q, *prev; + ngx_shm_zone_t *zone; + ngx_time_t *tp; + int total = 0; + int attempts = 1024; + uint64_t now; + int n; - ngx_stream_lua_shdict_ctx_t *ctx; ngx_stream_lua_shdict_node_t *sd; + ngx_stream_lua_shdict_ctx_t *ctx; + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 argument(s), " + "but saw %d", n); + } + + luaL_checktype(L, 1, LUA_TTABLE); + zone = ngx_stream_lua_shdict_get_zone(L, 1); if (zone == NULL) { - return NGX_ERROR; + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } - hash = ngx_crc32_short(key_data, key_len); + if (n == 2) { + attempts = luaL_checkint(L, 2); + } ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); - rc = ngx_stream_lua_shdict_lookup(zone, hash, key_data, key_len, &sd); - - dd("shdict lookup returned %d", (int) rc); - - if (rc == NGX_DECLINED || rc == NGX_DONE) { + if (ngx_queue_empty(&ctx->sh->lru_queue)) { ngx_shmtx_unlock(&ctx->shpool->mutex); - - return rc; + lua_createtable(L, 0, 0); + return 1; } - /* rc == NGX_OK */ - - value->type = sd->value_type; - - dd("type: %d", (int) value->type); + tp = ngx_timeofday(); - data = sd->data + sd->key_len; - len = (size_t) sd->value_len; + now = (uint64_t) tp->sec * 1000 + tp->msec; - switch (value->type) { + /* first run through: get total number of elements we need to allocate */ - case SHDICT_TSTRING: + q = ngx_queue_last(&ctx->sh->lru_queue); - if (value->value.s.data == NULL || value->value.s.len == 0) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no string buffer " - "initialized"); - ngx_shmtx_unlock(&ctx->shpool->mutex); - return NGX_ERROR; - } + while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { + prev = ngx_queue_prev(q); - if (len > value->value.s.len) { - len = value->value.s.len; + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); - } else { - value->value.s.len = len; + if (sd->expires == 0 || sd->expires > now) { + total++; + if (attempts && total == attempts) { + break; + } } - ngx_memcpy(value->value.s.data, data, len); - break; - - case SHDICT_TNUMBER: + q = prev; + } - if (len != sizeof(double)) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number " - "value size found for key %*s: %lu", key_len, - key_data, (unsigned long) len); + lua_createtable(L, total, 0); - ngx_shmtx_unlock(&ctx->shpool->mutex); - return NGX_ERROR; - } + /* second run through: add keys to table */ - ngx_memcpy(&value->value.b, data, len); - break; + total = 0; + q = ngx_queue_last(&ctx->sh->lru_queue); - case SHDICT_TBOOLEAN: + while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) { + prev = ngx_queue_prev(q); - if (len != sizeof(u_char)) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean " - "value size found for key %*s: %lu", key_len, - key_data, (unsigned long) len); + sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue); - ngx_shmtx_unlock(&ctx->shpool->mutex); - return NGX_ERROR; + if (sd->expires == 0 || sd->expires > now) { + lua_pushlstring(L, (char *) sd->data, sd->key_len); + lua_rawseti(L, -2, ++total); + if (attempts && total == attempts) { + break; + } } - value->value.b = *data; - break; - - default: - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua value type " - "found for key %*s: %d", key_len, key_data, - (int) value->type); - - ngx_shmtx_unlock(&ctx->shpool->mutex); - return NGX_ERROR; + q = prev; } ngx_shmtx_unlock(&ctx->shpool->mutex); - return NGX_OK; + + /* table is at top of stack */ + return 1; } + + static int ngx_stream_lua_shdict_lpush(lua_State *L) { @@ -3039,7 +2008,7 @@ ngx_stream_lua_ffi_shdict_capacity(ngx_shm_zone_t *zone) } -# if nginx_version >= 1011007 +#if defined(nginx_version) && nginx_version >= 1011007 size_t ngx_stream_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone) { @@ -3055,7 +2024,7 @@ ngx_stream_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone) return bytes; } -# endif /* nginx_version >= 1011007 */ +#endif /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_socket_tcp.c b/src/ngx_stream_lua_socket_tcp.c index c808b385..241e974c 100644 --- a/src/ngx_stream_lua_socket_tcp.c +++ b/src/ngx_stream_lua_socket_tcp.c @@ -19,6 +19,7 @@ #include "ngx_stream_lua_socket_tcp.h" +#include "ngx_stream_lua_input_filters.h" #include "ngx_stream_lua_util.h" #include "ngx_stream_lua_uthread.h" #include "ngx_stream_lua_output.h" @@ -32,9 +33,9 @@ static int ngx_stream_lua_socket_tcp_connect(lua_State *L); static int ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L); #endif static int ngx_stream_lua_socket_tcp_receive(lua_State *L); +static int ngx_stream_lua_socket_tcp_receiveany(lua_State *L); static int ngx_stream_lua_socket_tcp_send(lua_State *L); static int ngx_stream_lua_socket_tcp_close(lua_State *L); -static int ngx_stream_lua_socket_tcp_shutdown(lua_State *L); static int ngx_stream_lua_socket_tcp_setoption(lua_State *L); static int ngx_stream_lua_socket_tcp_settimeout(lua_State *L); static int ngx_stream_lua_socket_tcp_settimeouts(lua_State *L); @@ -57,6 +58,7 @@ static void ngx_stream_lua_socket_tcp_finalize_read_part( static void ngx_stream_lua_socket_tcp_finalize_write_part( ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, int do_shutdown); + static ngx_int_t ngx_stream_lua_socket_send(ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); static ngx_int_t ngx_stream_lua_socket_test_connect(ngx_stream_lua_request_t *r, @@ -82,6 +84,8 @@ static int ngx_stream_lua_socket_tcp_conn_retval_handler( lua_State *L); static void ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); +static int ngx_stream_lua_socket_tcp_receive_helper(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L); static ngx_int_t ngx_stream_lua_socket_tcp_read(ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); static int ngx_stream_lua_socket_tcp_receive_retval_handler( @@ -104,6 +108,7 @@ static int ngx_stream_lua_socket_write_error_retval_handler( static ngx_int_t ngx_stream_lua_socket_read_all(void *data, ssize_t bytes); static ngx_int_t ngx_stream_lua_socket_read_until(void *data, ssize_t bytes); static ngx_int_t ngx_stream_lua_socket_read_chunk(void *data, ssize_t bytes); +static ngx_int_t ngx_stream_lua_socket_read_any(void *data, ssize_t bytes); static int ngx_stream_lua_socket_tcp_receiveuntil(lua_State *L); static int ngx_stream_lua_socket_receiveuntil_iterator(lua_State *L); static ngx_int_t ngx_stream_lua_socket_compile_pattern(u_char *data, size_t len, @@ -112,12 +117,30 @@ static int ngx_stream_lua_socket_cleanup_compiled_pattern(lua_State *L); static void ngx_stream_lua_req_socket_rev_handler(ngx_stream_lua_request_t *r); static int ngx_stream_lua_socket_tcp_getreusedtimes(lua_State *L); static int ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L); +static void ngx_stream_lua_socket_tcp_create_socket_pool(lua_State *L, + ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size, + ngx_int_t backlog, ngx_stream_lua_socket_pool_t **spool); static ngx_int_t ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r, - lua_State *L, int key_index, ngx_stream_lua_socket_tcp_upstream_t *u); static void ngx_stream_lua_socket_keepalive_dummy_handler(ngx_event_t *ev); +static int ngx_stream_lua_socket_tcp_connect_helper(lua_State *L, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, + in_port_t port, unsigned resuming); +static void ngx_stream_lua_socket_tcp_conn_op_timeout_handler( + ngx_event_t *ev); +static int ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); +static void ngx_stream_lua_socket_tcp_resume_conn_op( + ngx_stream_lua_socket_pool_t *spool); +static void ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup(void *data); +static void ngx_stream_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev); static ngx_int_t ngx_stream_lua_socket_keepalive_close_handler(ngx_event_t *ev); static void ngx_stream_lua_socket_keepalive_rev_handler(ngx_event_t *ev); +static int ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L); static int ngx_stream_lua_socket_tcp_upstream_destroy(lua_State *L); static int ngx_stream_lua_socket_downstream_destroy(lua_State *L); static ngx_int_t ngx_stream_lua_socket_push_input_data( @@ -131,6 +154,8 @@ static ngx_int_t ngx_stream_lua_socket_add_input_buffer( static ngx_int_t ngx_stream_lua_socket_insert_buffer( ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix); +static ngx_int_t ngx_stream_lua_socket_tcp_conn_op_resume( + ngx_stream_lua_request_t *r); static ngx_int_t ngx_stream_lua_socket_tcp_conn_resume( ngx_stream_lua_request_t *r); static ngx_int_t ngx_stream_lua_socket_tcp_read_resume( @@ -139,6 +164,7 @@ static ngx_int_t ngx_stream_lua_socket_tcp_write_resume( ngx_stream_lua_request_t *r); static ngx_int_t ngx_stream_lua_socket_tcp_resume_helper( ngx_stream_lua_request_t *r, int socket_op); +static void ngx_stream_lua_tcp_queue_conn_op_cleanup(void *data); static void ngx_stream_lua_tcp_resolve_cleanup(void *data); static void ngx_stream_lua_coctx_cleanup(void *data); static void ngx_stream_lua_socket_free_pool(ngx_log_t *log, @@ -162,6 +188,7 @@ static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c); static int ngx_stream_lua_socket_tcp_peek(lua_State *L); static ngx_int_t ngx_stream_lua_socket_tcp_peek_resume(ngx_stream_lua_request_t *r); +static int ngx_stream_lua_socket_tcp_shutdown(lua_State *L); enum { @@ -176,7 +203,8 @@ enum { enum { SOCKET_OP_CONNECT, SOCKET_OP_READ, - SOCKET_OP_WRITE + SOCKET_OP_WRITE, + SOCKET_OP_RESUME_CONN }; @@ -258,10 +286,7 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) /* {{{raw req socket object metatable */ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( raw_req_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 7 /* nrec */); - - lua_pushcfunction(L, ngx_stream_lua_socket_tcp_peek); - lua_setfield(L, -2, "peek"); + lua_createtable(L, 0 /* narr */, 8 /* nrec */); lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receive); lua_setfield(L, -2, "receive"); @@ -278,6 +303,9 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeouts); lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_peek); + lua_setfield(L, -2, "peek"); + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown); lua_setfield(L, -2, "shutdown"); @@ -290,7 +318,7 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) /* {{{tcp object metatable */ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( tcp_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 12 /* nrec */); + lua_createtable(L, 0 /* narr */, 14 /* nrec */); lua_pushcfunction(L, ngx_stream_lua_socket_tcp_connect); lua_setfield(L, -2, "connect"); @@ -308,15 +336,15 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveuntil); lua_setfield(L, -2, "receiveuntil"); + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveany); + lua_setfield(L, -2, "receiveany"); + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_send); lua_setfield(L, -2, "send"); lua_pushcfunction(L, ngx_stream_lua_socket_tcp_close); lua_setfield(L, -2, "close"); - lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown); - lua_setfield(L, -2, "shutdown"); - lua_pushcfunction(L, ngx_stream_lua_socket_tcp_setoption); lua_setfield(L, -2, "setoption"); @@ -332,6 +360,9 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_stream_lua_socket_tcp_setkeepalive); lua_setfield(L, -2, "setkeepalive"); + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown); + lua_setfield(L, -2, "shutdown"); + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); @@ -426,32 +457,426 @@ ngx_stream_lua_socket_tcp(lua_State *L) } +static void +ngx_stream_lua_socket_tcp_create_socket_pool(lua_State *L, + ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size, ngx_int_t backlog, + ngx_stream_lua_socket_pool_t **spool) +{ + u_char *p; + size_t size, key_len; + ngx_int_t i; + + ngx_stream_lua_socket_pool_t *sp; + ngx_stream_lua_socket_pool_item_t *items; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket connection pool size: " + "%i, backlog: %i", + pool_size, backlog); + + key_len = ngx_align(key.len + 1, sizeof(void *)); + + size = sizeof(ngx_stream_lua_socket_pool_t) - 1 + key_len + + sizeof(ngx_stream_lua_socket_pool_item_t) * pool_size; + + /* before calling this function, the Lua stack is: + * -1 key + * -2 pools + */ + sp = lua_newuserdata(L, size); + if (sp == NULL) { + luaL_error(L, "no memory"); + return; + } + + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + pool_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket keepalive create " + "connection pool for key \"%V\"", &key); + + /* a new socket pool with metatable is push to the stack, so now we have: + * -1 sp + * -2 key + * -3 pools + * + * it is time to set pools[key] to sp. + */ + lua_rawset(L, -3); + + /* clean up the stack for consistency's sake */ + lua_pop(L, 1); + + sp->backlog = backlog; + sp->size = pool_size; + sp->connections = 0; + sp->lua_vm = ngx_stream_lua_get_lua_vm(r, NULL); + + ngx_queue_init(&sp->cache_connect_op); + ngx_queue_init(&sp->wait_connect_op); + ngx_queue_init(&sp->cache); + ngx_queue_init(&sp->free); + + p = ngx_copy(sp->key, key.data, key.len); + *p++ = '\0'; + + items = (ngx_stream_lua_socket_pool_item_t *) (sp->key + key_len); + + dd("items: %p", items); + + ngx_stream_lua_assert((void *) items + == ngx_align_ptr(items, sizeof(void *))); + + for (i = 0; i < pool_size; i++) { + ngx_queue_insert_head(&sp->free, &items[i].queue); + items[i].socket_pool = sp; + } + + *spool = sp; +} + + +static int +ngx_stream_lua_socket_tcp_connect_helper(lua_State *L, + ngx_stream_lua_socket_tcp_upstream_t *u, ngx_stream_lua_request_t *r, + ngx_stream_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, + in_port_t port, unsigned resuming) +{ + int n; + int host_size; + int saved_top; + ngx_int_t rc; + ngx_str_t host; + ngx_str_t *conn_op_host; + ngx_url_t url; + ngx_queue_t *q; + ngx_resolver_ctx_t *rctx, temp; + + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_pool_t *spool; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + ngx_stream_core_srv_conf_t *clcf; + + spool = u->socket_pool; + if (spool != NULL) { + rc = ngx_stream_lua_get_keepalive_peer(r, u); + + if (rc == NGX_OK) { + lua_pushinteger(L, 1); + return 1; + } + + /* rc == NGX_DECLINED */ + + spool->connections++; + + /* check if backlog is enabled and + * don't queue resuming connection operation */ + if (spool->backlog >= 0 && !resuming) { + + dd("lua tcp socket %s connections %ld", + spool->key, spool->connections); + + if (spool->connections > spool->size + spool->backlog) { + spool->connections--; + lua_pushnil(L); + lua_pushliteral(L, "too many waiting connect operations"); + return 2; + } + + if (spool->connections > spool->size) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->peer.log, 0, + "stream lua tcp socket queue connect " + "operation for connection pool \"%s\", " + "connections: %i", + spool->key, spool->connections); + + host_size = sizeof(u_char) * + (ngx_max(host_len, NGX_INET_ADDRSTRLEN) + 1); + + if (!ngx_queue_empty(&spool->cache_connect_op)) { + q = ngx_queue_last(&spool->cache_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data( + q, ngx_stream_lua_socket_tcp_conn_op_ctx_t, queue); + + conn_op_host = &conn_op_ctx->host; + if (host_len > conn_op_host->len + && host_len > NGX_INET_ADDRSTRLEN) + { + ngx_free(conn_op_host->data); + conn_op_host->data = ngx_alloc(host_size, + ngx_cycle->log); + if (conn_op_host->data == NULL) { + ngx_free(conn_op_ctx); + goto no_memory_and_not_resuming; + } + } + + } else { + conn_op_ctx = ngx_alloc( + sizeof(ngx_stream_lua_socket_tcp_conn_op_ctx_t), + ngx_cycle->log); + if (conn_op_ctx == NULL) { + goto no_memory_and_not_resuming; + } + + conn_op_host = &conn_op_ctx->host; + conn_op_host->data = ngx_alloc(host_size, ngx_cycle->log); + if (conn_op_host->data == NULL) { + ngx_free(conn_op_ctx); + goto no_memory_and_not_resuming; + } + } + + conn_op_ctx->cleanup = NULL; + + ngx_memcpy(conn_op_host->data, host_ref, host_len); + conn_op_host->data[host_len] = '\0'; + conn_op_host->len = host_len; + + conn_op_ctx->port = port; + + u->write_co_ctx = ctx->cur_co_ctx; + + conn_op_ctx->u = u; + ctx->cur_co_ctx->cleanup = + ngx_stream_lua_tcp_queue_conn_op_cleanup; + ctx->cur_co_ctx->data = conn_op_ctx; + + ngx_memzero(&conn_op_ctx->event, sizeof(ngx_event_t)); + conn_op_ctx->event.handler = + ngx_stream_lua_socket_tcp_conn_op_timeout_handler; + conn_op_ctx->event.data = conn_op_ctx; + conn_op_ctx->event.log = ngx_cycle->log; + + ngx_add_timer(&conn_op_ctx->event, u->connect_timeout); + + ngx_queue_insert_tail(&spool->wait_connect_op, + &conn_op_ctx->queue); + + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket queued connect " + "operation for %d(ms), u: %p, ctx: %p", + u->connect_timeout, conn_op_ctx->u, conn_op_ctx); + + return lua_yield(L, 0); + } + } + + } /* end spool != NULL */ + + host.data = ngx_palloc(r->pool, host_len + 1); + if (host.data == NULL) { + return luaL_error(L, "no memory"); + } + + host.len = host_len; + + ngx_memcpy(host.data, host_ref, host_len); + host.data[host_len] = '\0'; + + ngx_memzero(&url, sizeof(ngx_url_t)); + url.url = host; + url.default_port = port; + url.no_resolve = 1; + + coctx = ctx->cur_co_ctx; + + if (ngx_parse_url(r->pool, &url) != NGX_OK) { + lua_pushnil(L); + + if (url.err) { + lua_pushfstring(L, "failed to parse host name \"%s\": %s", + url.url.data, url.err); + + } else { + lua_pushfstring(L, "failed to parse host name \"%s\"", + url.url.data); + } + + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket connect timeout: %M", + u->connect_timeout); + + u->resolved = ngx_pcalloc(r->pool, + sizeof(ngx_stream_upstream_resolved_t)); + if (u->resolved == NULL) { + if (resuming) { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + goto failed; + } + + goto no_memory_and_not_resuming; + } + + if (url.addrs && url.addrs[0].sockaddr) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket network address given " + "directly"); + + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = host; + u->resolved->port = url.default_port; + } + + if (u->resolved->sockaddr) { + rc = ngx_stream_lua_socket_resolve_retval_handler(r, u, L); + if (rc == NGX_AGAIN && !resuming) { + return lua_yield(L, 0); + } + + if (rc > 1) { + goto failed; + } + + return rc; + } + + clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module); + + temp.name = host; + rctx = ngx_resolve_start(clcf->resolver, &temp); + if (rctx == NULL) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushliteral(L, "failed to start the resolver"); + goto failed; + } + + if (rctx == NGX_NO_RESOLVER) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); + goto failed; + } + + rctx->name = host; + rctx->handler = ngx_stream_lua_socket_resolve_handler; + rctx->data = u; + rctx->timeout = clcf->resolver_timeout; + + u->resolved->ctx = rctx; + u->write_co_ctx = ctx->cur_co_ctx; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_tcp_resolve_cleanup; + coctx->data = u; + + saved_top = lua_gettop(L); + + if (ngx_resolve_name(rctx) != NGX_OK) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket fail to run resolver " + "immediately"); + + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; + + coctx->cleanup = NULL; + coctx->data = NULL; + + u->resolved->ctx = NULL; + lua_pushnil(L); + lua_pushfstring(L, "%s could not be resolved", host.data); + goto failed; + } + + if (u->conn_waiting) { + dd("resolved and already connecting"); + + if (resuming) { + return NGX_AGAIN; + } + + return lua_yield(L, 0); + } + + n = lua_gettop(L) - saved_top; + if (n) { + dd("errors occurred during resolving or connecting" + "or already connected"); + + if (n > 1) { + goto failed; + } + + return n; + } + + /* still resolving */ + + u->conn_waiting = 1; + u->write_prepare_retvals = ngx_stream_lua_socket_resolve_retval_handler; + + dd("setting data to %p", u); + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + if (resuming) { + return NGX_AGAIN; + } + + return lua_yield(L, 0); + +failed: + + if (spool != NULL) { + spool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + } + + return 2; + +no_memory_and_not_resuming: + + if (spool != NULL) { + spool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + } + + return luaL_error(L, "no memory"); +} + + static int ngx_stream_lua_socket_tcp_connect(lua_State *L) { ngx_stream_lua_request_t *r; ngx_stream_lua_ctx_t *ctx; - ngx_str_t host; int port; - ngx_resolver_ctx_t *rctx, temp; - - ngx_stream_core_srv_conf_t *clcf; - - int saved_top; int n; u_char *p; size_t len; - ngx_url_t url; - ngx_int_t rc; ngx_peer_connection_t *pc; int connect_timeout, send_timeout, read_timeout; unsigned custom_pool; int key_index; + ngx_int_t backlog; + ngx_int_t pool_size; + ngx_str_t key; const char *msg; ngx_stream_lua_loc_conf_t *llcf; - ngx_stream_lua_co_ctx_t *coctx; ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_pool_t *spool; n = lua_gettop(L); if (n != 2 && n != 3 && n != 4) { @@ -480,13 +905,54 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) p = (u_char *) luaL_checklstring(L, 2, &len); + backlog = -1; key_index = 2; + pool_size = 0; custom_pool = 0; + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); if (lua_type(L, n) == LUA_TTABLE) { /* found the last optional option table */ + lua_getfield(L, n, "pool_size"); + + if (lua_isnumber(L, -1)) { + pool_size = (ngx_int_t) lua_tointeger(L, -1); + + if (pool_size <= 0) { + msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", + pool_size); + return luaL_argerror(L, n, msg); + } + + } else if (!lua_isnil(L, -1)) { + msg = lua_pushfstring(L, "bad \"pool_size\" option type: %s", + lua_typename(L, lua_type(L, -1))); + return luaL_argerror(L, n, msg); + } + + lua_pop(L, 1); + + lua_getfield(L, n, "backlog"); + + if (lua_isnumber(L, -1)) { + backlog = (ngx_int_t) lua_tointeger(L, -1); + + if (backlog < 0) { + msg = lua_pushfstring(L, "bad \"backlog\" option value: %i", + backlog); + return luaL_argerror(L, n, msg); + } + + /* use default value for pool size if only backlog specified */ + if (pool_size == 0) { + pool_size = llcf->pool_size; + } + } + + lua_pop(L, 1); + lua_getfield(L, n, "pool"); switch (lua_type(L, -1)) { @@ -598,12 +1064,8 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) ngx_memzero(u, sizeof(ngx_stream_lua_socket_tcp_upstream_t)); - coctx = ctx->cur_co_ctx; - u->request = r; /* set the controlling request */ - llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); - u->conf = llcf; pc = &u->peer; @@ -644,165 +1106,29 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) u->read_timeout = u->conf->read_timeout; } - rc = ngx_stream_lua_get_keepalive_peer(r, L, key_index, u); - - if (rc == NGX_OK) { - lua_pushinteger(L, 1); - return 1; - } - - if (rc == NGX_ERROR) { - lua_pushnil(L); - lua_pushliteral(L, "error in get keepalive peer"); - return 2; - } - - /* rc == NGX_DECLINED */ - - /* TODO: we should avoid this in-pool allocation */ - - host.data = ngx_palloc(r->pool, len + 1); - if (host.data == NULL) { - return luaL_error(L, "no memory"); - } - - host.len = len; - - ngx_memcpy(host.data, p, len); - host.data[len] = '\0'; - - ngx_memzero(&url, sizeof(ngx_url_t)); - - url.url.len = host.len; - url.url.data = host.data; - url.default_port = (in_port_t) port; - url.no_resolve = 1; - - if (ngx_parse_url(r->pool, &url) != NGX_OK) { - lua_pushnil(L); - - if (url.err) { - lua_pushfstring(L, "failed to parse host name \"%s\": %s", - host.data, url.err); - - } else { - lua_pushfstring(L, "failed to parse host name \"%s\"", host.data); - } - - return 2; - } - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "stream lua tcp socket connect timeout: %M", - u->connect_timeout); - - u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_stream_upstream_resolved_t)); - if (u->resolved == NULL) { - return luaL_error(L, "no memory"); - } - - if (url.addrs && url.addrs[0].sockaddr) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "stream lua tcp socket network address given directly"); - - u->resolved->sockaddr = url.addrs[0].sockaddr; - u->resolved->socklen = url.addrs[0].socklen; - u->resolved->naddrs = 1; - u->resolved->host = url.addrs[0].name; - - } else { - u->resolved->host = host; - u->resolved->port = (in_port_t) port; - } - - if (u->resolved->sockaddr) { - rc = ngx_stream_lua_socket_resolve_retval_handler(r, u, L); - if (rc == NGX_AGAIN) { - return lua_yield(L, 0); - } - - return rc; - } - - clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module); - - temp.name = host; - rctx = ngx_resolve_start(clcf->resolver, &temp); - if (rctx == NULL) { - u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; - lua_pushnil(L); - lua_pushliteral(L, "failed to start the resolver"); - return 2; - } - - if (rctx == NGX_NO_RESOLVER) { - u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; - lua_pushnil(L); - lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); - return 2; - } - - rctx->name = host; -#if !defined(nginx_version) || nginx_version < 1005008 - rctx->type = NGX_RESOLVE_A; -#endif - rctx->handler = ngx_stream_lua_socket_resolve_handler; - rctx->data = u; - rctx->timeout = clcf->resolver_timeout; - - u->resolved->ctx = rctx; - u->write_co_ctx = ctx->cur_co_ctx; - - ngx_stream_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_stream_lua_tcp_resolve_cleanup; - coctx->data = u; - - saved_top = lua_gettop(L); - - if (ngx_resolve_name(rctx) != NGX_OK) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "stream lua tcp socket fail to run resolver " - "immediately"); - - u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER; - - coctx->cleanup = NULL; - coctx->data = NULL; - - u->resolved->ctx = NULL; - lua_pushnil(L); - lua_pushfstring(L, "%s could not be resolved", host.data); - - return 2; - } - - if (u->conn_waiting) { - dd("resolved and already connecting"); - return lua_yield(L, 0); - } - - n = lua_gettop(L) - saved_top; - if (n) { - dd("errors occurred during resolving or connecting" - "or already connected"); - return n; - } - - /* still resolving */ + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); /* table */ + lua_pushvalue(L, key_index); /* key */ - u->conn_waiting = 1; - u->write_prepare_retvals = ngx_stream_lua_socket_resolve_retval_handler; + lua_rawget(L, -2); + spool = lua_touserdata(L, -1); + lua_pop(L, 1); - dd("setting data to %p", u); + if (spool != NULL) { + u->socket_pool = spool; - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_stream_lua_content_wev_handler; + } else if (pool_size > 0) { + lua_pushvalue(L, key_index); + key.data = (u_char *) lua_tolstring(L, -1, &key.len); - } else { - r->write_event_handler = ngx_stream_lua_core_run_phases; + ngx_stream_lua_socket_tcp_create_socket_pool(L, r, key, pool_size, + backlog, &spool); + u->socket_pool = spool; } - return lua_yield(L, 0); + return ngx_stream_lua_socket_tcp_connect_helper(L, u, r, ctx, p, + len, port, 0); } @@ -823,12 +1149,8 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) lua_State *L; u_char *p; size_t len; -#if defined(nginx_version) && nginx_version >= 1005008 socklen_t socklen; struct sockaddr *sockaddr; -#else - struct sockaddr_in *sin; -#endif ngx_uint_t i; unsigned waiting; @@ -890,36 +1212,19 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) #if (NGX_DEBUG) { -# if defined(nginx_version) && nginx_version >= 1005008 - u_char text[NGX_SOCKADDR_STRLEN]; - ngx_str_t addr; -# else - in_addr_t addr; -# endif - ngx_uint_t i; - -# if defined(nginx_version) && nginx_version >= 1005008 - addr.data = text; + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + ngx_uint_t i; - for (i = 0; i < ctx->naddrs; i++) { - addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, - text, NGX_SOCKADDR_STRLEN, 0); - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "name was resolved to %V", &addr); - } -# else - for (i = 0; i < ctx->naddrs; i++) { - dd("addr i: %d %p", (int) i, &ctx->addrs[i]); + addr.data = text; - addr = ntohl(ctx->addrs[i]); + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); - ngx_log_debug4(NGX_LOG_DEBUG_STREAM, c->log, 0, - "name was resolved to %ud.%ud.%ud.%ud", - (addr >> 24) & 0xff, (addr >> 16) & 0xff, - (addr >> 8) & 0xff, addr & 0xff); - } -# endif + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "name was resolved to %V", &addr); + } } #endif @@ -934,7 +1239,6 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) dd("selected addr index: %d", (int) i); -#if defined(nginx_version) && nginx_version >= 1005008 socklen = ur->addrs[i].socklen; sockaddr = ngx_palloc(r->pool, socklen); @@ -962,31 +1266,6 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); ur->sockaddr = sockaddr; ur->socklen = socklen; - -#else - /* for nginx older than 1.5.8 */ - - len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; - - p = ngx_pnalloc(r->pool, len + sizeof(struct sockaddr_in)); - if (p == NULL) { - goto nomem; - } - - sin = (struct sockaddr_in *) &p[len]; - ngx_memzero(sin, sizeof(struct sockaddr_in)); - - len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); - len = ngx_sprintf(&p[len], ":%d", ur->port) - p; - - sin->sin_family = AF_INET; - sin->sin_port = htons(ur->port); - sin->sin_addr.s_addr = ur->addrs[i]; - - ur->sockaddr = (struct sockaddr *) sin; - ur->socklen = sizeof(struct sockaddr_in); -#endif - ur->host.data = p; ur->host.len = len; ur->naddrs = 1; @@ -1507,12 +1786,12 @@ ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L) c->ssl->handler = ngx_stream_lua_ssl_handshake_handler; - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_stream_lua_content_wev_handler; + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; - } else { - r->write_event_handler = ngx_stream_lua_core_run_phases; - } + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } return lua_yield(L, 0); } @@ -1748,11 +2027,7 @@ ngx_stream_lua_socket_prepare_error_retvals(ngx_stream_lua_request_t *r, } else { if (u->socket_errno) { -#if defined(nginx_version) && nginx_version >= 9000 p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); -#else - p = ngx_strerror_r(u->socket_errno, errstr, sizeof(errstr)); -#endif /* for compatibility with LuaSocket */ ngx_strlow(errstr, errstr, p - errstr); lua_pushlstring(L, (char *) errstr, p - errstr); @@ -1954,14 +2229,169 @@ ngx_stream_lua_socket_tcp_peek_resume(ngx_stream_lua_request_t *r) } +static int +ngx_stream_lua_socket_tcp_receive_helper(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_int_t rc; + ngx_stream_lua_ctx_t *lctx; + ngx_stream_lua_co_ctx_t *coctx; + + u->input_filter_ctx = u; + + lctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (u->bufs_in == NULL) { + u->bufs_in = + ngx_stream_lua_chain_get_free_buf(r->connection->log, + r->pool, + &lctx->free_recv_bufs, + u->conf->buffer_size); + + if (u->bufs_in == NULL) { + return luaL_error(L, "no memory"); + } + + u->buf_in = u->bufs_in; + u->buffer = *u->buf_in->buf; + } + + dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket read timeout: %M", u->read_timeout); + + if (u->raw_downstream || u->body_downstream) { + r->read_event_handler = ngx_stream_lua_req_socket_rev_handler; + } + + u->read_waiting = 0; + u->read_co_ctx = NULL; + + rc = ngx_stream_lua_socket_tcp_read(r, u); + + if (rc == NGX_ERROR) { + dd("read failed: %d", (int) u->ft_type); + rc = ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); + dd("tcp receive retval returned: %d", (int) rc); + return rc; + } + + if (rc == NGX_OK) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket receive done in a single run"); + + return ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); + } + + /* rc == NGX_AGAIN */ + + u->read_event_handler = ngx_stream_lua_socket_read_handler; + + coctx = lctx->cur_co_ctx; + + ngx_stream_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_stream_lua_coctx_cleanup; + coctx->data = u; + + if (lctx->entered_content_phase) { + r->write_event_handler = ngx_stream_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_stream_lua_core_run_phases; + } + + u->read_co_ctx = coctx; + u->read_waiting = 1; + u->read_prepare_retvals = ngx_stream_lua_socket_tcp_receive_retval_handler; + + dd("setting data to %p, coctx:%p", u, coctx); + + if (u->raw_downstream || u->body_downstream) { + lctx->downstream = u; + } + + return lua_yield(L, 0); +} + + +static int +ngx_stream_lua_socket_tcp_receiveany(lua_State *L) +{ + int n; + lua_Integer bytes; + ngx_stream_lua_request_t *r; + ngx_stream_lua_loc_conf_t *llcf; + + ngx_stream_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments " + "(including the object), but got %d", n); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL || u->peer.connection == NULL || u->read_closed) { + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to receive data on a closed " + "socket: u:%p, c:%p, ft:%d eof:%d", + u, u ? u->peer.connection : NULL, + u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_stream_lua_socket_check_busy_connecting(r, u, L); + ngx_stream_lua_socket_check_busy_reading(r, u, L); + + if (!lua_isnumber(L, 2)) { + return luaL_argerror(L, 2, "bad max argument"); + } + + bytes = lua_tointeger(L, 2); + if (bytes <= 0) { + return luaL_argerror(L, 2, "bad max argument"); + } + + u->input_filter = ngx_stream_lua_socket_read_any; + u->rest = (size_t) bytes; + u->length = u->rest; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket calling receiveany() " + "method to read at most %uz bytes", u->rest); + + return ngx_stream_lua_socket_tcp_receive_helper(r, u, L); +} + + static int ngx_stream_lua_socket_tcp_receive(lua_State *L) { ngx_stream_lua_request_t *r; - ngx_int_t rc; - ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_loc_conf_t *llcf; - ngx_stream_lua_co_ctx_t *coctx; int n; ngx_str_t pat; lua_Integer bytes; @@ -1995,8 +2425,8 @@ ngx_stream_lua_socket_tcp_receive(lua_State *L) if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "attempt to receive data on a closed socket: u:%p, " - "c:%p, ft:%d eof:%d", + "stream attempt to receive data on a closed " + "socket: u:%p, c:%p, ft:%d eof:%d", u, u ? u->peer.connection : NULL, u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); } @@ -2072,130 +2502,38 @@ ngx_stream_lua_socket_tcp_receive(lua_State *L) break; - default: - return luaL_argerror(L, 2, "bad pattern argument"); - break; - } - - } else { - u->input_filter = ngx_stream_lua_socket_read_line; - u->length = 0; - u->rest = 0; - } - - u->input_filter_ctx = u; - - ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); - - if (u->bufs_in == NULL) { - u->bufs_in = - ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool, - &ctx->free_recv_bufs, - u->conf->buffer_size); - - if (u->bufs_in == NULL) { - return luaL_error(L, "no memory"); - } - - u->buf_in = u->bufs_in; - u->buffer = *u->buf_in->buf; - } - - dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in); - - if (u->raw_downstream || u->body_downstream) { - r->read_event_handler = ngx_stream_lua_req_socket_rev_handler; - } - - u->read_waiting = 0; - u->read_co_ctx = NULL; - - rc = ngx_stream_lua_socket_tcp_read(r, u); - - if (rc == NGX_ERROR) { - dd("read failed: %d", (int) u->ft_type); - rc = ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); - dd("tcp receive retval returned: %d", (int) rc); - return rc; - } - - if (rc == NGX_OK) { - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "stream lua tcp socket receive done in a single run"); - - return ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L); - } - - /* rc == NGX_AGAIN */ - - u->read_event_handler = ngx_stream_lua_socket_read_handler; - - coctx = ctx->cur_co_ctx; - - ngx_stream_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_stream_lua_coctx_cleanup; - coctx->data = u; - - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_stream_lua_content_wev_handler; - - } else { - r->write_event_handler = ngx_stream_lua_core_run_phases; - } - - u->read_co_ctx = coctx; - u->read_waiting = 1; - u->read_prepare_retvals = ngx_stream_lua_socket_tcp_receive_retval_handler; - - dd("setting data to %p, coctx:%p", u, coctx); + default: + return luaL_argerror(L, 2, "bad pattern argument"); + break; + } - if (u->raw_downstream || u->body_downstream) { - ctx->downstream = u; + } else { + u->input_filter = ngx_stream_lua_socket_read_line; + u->length = 0; + u->rest = 0; } - return lua_yield(L, 0); + return ngx_stream_lua_socket_tcp_receive_helper(r, u, L); } static ngx_int_t ngx_stream_lua_socket_read_chunk(void *data, ssize_t bytes) { + ngx_int_t rc; ngx_stream_lua_socket_tcp_upstream_t *u = data; - ngx_buf_t *b; -#if (NGX_DEBUG) - ngx_stream_lua_request_t *r; - - r = u->request; -#endif - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, "stream lua tcp socket read chunk %z", bytes); - if (bytes == 0) { + rc = ngx_stream_lua_read_bytes(&u->buffer, u->buf_in, &u->rest, + bytes, u->request->connection->log); + if (rc == NGX_ERROR) { u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED; return NGX_ERROR; } - b = &u->buffer; - - if (bytes >= (ssize_t) u->rest) { - - u->buf_in->buf->last += u->rest; - b->pos += u->rest; - u->rest = 0; - - return NGX_OK; - } - - /* bytes < u->rest */ - - u->buf_in->buf->last += bytes; - b->pos += bytes; - u->rest -= bytes; - - return NGX_AGAIN; + return rc; } @@ -2204,100 +2542,50 @@ ngx_stream_lua_socket_read_all(void *data, ssize_t bytes) { ngx_stream_lua_socket_tcp_upstream_t *u = data; - ngx_buf_t *b; -#if (NGX_DEBUG) - ngx_stream_lua_request_t *r; - - r = u->request; -#endif - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, "stream lua tcp socket read all"); - - if (bytes == 0) { - return NGX_OK; - } - - b = &u->buffer; - - u->buf_in->buf->last += bytes; - b->pos += bytes; - - return NGX_AGAIN; + return ngx_stream_lua_read_all(&u->buffer, u->buf_in, bytes, + u->request->connection->log); } static ngx_int_t ngx_stream_lua_socket_read_line(void *data, ssize_t bytes) { + ngx_int_t rc; ngx_stream_lua_socket_tcp_upstream_t *u = data; - ngx_buf_t *b; - u_char *dst; - u_char c; -#if (NGX_DEBUG) - u_char *begin; -#endif - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, "stream lua tcp socket read line"); - if (bytes == 0) { + rc = ngx_stream_lua_read_line(&u->buffer, u->buf_in, bytes, + u->request->connection->log); + if (rc == NGX_ERROR) { u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED; return NGX_ERROR; } - b = &u->buffer; - -#if (NGX_DEBUG) - begin = b->pos; -#endif - - dd("already read: %p: %.*s", u->buf_in, - (int) (u->buf_in->buf->last - u->buf_in->buf->pos), - u->buf_in->buf->pos); - - dd("data read: %.*s", (int) bytes, b->pos); - - dst = u->buf_in->buf->last; - - while (bytes--) { - - c = *b->pos++; - - switch (c) { - case '\n': - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, - "stream lua tcp socket read the final line " - "part: \"%*s\"", b->pos - 1 - begin, begin); - - u->buf_in->buf->last = dst; + return rc; +} - dd("read a line: %p: %.*s", u->buf_in, - (int) (u->buf_in->buf->last - u->buf_in->buf->pos), - u->buf_in->buf->pos); - return NGX_OK; +static ngx_int_t +ngx_stream_lua_socket_read_any(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_stream_lua_socket_tcp_upstream_t *u = data; - case '\r': - /* ignore it */ - break; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, + "stream lua tcp socket read any"); - default: - *dst++ = c; - break; - } + rc = ngx_stream_lua_read_any(&u->buffer, u->buf_in, &u->rest, bytes, + u->request->connection->log); + if (rc == NGX_ERROR) { + u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; } -#if (NGX_DEBUG) - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, - "stream lua tcp socket read partial line data: %*s", - dst - begin, begin); -#endif - - u->buf_in->buf->last = dst; - - return NGX_AGAIN; + return rc; } @@ -2578,6 +2866,20 @@ ngx_stream_lua_socket_tcp_send(lua_State *L) len = ngx_stream_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); break; + case LUA_TNIL: + len = sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + len = sizeof("true") - 1; + + } else { + len = sizeof("false") - 1; + } + + break; + default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "or array table expected, got %s", @@ -2613,6 +2915,29 @@ ngx_stream_lua_socket_tcp_send(lua_State *L) b->last = ngx_stream_lua_copy_str_in_table(L, -1, b->last); break; + case LUA_TNIL: + *b->last++ = 'n'; + *b->last++ = 'i'; + *b->last++ = 'l'; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + *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; + default: return luaL_error(L, "impossible to reach here"); } @@ -3207,11 +3532,7 @@ ngx_stream_lua_socket_send(ngx_stream_lua_request_t *r, } -#if defined(nginx_version) && nginx_version >= 1001004 ngx_chain_update_chains(r->pool, -#else - ngx_chain_update_chains( -#endif &ctx->free_bufs, &ctx->busy_bufs, &u->request_bufs, (ngx_buf_tag_t) &ngx_stream_lua_module); @@ -3625,98 +3946,361 @@ ngx_stream_lua_socket_tcp_finalize_read_part(ngx_stream_lua_request_t *r, ngx_memzero(&u->buffer, sizeof(ngx_buf_t)); } - if (u->raw_downstream || u->body_downstream) { - if (r->connection->read->timer_set) { - ngx_del_timer(r->connection->read); - } + if (u->raw_downstream || u->body_downstream) { + if (r->connection->read->timer_set) { + ngx_del_timer(r->connection->read); + } + return; + } + + c = u->peer.connection; + + if (c) { + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->read->active || c->read->disabled) { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + +#if defined(nginx_version) && nginx_version >= 1007005 + if (c->read->posted) { +#else + if (c->read->prev) { +#endif + ngx_delete_posted_event(c->read); + } + + c->read->closed = 1; + + /* TODO: shutdown the reading part of the connection */ + } +} + + +static void +ngx_stream_lua_socket_tcp_finalize_write_part(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, int do_shutdown) + +{ + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + + c = u->peer.connection; + + if (u->write_closed) { + return; + } + + u->write_closed = 1; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (c && do_shutdown) { + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua shutdown socket write direction"); + } + + if (u->raw_downstream || u->body_downstream) { + if (ctx && ctx->writing_raw_req_socket) { + ctx->writing_raw_req_socket = 0; + if (r->connection->write->timer_set) { + ngx_del_timer(r->connection->write); + } + + r->connection->write->error = 1; + } + return; + } + + if (c) { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (c->write->active || c->write->disabled) { + ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); + } + +#if defined(nginx_version) && nginx_version >= 1007005 + if (c->write->posted) { +#else + if (c->write->prev) { +#endif + ngx_delete_posted_event(c->write); + } + + c->write->closed = 1; + } +} + + +static void +ngx_stream_lua_socket_tcp_conn_op_timeout_handler(ngx_event_t *ev) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_request_t *r; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_loc_conf_t *llcf; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = ev->data; + ngx_queue_remove(&conn_op_ctx->queue); + + u = conn_op_ctx->u; + r = u->request; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + /* note that we store conn_op_ctx in coctx->data instead of u */ + coctx->data = conn_op_ctx; + u->write_co_ctx = NULL; + + llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream lua tcp socket queued connect " + "timed out, when trying to connect to %V:%ud", + &conn_op_ctx->host, conn_op_ctx->port); + } + + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + u->socket_pool->connections--; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return; + } + + ctx->cur_co_ctx = coctx; + + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current " + "request"); + + u->write_prepare_retvals = + ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler; + + if (ctx->entered_content_phase) { + (void) ngx_stream_lua_socket_tcp_conn_op_resume(r); + + } else { + ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_op_resume; + ngx_stream_lua_core_run_phases(r); + } + +} + + +static int +ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, + lua_State *L) +{ + lua_pushnil(L); + lua_pushliteral(L, "timeout"); + return 2; +} + + +static void +ngx_stream_lua_socket_tcp_resume_conn_op( + ngx_stream_lua_socket_pool_t *spool) +{ + ngx_queue_t *q; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + +#if (NGX_DEBUG) + ngx_stream_lua_assert(spool->connections >= 0); + +#else + if (spool->connections < 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "stream lua tcp socket connections count " + "mismatched for connection pool \"%s\", connections: " + "%i, size: %i", + spool->key, spool->connections, spool->size); + spool->connections = 0; + } +#endif + + /* we manually destroy wait_connect_op before triggering connect + * operation resumption, so that there is no resumption happens when Nginx + * is exiting. + */ + if (ngx_queue_empty(&spool->wait_connect_op)) { return; } - c = u->peer.connection; + q = ngx_queue_head(&spool->wait_connect_op); + conn_op_ctx = ngx_queue_data(q, + ngx_stream_lua_socket_tcp_conn_op_ctx_t, + queue); + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket post connect operation " + "resumption u: %p, ctx: %p for connection pool \"%s\", " + "connections: %i", + conn_op_ctx->u, conn_op_ctx, spool->key, spool->connections); - if (c) { - if (c->read->timer_set) { - ngx_del_timer(c->read); - } + if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } - if (c->read->active || c->read->disabled) { - ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); - } + conn_op_ctx->event.handler = + ngx_stream_lua_socket_tcp_conn_op_resume_handler; -#if defined(nginx_version) && nginx_version >= 1007005 - if (c->read->posted) { -#else - if (c->read->prev) { -#endif - ngx_delete_posted_event(c->read); - } + ngx_post_event((&conn_op_ctx->event), &ngx_posted_events); +} - c->read->closed = 1; - /* TODO: shutdown the reading part of the connection */ - } +static void +ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup(void *data) +{ + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx = data; + + u = conn_op_ctx->u; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0, + "stream cleanup lua tcp socket " + "conn_op_ctx: %p, u: %p", + conn_op_ctx, u); + + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); } static void -ngx_stream_lua_socket_tcp_finalize_write_part(ngx_stream_lua_request_t *r, - ngx_stream_lua_socket_tcp_upstream_t *u, int do_shutdown) +ngx_stream_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev) { - ngx_connection_t *c; - ngx_stream_lua_ctx_t *ctx; + ngx_queue_t *q; + ngx_stream_lua_request_t *r; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_pool_t *spool; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - c = u->peer.connection; + conn_op_ctx = ev->data; + u = conn_op_ctx->u; + r = u->request; + spool = u->socket_pool; + + if (ngx_queue_empty(&spool->wait_connect_op)) { +#if (NGX_DEBUG) + ngx_stream_lua_assert(!(spool->backlog >= 0 + && spool->connections > spool->size)); + +#else + if (spool->backlog >= 0 && spool->connections > spool->size) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "stream lua tcp socket connections count " + "mismatched for connection pool \"%s\", connections: " + "%i, size: %i", + spool->key, spool->connections, spool->size); + spool->connections = spool->size; + } +#endif - if (u->write_closed) { return; } - u->write_closed = 1; + q = ngx_queue_head(&spool->wait_connect_op); + ngx_queue_remove(q); + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + /* note that we store conn_op_ctx in coctx->data instead of u */ + coctx->data = conn_op_ctx; + /* clear ngx_stream_lua_tcp_queue_conn_op_cleanup */ + u->write_co_ctx = NULL; ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + ngx_queue_insert_head(&spool->cache_connect_op, + &conn_op_ctx->queue); + return; + } - if (c && do_shutdown) { - if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { - ngx_connection_error(c, ngx_socket_errno, - ngx_shutdown_socket_n " failed"); - return; - } + ctx->cur_co_ctx = coctx; - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "stream lua shutdown socket write direction"); - } + ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx) + || coctx->co_ref >= 0)); - if (u->raw_downstream || u->body_downstream) { - if (ctx && ctx->writing_raw_req_socket) { - ctx->writing_raw_req_socket = 0; - if (r->connection->write->timer_set) { - ngx_del_timer(r->connection->write); - } + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua tcp socket waking up the current " + "request"); - r->connection->write->error = 1; + u->write_prepare_retvals = + ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler; + + if (ctx->entered_content_phase) { + (void) ngx_stream_lua_socket_tcp_conn_op_resume(r); + + } else { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln != NULL) { + cln->handler = ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup; + cln->data = conn_op_ctx; + conn_op_ctx->cleanup = &cln->handler; } - return; + + ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_op_resume; + ngx_stream_lua_core_run_phases(r); } - if (c) { - if (c->write->timer_set) { - ngx_del_timer(c->write); - } +} - if (c->write->active || c->write->disabled) { - ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); - } -#if defined(nginx_version) && nginx_version >= 1007005 - if (c->write->posted) { -#else - if (c->write->prev) { -#endif - ngx_delete_posted_event(c->write); - } +static int +ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + int nret; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - c->write->closed = 1; + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + if (ctx == NULL) { + return NGX_ERROR; } + + coctx = ctx->cur_co_ctx; + dd("coctx: %p", coctx); + conn_op_ctx = coctx->data; + if (conn_op_ctx->cleanup != NULL) { + *conn_op_ctx->cleanup = NULL; + ngx_stream_lua_cleanup_free(r, conn_op_ctx->cleanup); + conn_op_ctx->cleanup = NULL; + } + + /* decrease pending connect operation counter */ + u->socket_pool->connections--; + + nret = ngx_stream_lua_socket_tcp_connect_helper(L, u, r, ctx, + conn_op_ctx->host.data, + conn_op_ctx->host.len, + conn_op_ctx->port, 1); + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + + return nret; } @@ -3740,6 +4324,7 @@ ngx_stream_lua_socket_tcp_finalize(ngx_stream_lua_request_t *r, } ngx_stream_lua_socket_tcp_finalize_read_part(r, u); + ngx_stream_lua_socket_tcp_finalize_write_part(r, u, 0); if (u->raw_downstream || u->body_downstream) { @@ -3772,20 +4357,21 @@ ngx_stream_lua_socket_tcp_finalize(ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_close_connection(c); u->peer.connection = NULL; - if (!u->reused) { - return; - } + u->conn_closed = 1; spool = u->socket_pool; if (spool == NULL) { return; } - spool->active_connections--; + spool->connections--; - if (spool->active_connections == 0) { + if (spool->connections == 0) { ngx_stream_lua_socket_free_pool(r->connection->log, spool); + return; } + + ngx_stream_lua_socket_tcp_resume_conn_op(spool); } } @@ -4491,12 +5077,6 @@ ngx_stream_lua_req_socket_tcp(lua_State *L) c = r->connection; if (raw) { -#if !defined(nginx_version) || nginx_version < 1003013 - lua_pushnil(L); - lua_pushliteral(L, "nginx version too old"); - return 2; -#else - if (c->buffered) { lua_pushnil(L); @@ -4516,7 +5096,6 @@ ngx_stream_lua_req_socket_tcp(lua_State *L) ctx->acquired_raw_req_socket = 1; -#endif } else { } @@ -4664,21 +5243,19 @@ ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) ngx_stream_lua_loc_conf_t *llcf; ngx_stream_lua_socket_tcp_upstream_t *u; ngx_stream_lua_socket_pool_t *spool; - ngx_stream_lua_socket_pool_item_t *items, *item; + ngx_stream_lua_socket_pool_item_t *item; ngx_connection_t *c; - size_t size, key_len; ngx_str_t key; - ngx_uint_t i; ngx_queue_t *q; ngx_peer_connection_t *pc; - u_char *p; ngx_stream_lua_request_t *r; ngx_msec_t timeout; - ngx_uint_t pool_size; + ngx_int_t pool_size; int n; ngx_int_t rc; ngx_buf_t *b; + const char *msg; n = lua_gettop(L); @@ -4689,18 +5266,6 @@ ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) luaL_checktype(L, 1, LUA_TTABLE); - lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( - socket_pool_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - - lua_rawgeti(L, 1, SOCKET_KEY_INDEX); - key.data = (u_char *) lua_tolstring(L, -1, &key.len); - if (key.data == NULL) { - lua_pushnil(L); - lua_pushliteral(L, "key not found"); - return 2; - } - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); @@ -4711,7 +5276,7 @@ ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) return 2; } - /* stack: obj cache key */ + /* stack: obj timeout? size? */ pc = &u->peer; c = pc->connection; @@ -4763,10 +5328,34 @@ ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) return 2; } + if (ngx_terminate || ngx_exiting) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "stream lua tcp socket set keepalive while " + "process exiting, closing connection %p", c); + + ngx_stream_lua_socket_tcp_finalize(r, u); + lua_pushinteger(L, 1); + return 1; + } + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, "stream lua tcp socket set keepalive: saving " "connection %p", c); + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* stack: obj timeout? size? pools */ + + lua_rawgeti(L, 1, SOCKET_KEY_INDEX); + key.data = (u_char *) lua_tolstring(L, -1, &key.len); + if (key.data == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "key not found"); + return 2; + } + dd("saving connection to key %s", lua_tostring(L, -1)); lua_pushvalue(L, -1); @@ -4774,7 +5363,7 @@ ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) spool = lua_touserdata(L, -1); lua_pop(L, 1); - /* stack: obj timeout? size? cache key */ + /* stack: obj timeout? size? pools cache_key */ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module); @@ -4788,85 +5377,50 @@ ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) pool_size = llcf->pool_size; } - if (pool_size == 0) { - lua_pushnil(L); - lua_pushliteral(L, "zero pool size"); - return 2; - } - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "stream lua tcp socket connection pool size: %ui", - pool_size); - - key_len = ngx_align(key.len + 1, sizeof(void *)); - - size = sizeof(ngx_stream_lua_socket_pool_t) + key_len - 1 - + sizeof(ngx_stream_lua_socket_pool_item_t) - * pool_size; - - spool = lua_newuserdata(L, size); - if (spool == NULL) { - return luaL_error(L, "no memory"); + if (pool_size <= 0) { + msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", + pool_size); + return luaL_argerror(L, n, msg); } - lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( - pool_udata_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, - "stream lua tcp socket keepalive create " - "connection pool for key" " \"%s\"", - lua_tostring(L, -2)); - - lua_rawset(L, -3); - - spool->active_connections = 0; - spool->lua_vm = ngx_stream_lua_get_lua_vm(r, NULL); - - ngx_queue_init(&spool->cache); - ngx_queue_init(&spool->free); - - p = ngx_copy(spool->key, key.data, key.len); - *p++ = '\0'; - - items = (ngx_stream_lua_socket_pool_item_t *) (spool->key + key_len); - - dd("items: %p", items); - - ngx_stream_lua_assert((void *) items - == ngx_align_ptr(items, sizeof(void *))); - - for (i = 0; i < pool_size; i++) { - ngx_queue_insert_head(&spool->free, &items[i].queue); - items[i].socket_pool = spool; - } + ngx_stream_lua_socket_tcp_create_socket_pool(L, r, key, + pool_size, -1, + &spool); } if (ngx_queue_empty(&spool->free)) { q = ngx_queue_last(&spool->cache); ngx_queue_remove(q); - spool->active_connections--; item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue); ngx_stream_lua_socket_tcp_close_connection(item->connection); + /* only decrease the counter for connections which were counted */ + if (u->socket_pool != NULL) { + u->socket_pool->connections--; + } + } else { q = ngx_queue_head(&spool->free); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue); + + /* we should always increase connections after getting connected, + * and decrease connections after getting closed. + * however, we don't create connection pool in previous connect method. + * so we increase connections here for backward compatibility. + */ + if (u->socket_pool == NULL) { + spool->connections++; + } } item->connection = c; ngx_queue_insert_head(&spool->cache, q); - if (!u->reused) { - spool->active_connections++; - } - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, "stream lua tcp socket clear current socket connection"); @@ -4936,51 +5490,36 @@ ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L) ngx_stream_lua_socket_tcp_finalize(r, u); #endif + /* since we set u->peer->connection to NULL previously, the connect + * operation won't be resumed in the + * ngx_stream_lua_socket_tcp_finalize. + * Therefore we need to resume it here. + */ + ngx_stream_lua_socket_tcp_resume_conn_op(spool); + lua_pushinteger(L, 1); return 1; } static ngx_int_t -ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r, lua_State *L, - int key_index, ngx_stream_lua_socket_tcp_upstream_t *u) +ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r, + ngx_stream_lua_socket_tcp_upstream_t *u) { ngx_stream_lua_socket_pool_item_t *item; ngx_stream_lua_socket_pool_t *spool; ngx_stream_lua_cleanup_t *cln; ngx_queue_t *q; - int top; ngx_peer_connection_t *pc; ngx_connection_t *c; - top = lua_gettop(L); - - if (key_index < 0) { - key_index = top + key_index + 1; - } - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, "stream lua tcp socket pool get keepalive peer"); pc = &u->peer; - lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( - socket_pool_key)); - lua_rawget(L, LUA_REGISTRYINDEX); /* table */ - lua_pushvalue(L, key_index); /* key */ - lua_rawget(L, -2); - - spool = lua_touserdata(L, -1); - if (spool == NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, - "stream lua tcp socket keepalive connection " - "pool not found"); - lua_settop(L, top); - return NGX_DECLINED; - } - - u->socket_pool = spool; + spool = u->socket_pool; if (!ngx_queue_empty(&spool->cache)) { q = ngx_queue_head(&spool->cache); @@ -5025,7 +5564,6 @@ ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r, lua_State *L, cln = ngx_stream_lua_cleanup_add(r, 0); if (cln == NULL) { u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR; - lua_settop(L, top); return NGX_ERROR; } @@ -5034,16 +5572,12 @@ ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r, lua_State *L, u->cleanup = &cln->handler; } - lua_settop(L, top); - return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, "stream lua tcp socket keepalive: connection pool empty"); - lua_settop(L, top); - return NGX_DECLINED; } @@ -5052,7 +5586,7 @@ static void ngx_stream_lua_socket_keepalive_dummy_handler(ngx_event_t *ev) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ev->log, 0, - "keepalive dummy handler"); + "stream keepalive dummy handler"); } @@ -5117,13 +5651,15 @@ ngx_stream_lua_socket_keepalive_close_handler(ngx_event_t *ev) ngx_queue_remove(&item->queue); ngx_queue_insert_head(&spool->free, &item->queue); - spool->active_connections--; + spool->connections--; - dd("keepalive: active connections: %u", - (unsigned) spool->active_connections); + dd("keepalive: connections: %u", (unsigned) spool->connections); - if (spool->active_connections == 0) { + if (spool->connections == 0) { ngx_stream_lua_socket_free_pool(ev->log, spool); + + } else { + ngx_stream_lua_socket_tcp_resume_conn_op(spool); } return NGX_DECLINED; @@ -5137,8 +5673,8 @@ ngx_stream_lua_socket_free_pool(ngx_log_t *log, lua_State *L; ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, - "stream lua tcp socket keepalive: free connection pool " - "for \"%s\"", spool->key); + "stream lua tcp socket keepalive: free " + "connection pool for \"%s\"", spool->key); L = spool->lua_vm; @@ -5153,11 +5689,13 @@ ngx_stream_lua_socket_free_pool(ngx_log_t *log, static void -ngx_stream_lua_socket_shutdown_pool_helper(ngx_stream_lua_socket_pool_t *spool) +ngx_stream_lua_socket_shutdown_pool_helper( + ngx_stream_lua_socket_pool_t *spool) { - ngx_queue_t *q; - ngx_connection_t *c; - ngx_stream_lua_socket_pool_item_t *item; + ngx_queue_t *q; + ngx_connection_t *c; + ngx_stream_lua_socket_pool_item_t *item; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; while (!ngx_queue_empty(&spool->cache)) { q = ngx_queue_head(&spool->cache); @@ -5171,7 +5709,29 @@ ngx_stream_lua_socket_shutdown_pool_helper(ngx_stream_lua_socket_pool_t *spool) ngx_queue_insert_head(&spool->free, q); } - spool->active_connections = 0; + while (!ngx_queue_empty(&spool->cache_connect_op)) { + q = ngx_queue_head(&spool->cache_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data(q, ngx_stream_lua_socket_tcp_conn_op_ctx_t, + queue); + ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); + } + + while (!ngx_queue_empty(&spool->wait_connect_op)) { + q = ngx_queue_head(&spool->wait_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data(q, ngx_stream_lua_socket_tcp_conn_op_ctx_t, + queue); + + if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); + } + + /* spool->connections will be decreased down to zero in + * ngx_stream_lua_socket_tcp_finalize */ } @@ -5427,6 +5987,14 @@ static ngx_int_t ngx_stream_lua_socket_insert_buffer( } +static ngx_int_t +ngx_stream_lua_socket_tcp_conn_op_resume(ngx_stream_lua_request_t *r) +{ + return ngx_stream_lua_socket_tcp_resume_helper(r, + SOCKET_OP_RESUME_CONN); +} + + static ngx_int_t ngx_stream_lua_socket_tcp_conn_resume(ngx_stream_lua_request_t *r) { @@ -5457,11 +6025,12 @@ ngx_stream_lua_socket_tcp_resume_helper(ngx_stream_lua_request_t *r, ngx_int_t rc; ngx_uint_t nreqs; ngx_connection_t *c; - ngx_stream_lua_ctx_t *ctx; - ngx_stream_lua_co_ctx_t *coctx; - ngx_stream_lua_socket_tcp_retval_handler prepare_retvals; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_co_ctx_t *coctx; ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + ngx_stream_lua_socket_tcp_retval_handler prepare_retvals; ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); if (ctx == NULL) { @@ -5471,21 +6040,29 @@ ngx_stream_lua_socket_tcp_resume_helper(ngx_stream_lua_request_t *r, ctx->resume_handler = ngx_stream_lua_wev_handler; ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua tcp operation done, resuming lua thread"); + "stream lua tcp operation done, resuming lua " + "thread"); coctx = ctx->cur_co_ctx; dd("coctx: %p", coctx); - u = coctx->data; - switch (socket_op) { + + case SOCKET_OP_RESUME_CONN: + conn_op_ctx = coctx->data; + u = conn_op_ctx->u; + prepare_retvals = u->write_prepare_retvals; + break; + case SOCKET_OP_CONNECT: case SOCKET_OP_WRITE: + u = coctx->data; prepare_retvals = u->write_prepare_retvals; break; case SOCKET_OP_READ: + u = coctx->data; prepare_retvals = u->read_prepare_retvals; break; @@ -5499,6 +6076,15 @@ ngx_stream_lua_socket_tcp_resume_helper(ngx_stream_lua_request_t *r, "u:%p", prepare_retvals, u); nret = prepare_retvals(r, u, ctx->cur_co_ctx->co); + if (socket_op == SOCKET_OP_CONNECT + && nret > 1 + && !u->conn_closed + && u->socket_pool != NULL) + { + u->socket_pool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool); + } + if (nret == NGX_AGAIN) { return NGX_DONE; } @@ -5510,7 +6096,7 @@ ngx_stream_lua_socket_tcp_resume_helper(ngx_stream_lua_request_t *r, rc = ngx_stream_lua_run_thread(vm, r, ctx, nret); ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "lua run thread returned %d", rc); + "stream lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs); @@ -5530,6 +6116,37 @@ ngx_stream_lua_socket_tcp_resume_helper(ngx_stream_lua_request_t *r, } +static void +ngx_stream_lua_tcp_queue_conn_op_cleanup(void *data) +{ + ngx_stream_lua_co_ctx_t *coctx = data; + ngx_stream_lua_socket_tcp_upstream_t *u; + ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = coctx->data; + u = conn_op_ctx->u; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream lua tcp socket abort queueing, " + "conn_op_ctx: %p, u: %p", + conn_op_ctx, u); + + if (conn_op_ctx->event.posted) { + ngx_delete_posted_event(&conn_op_ctx->event); + + } else if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + ngx_queue_remove(&conn_op_ctx->queue); + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + + u->socket_pool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool); +} + + static void ngx_stream_lua_tcp_resolve_cleanup(void *data) { @@ -5545,6 +6162,11 @@ ngx_stream_lua_tcp_resolve_cleanup(void *data) return; } + if (u->socket_pool != NULL) { + u->socket_pool->connections--; + ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool); + } + rctx = u->resolved->ctx; if (rctx == NULL) { return; diff --git a/src/ngx_stream_lua_socket_tcp.h b/src/ngx_stream_lua_socket_tcp.h index 629290e0..3473250a 100644 --- a/src/ngx_stream_lua_socket_tcp.h +++ b/src/ngx_stream_lua_socket_tcp.h @@ -43,17 +43,39 @@ typedef void (*ngx_stream_lua_socket_tcp_upstream_handler_pt) (ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u); +typedef struct { + ngx_event_t event; + ngx_queue_t queue; + ngx_str_t host; + ngx_stream_lua_cleanup_pt *cleanup; + ngx_stream_lua_socket_tcp_upstream_t *u; + in_port_t port; +} ngx_stream_lua_socket_tcp_conn_op_ctx_t; + + +#define ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx) \ + ngx_free(conn_op_ctx->host.data); \ + ngx_free(conn_op_ctx) + + typedef struct { lua_State *lua_vm; - /* active connections == out-of-pool reused connections - * + in-pool connections */ - ngx_uint_t active_connections; + ngx_int_t size; + ngx_queue_t cache_connect_op; + ngx_queue_t wait_connect_op; + + /* connections == active connections + pending connect operations, + * while active connections == out-of-pool reused connections + * + in-pool connections */ + ngx_int_t connections; /* queues of ngx_stream_lua_socket_pool_item_t: */ ngx_queue_t cache; ngx_queue_t free; + ngx_int_t backlog; + u_char key[1]; } ngx_stream_lua_socket_pool_t; @@ -113,6 +135,7 @@ struct ngx_stream_lua_socket_tcp_upstream_s { unsigned raw_downstream:1; unsigned read_closed:1; unsigned write_closed:1; + unsigned conn_closed:1; unsigned read_consumed:1; #if (NGX_STREAM_SSL) unsigned ssl_verify:1; diff --git a/src/ngx_stream_lua_socket_udp.c b/src/ngx_stream_lua_socket_udp.c index 0e482168..a9cecbfd 100644 --- a/src/ngx_stream_lua_socket_udp.c +++ b/src/ngx_stream_lua_socket_udp.c @@ -411,9 +411,6 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L) } rctx->name = host; -#if !defined(nginx_version) || nginx_version < 1005008 - rctx->type = NGX_RESOLVE_A; -#endif rctx->handler = ngx_stream_lua_socket_resolve_handler; rctx->data = u; rctx->timeout = clcf->resolver_timeout; @@ -480,12 +477,8 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) lua_State *L; u_char *p; size_t len; -#if defined(nginx_version) && nginx_version >= 1005008 socklen_t socklen; struct sockaddr *sockaddr; -#else - struct sockaddr_in *sin; -#endif ngx_uint_t i; unsigned waiting; @@ -552,36 +545,19 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) #if (NGX_DEBUG) { -# if defined(nginx_version) && nginx_version >= 1005008 - u_char text[NGX_SOCKADDR_STRLEN]; - ngx_str_t addr; -# else - in_addr_t addr; -# endif - ngx_uint_t i; - -# if defined(nginx_version) && nginx_version >= 1005008 - addr.data = text; + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + ngx_uint_t i; - for (i = 0; i < ctx->naddrs; i++) { - addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, - text, NGX_SOCKADDR_STRLEN, 0); - - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, - "name was resolved to %V", &addr); - } -# else - for (i = 0; i < ctx->naddrs; i++) { - dd("addr i: %d %p", (int) i, &ctx->addrs[i]); + addr.data = text; - addr = ntohl(ctx->addrs[i]); + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); - ngx_log_debug4(NGX_LOG_DEBUG_STREAM, c->log, 0, - "name was resolved to %ud.%ud.%ud.%ud", - (addr >> 24) & 0xff, (addr >> 16) & 0xff, - (addr >> 8) & 0xff, addr & 0xff); - } -# endif + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "name was resolved to %V", &addr); + } } #endif @@ -596,7 +572,6 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) dd("selected addr index: %d", (int) i); -#if defined(nginx_version) && nginx_version >= 1005008 socklen = ur->addrs[i].socklen; sockaddr = ngx_palloc(r->pool, socklen); @@ -624,31 +599,6 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); ur->sockaddr = sockaddr; ur->socklen = socklen; - -#else - /* for nginx older than 1.5.8 */ - - len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; - - p = ngx_pnalloc(r->pool, len + sizeof(struct sockaddr_in)); - if (p == NULL) { - goto nomem; - } - - sin = (struct sockaddr_in *) &p[len]; - ngx_memzero(sin, sizeof(struct sockaddr_in)); - - len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); - len = ngx_sprintf(&p[len], ":%d", ur->port) - p; - - sin->sin_family = AF_INET; - sin->sin_port = htons(ur->port); - sin->sin_addr.s_addr = ur->addrs[i]; - - ur->sockaddr = (struct sockaddr *) sin; - ur->socklen = sizeof(struct sockaddr_in); -#endif - ur->host.data = p; ur->host.len = len; ur->naddrs = 1; @@ -812,11 +762,7 @@ ngx_stream_lua_socket_error_retval_handler(ngx_stream_lua_request_t *r, } else { if (u->socket_errno) { -#if defined(nginx_version) && nginx_version >= 9000 p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); -#else - p = ngx_strerror_r(u->socket_errno, errstr, sizeof(errstr)); -#endif /* for compatibility with LuaSocket */ ngx_strlow(errstr, errstr, p - errstr); lua_pushlstring(L, (char *) errstr, p - errstr); @@ -899,6 +845,20 @@ ngx_stream_lua_socket_udp_send(lua_State *L) len = ngx_stream_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); break; + case LUA_TNIL: + len = sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + len = sizeof("true") - 1; + + } else { + len = sizeof("false") - 1; + } + + break; + default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "or array table expected, got %s", @@ -921,6 +881,32 @@ ngx_stream_lua_socket_udp_send(lua_State *L) (void) ngx_stream_lua_copy_str_in_table(L, 2, query.data); break; + case LUA_TNIL: + p = query.data; + *p++ = 'n'; + *p++ = 'i'; + *p++ = 'l'; + break; + + case LUA_TBOOLEAN: + p = query.data; + + if (lua_toboolean(L, 2)) { + *p++ = 't'; + *p++ = 'r'; + *p++ = 'u'; + *p++ = 'e'; + + } else { + *p++ = 'f'; + *p++ = 'a'; + *p++ = 'l'; + *p++ = 's'; + *p++ = 'e'; + } + + break; + default: return luaL_error(L, "impossible to reach here"); } @@ -937,7 +923,7 @@ ngx_stream_lua_socket_udp_send(lua_State *L) n = ngx_udp_send(u->udp_connection.connection, query.data, query.len); - dd("ngx_send returns %d (query len %d)", (int) n, (int) query.len); + dd("ngx_udp_send returns %d (query len %d)", (int) n, (int) query.len); if (n == NGX_ERROR || n == NGX_AGAIN) { u->socket_errno = ngx_socket_errno; @@ -1175,6 +1161,7 @@ ngx_stream_lua_socket_udp_finalize(ngx_stream_lua_request_t *r, * be handled by stream subsystem itself */ if (u->udp_connection.connection && !u->raw_downstream) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, "lua close socket connection"); diff --git a/src/ngx_stream_lua_socket_udp.h b/src/ngx_stream_lua_socket_udp.h index 79481ae4..abcc76d3 100644 --- a/src/ngx_stream_lua_socket_udp.h +++ b/src/ngx_stream_lua_socket_udp.h @@ -62,6 +62,7 @@ struct ngx_stream_lua_socket_udp_upstream_s { ngx_stream_lua_co_ctx_t *co_ctx; unsigned waiting:1; + unsigned raw_downstream:1; }; diff --git a/src/ngx_stream_lua_ssl_certby.c b/src/ngx_stream_lua_ssl_certby.c index 72020393..13fb3663 100644 --- a/src/ngx_stream_lua_ssl_certby.c +++ b/src/ngx_stream_lua_ssl_certby.c @@ -267,22 +267,11 @@ ngx_stream_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data) cscf = ngx_stream_get_module_srv_conf(fs, ngx_stream_core_module); -#if defined(nginx_version) && nginx_version >= 1003014 - -# if nginx_version >= 1009000 - +#if defined(nginx_version) && nginx_version >= 1009000 ngx_set_connection_log(fc, cscf->error_log); -# else - -# error "stream ssl_cert_by_lua only supports nginx >= 1.13.0" - -# endif - #else - # error "stream ssl_cert_by_lua only supports nginx >= 1.13.0" - #endif if (cctx == NULL) { @@ -496,7 +485,7 @@ ngx_stream_lua_ssl_cert_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) if (co == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "stream lua: failed to create new" + "stream failed to create new" " coroutine to handle request"); rc = NGX_ERROR; @@ -559,7 +548,6 @@ ngx_stream_lua_ssl_cert_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) } - int ngx_stream_lua_ffi_ssl_get_tls1_version(ngx_stream_lua_request_t *r, char **err) { diff --git a/src/ngx_stream_lua_string.c b/src/ngx_stream_lua_string.c index 31f54a49..eb7f0e7d 100644 --- a/src/ngx_stream_lua_string.c +++ b/src/ngx_stream_lua_string.c @@ -35,30 +35,13 @@ #endif -#ifndef SHA_DIGEST_LENGTH -#define SHA_DIGEST_LENGTH 20 -#endif - - -static uintptr_t ngx_stream_lua_ngx_escape_sql_str(u_char *dst, u_char *src, - size_t size); -static int ngx_stream_lua_ngx_escape_uri(lua_State *L); -static int ngx_stream_lua_ngx_unescape_uri(lua_State *L); +static uintptr_t ngx_stream_lua_ngx_escape_sql_str(u_char *dst, + u_char *src, size_t size); static int ngx_stream_lua_ngx_quote_sql_str(lua_State *L); -static int ngx_stream_lua_ngx_md5(lua_State *L); -static int ngx_stream_lua_ngx_md5_bin(lua_State *L); - -#if (NGX_HAVE_SHA1) -static int ngx_stream_lua_ngx_sha1_bin(lua_State *L); -#endif - -static int ngx_stream_lua_ngx_decode_base64(lua_State *L); -static int ngx_stream_lua_ngx_encode_base64(lua_State *L); static int ngx_stream_lua_ngx_crc32_short(lua_State *L); static int ngx_stream_lua_ngx_crc32_long(lua_State *L); static int ngx_stream_lua_ngx_encode_args(lua_State *L); static int ngx_stream_lua_ngx_decode_args(lua_State *L); - #if (NGX_OPENSSL) static int ngx_stream_lua_ngx_hmac_sha1(lua_State *L); #endif @@ -67,12 +50,6 @@ static int ngx_stream_lua_ngx_hmac_sha1(lua_State *L); void ngx_stream_lua_inject_string_api(lua_State *L) { - lua_pushcfunction(L, ngx_stream_lua_ngx_escape_uri); - lua_setfield(L, -2, "escape_uri"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_unescape_uri); - lua_setfield(L, -2, "unescape_uri"); - lua_pushcfunction(L, ngx_stream_lua_ngx_encode_args); lua_setfield(L, -2, "encode_args"); @@ -82,23 +59,6 @@ ngx_stream_lua_inject_string_api(lua_State *L) lua_pushcfunction(L, ngx_stream_lua_ngx_quote_sql_str); lua_setfield(L, -2, "quote_sql_str"); - lua_pushcfunction(L, ngx_stream_lua_ngx_decode_base64); - lua_setfield(L, -2, "decode_base64"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_encode_base64); - lua_setfield(L, -2, "encode_base64"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_md5_bin); - lua_setfield(L, -2, "md5_bin"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_md5); - lua_setfield(L, -2, "md5"); - -#if (NGX_HAVE_SHA1) - lua_pushcfunction(L, ngx_stream_lua_ngx_sha1_bin); - lua_setfield(L, -2, "sha1_bin"); -#endif - lua_pushcfunction(L, ngx_stream_lua_ngx_crc32_short); lua_setfield(L, -2, "crc32_short"); @@ -112,75 +72,6 @@ ngx_stream_lua_inject_string_api(lua_State *L) } -static int -ngx_stream_lua_ngx_escape_uri(lua_State *L) -{ - size_t len, dlen; - uintptr_t escape; - u_char *src, *dst; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one argument"); - } - - if (lua_isnil(L, 1)) { - lua_pushliteral(L, ""); - return 1; - } - - src = (u_char *) luaL_checklstring(L, 1, &len); - - if (len == 0) { - return 1; - } - - escape = 2 * ngx_stream_lua_escape_uri(NULL, src, len, - NGX_ESCAPE_URI_COMPONENT); - - if (escape) { - dlen = escape + len; - dst = lua_newuserdata(L, dlen); - ngx_stream_lua_escape_uri(dst, src, len, NGX_ESCAPE_URI_COMPONENT); - lua_pushlstring(L, (char *) dst, dlen); - } - - return 1; -} - - -static int -ngx_stream_lua_ngx_unescape_uri(lua_State *L) -{ - size_t len, dlen; - u_char *p; - u_char *src, *dst; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one argument"); - } - - if (lua_isnil(L, 1)) { - lua_pushliteral(L, ""); - return 1; - } - - src = (u_char *) luaL_checklstring(L, 1, &len); - - /* the unescaped string can only be smaller */ - dlen = len; - - p = lua_newuserdata(L, dlen); - - dst = p; - - ngx_stream_lua_unescape_uri(&dst, &src, len, NGX_UNESCAPE_URI_COMPONENT); - - lua_pushlstring(L, (char *) p, dst - p); - - return 1; -} - - static int ngx_stream_lua_ngx_quote_sql_str(lua_State *L) { @@ -328,138 +219,6 @@ ngx_stream_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size) } -static int -ngx_stream_lua_ngx_md5(lua_State *L) -{ - u_char *src; - size_t slen; - - ngx_md5_t md5; - u_char md5_buf[MD5_DIGEST_LENGTH]; - u_char hex_buf[2 * sizeof(md5_buf)]; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one argument"); - } - - if (lua_isnil(L, 1)) { - src = (u_char *) ""; - slen = 0; - - } else { - src = (u_char *) luaL_checklstring(L, 1, &slen); - } - - ngx_md5_init(&md5); - ngx_md5_update(&md5, src, slen); - ngx_md5_final(md5_buf, &md5); - - ngx_hex_dump(hex_buf, md5_buf, sizeof(md5_buf)); - - lua_pushlstring(L, (char *) hex_buf, sizeof(hex_buf)); - - return 1; -} - - -static int -ngx_stream_lua_ngx_md5_bin(lua_State *L) -{ - u_char *src; - size_t slen; - - ngx_md5_t md5; - u_char md5_buf[MD5_DIGEST_LENGTH]; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one argument"); - } - - if (lua_isnil(L, 1)) { - src = (u_char *) ""; - slen = 0; - - } else { - src = (u_char *) luaL_checklstring(L, 1, &slen); - } - - dd("slen: %d", (int) slen); - - ngx_md5_init(&md5); - ngx_md5_update(&md5, src, slen); - ngx_md5_final(md5_buf, &md5); - - lua_pushlstring(L, (char *) md5_buf, sizeof(md5_buf)); - - return 1; -} - - -#if (NGX_HAVE_SHA1) -static int -ngx_stream_lua_ngx_sha1_bin(lua_State *L) -{ - u_char *src; - size_t slen; - - ngx_sha1_t sha; - u_char sha_buf[SHA_DIGEST_LENGTH]; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one argument"); - } - - if (lua_isnil(L, 1)) { - src = (u_char *) ""; - slen = 0; - - } else { - src = (u_char *) luaL_checklstring(L, 1, &slen); - } - - dd("slen: %d", (int) slen); - - ngx_sha1_init(&sha); - ngx_sha1_update(&sha, src, slen); - ngx_sha1_final(sha_buf, &sha); - - lua_pushlstring(L, (char *) sha_buf, sizeof(sha_buf)); - - return 1; -} -#endif - - -static int -ngx_stream_lua_ngx_decode_base64(lua_State *L) -{ - ngx_str_t p, src; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one argument"); - } - - if (lua_type(L, 1) != LUA_TSTRING) { - return luaL_error(L, "string argument only"); - } - - src.data = (u_char *) luaL_checklstring(L, 1, &src.len); - - p.len = ngx_base64_decoded_length(src.len); - - p.data = lua_newuserdata(L, p.len); - - if (ngx_decode_base64(&p, &src) == NGX_OK) { - lua_pushlstring(L, (char *) p.data, p.len); - - } else { - lua_pushnil(L); - } - - return 1; -} - - static void ngx_stream_lua_encode_base64(ngx_str_t *dst, ngx_str_t *src, int no_padding) { @@ -505,51 +264,6 @@ ngx_stream_lua_encode_base64(ngx_str_t *dst, ngx_str_t *src, int no_padding) } -static size_t -ngx_stream_lua_base64_encoded_length(size_t n, int no_padding) -{ - return no_padding ? (n * 8 + 5) / 6 : ngx_base64_encoded_length(n); -} - - -static int -ngx_stream_lua_ngx_encode_base64(lua_State *L) -{ - int n; - int no_padding = 0; - ngx_str_t p, src; - - n = lua_gettop(L); - if (n != 1 && n != 2) { - return luaL_error(L, "expecting one or two arguments"); - } - - if (lua_isnil(L, 1)) { - src.data = (u_char *) ""; - src.len = 0; - - } else { - src.data = (u_char *) luaL_checklstring(L, 1, &src.len); - } - - if (n == 2) { - /* get the 2nd optional argument */ - luaL_checktype(L, 2, LUA_TBOOLEAN); - no_padding = lua_toboolean(L, 2); - } - - p.len = ngx_stream_lua_base64_encoded_length(src.len, no_padding); - - p.data = lua_newuserdata(L, p.len); - - ngx_stream_lua_encode_base64(&p, &src, no_padding); - - lua_pushlstring(L, (char *) p.data, p.len); - - return 1; -} - - static int ngx_stream_lua_ngx_crc32_short(lua_State *L) { @@ -666,7 +380,6 @@ ngx_stream_lua_ngx_hmac_sha1(lua_State *L) #endif -#ifndef NGX_LUA_NO_FFI_API void ngx_stream_lua_ffi_md5_bin(const u_char *src, size_t len, u_char *dst) { @@ -772,6 +485,7 @@ ngx_stream_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst) NGX_ESCAPE_URI_COMPONENT); } -#endif + + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_time.c b/src/ngx_stream_lua_time.c index 979d6aff..e4b69b38 100644 --- a/src/ngx_stream_lua_time.c +++ b/src/ngx_stream_lua_time.c @@ -19,163 +19,7 @@ #include "ddebug.h" -#include "ngx_stream_lua_time.h" -#include "ngx_stream_lua_util.h" - - -static int ngx_stream_lua_ngx_today(lua_State *L); -static int ngx_stream_lua_ngx_time(lua_State *L); -static int ngx_stream_lua_ngx_now(lua_State *L); -static int ngx_stream_lua_ngx_localtime(lua_State *L); -static int ngx_stream_lua_ngx_utctime(lua_State *L); -static int ngx_stream_lua_ngx_update_time(lua_State *L); -static int ngx_stream_lua_ngx_req_start_time(lua_State *L); - - -static int -ngx_stream_lua_ngx_today(lua_State *L) -{ - time_t now; - ngx_tm_t tm; - u_char buf[sizeof("2010-11-19") - 1]; - - now = ngx_time(); - ngx_gmtime(now + ngx_cached_time->gmtoff * 60, &tm); - - ngx_sprintf(buf, "%04d-%02d-%02d", tm.ngx_tm_year, tm.ngx_tm_mon, - tm.ngx_tm_mday); - - lua_pushlstring(L, (char *) buf, sizeof(buf)); - - return 1; -} - - -static int -ngx_stream_lua_ngx_localtime(lua_State *L) -{ - ngx_tm_t tm; - - u_char buf[sizeof("2010-11-19 20:56:31") - 1]; - - ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm); - - ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year, - tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, - tm.ngx_tm_sec); - - lua_pushlstring(L, (char *) buf, sizeof(buf)); - - return 1; -} - - -static int -ngx_stream_lua_ngx_time(lua_State *L) -{ - lua_pushnumber(L, (lua_Number) ngx_time()); - - return 1; -} - - -static int -ngx_stream_lua_ngx_now(lua_State *L) -{ - ngx_time_t *tp; - - tp = ngx_timeofday(); - - lua_pushnumber(L, (lua_Number) (tp->sec + tp->msec / 1000.0L)); - - return 1; -} - - -static int -ngx_stream_lua_ngx_update_time(lua_State *L) -{ - ngx_time_update(); - return 0; -} - - -static int -ngx_stream_lua_ngx_utctime(lua_State *L) -{ - ngx_tm_t tm; - u_char buf[sizeof("2010-11-19 20:56:31") - 1]; - - ngx_gmtime(ngx_time(), &tm); - - ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year, - tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, - tm.ngx_tm_sec); - - lua_pushlstring(L, (char *) buf, sizeof(buf)); - - return 1; -} - - - - -static int -ngx_stream_lua_ngx_req_start_time(lua_State *L) -{ - ngx_stream_lua_request_t *r; - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - lua_pushnumber(L, (lua_Number) (r->session->start_sec - + r->session->start_msec / 1000.0L)); - - return 1; -} - - -void -ngx_stream_lua_inject_time_api(lua_State *L) -{ - lua_pushcfunction(L, ngx_stream_lua_ngx_utctime); - lua_setfield(L, -2, "utctime"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_time); - lua_setfield(L, -2, "get_now_ts"); /* deprecated */ - - lua_pushcfunction(L, ngx_stream_lua_ngx_localtime); - lua_setfield(L, -2, "get_now"); /* deprecated */ - - lua_pushcfunction(L, ngx_stream_lua_ngx_localtime); - lua_setfield(L, -2, "localtime"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_time); - lua_setfield(L, -2, "time"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_now); - lua_setfield(L, -2, "now"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_update_time); - lua_setfield(L, -2, "update_time"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_today); - lua_setfield(L, -2, "get_today"); /* deprecated */ - - lua_pushcfunction(L, ngx_stream_lua_ngx_today); - lua_setfield(L, -2, "today"); - -} - - -void -ngx_stream_lua_inject_req_time_api(lua_State *L) -{ - lua_pushcfunction(L, ngx_stream_lua_ngx_req_start_time); - lua_setfield(L, -2, "start_time"); -} +#include "ngx_stream_lua_common.h" double @@ -248,4 +92,6 @@ ngx_stream_lua_ffi_utctime(u_char *buf) } + + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_time.h b/src/ngx_stream_lua_time.h deleted file mode 100644 index 41892cc1..00000000 --- a/src/ngx_stream_lua_time.h +++ /dev/null @@ -1,29 +0,0 @@ - -/* - * !!! DO NOT EDIT DIRECTLY !!! - * This file was automatically generated from the following template: - * - * src/subsys/ngx_subsys_lua_time.h.tt2 - */ - - -/* - * Copyright (C) Xiaozhe Wang (chaoslawful) - * Copyright (C) Yichun Zhang (agentzh) - */ - - -#ifndef _NGX_STREAM_LUA_TIME_H_INCLUDED_ -#define _NGX_STREAM_LUA_TIME_H_INCLUDED_ - - -#include "ngx_stream_lua_common.h" - - -void ngx_stream_lua_inject_time_api(lua_State *L); -void ngx_stream_lua_inject_req_time_api(lua_State *L); - - -#endif /* _NGX_STREAM_LUA_TIME_H_INCLUDED_ */ - -/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_timer.c b/src/ngx_stream_lua_timer.c index d8a13eb8..00ba37fb 100644 --- a/src/ngx_stream_lua_timer.c +++ b/src/ngx_stream_lua_timer.c @@ -24,6 +24,9 @@ #include "ngx_stream_lua_probe.h" +#define NGX_STREAM_LUA_TIMER_ERRBUF_SIZE 128 + + typedef struct { void **main_conf; void **srv_conf; @@ -361,6 +364,10 @@ ngx_stream_lua_ngx_timer_helper(lua_State *L, int every) ngx_add_timer(ev, delay); + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream created timer (co: %p delay: %M ms)", + tctx->co, delay); + lua_pushinteger(L, 1); return 1; @@ -521,6 +528,10 @@ ngx_stream_lua_timer_copy(ngx_stream_lua_timer_ctx_t *old_tctx) ngx_add_timer(ev, tctx->delay); + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, + "stream created next timer (co: %p delay: %M ms)", + tctx->co, tctx->delay); + return NGX_OK; nomem: @@ -555,23 +566,31 @@ ngx_stream_lua_timer_handler(ngx_event_t *ev) lua_State *L; ngx_int_t rc; ngx_connection_t *c = NULL; - ngx_stream_lua_request_t *r = NULL; - ngx_stream_lua_ctx_t *ctx; - ngx_stream_lua_cleanup_t *cln; ngx_pool_cleanup_t *pcln; + ngx_stream_lua_request_t *r = NULL; + ngx_stream_lua_cleanup_t *cln; + ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_timer_ctx_t tctx; ngx_stream_lua_main_conf_t *lmcf; ngx_stream_core_srv_conf_t *clcf; ngx_stream_session_t *s; + lua_Debug ar; + u_char *p; + u_char errbuf[NGX_STREAM_LUA_TIMER_ERRBUF_SIZE]; + const char *source; + const char *errmsg; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0, "stream lua ngx.timer expired"); ngx_memcpy(&tctx, ev->data, sizeof(ngx_stream_lua_timer_ctx_t)); ngx_free(ev); + ngx_stream_lua_assert(tctx.co_ref && tctx.co); + lmcf = tctx.lmcf; lmcf->pending_timers--; @@ -586,14 +605,17 @@ ngx_stream_lua_timer_handler(ngx_event_t *ev) } if (lmcf->running_timers >= lmcf->max_running_timers) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "stream lua: %i lua_max_running_timers are not enough", - lmcf->max_running_timers); + p = ngx_snprintf(errbuf, NGX_STREAM_LUA_TIMER_ERRBUF_SIZE - 1, + "stream lua: %i lua_max_running_timers are not enough", + lmcf->max_running_timers); + *p = '\0'; + errmsg = (const char *) errbuf; goto failed; } c = ngx_stream_lua_create_fake_connection(tctx.pool); if (c == NULL) { + errmsg = "could not create fake connection"; goto failed; } @@ -605,6 +627,7 @@ ngx_stream_lua_timer_handler(ngx_event_t *ev) s = ngx_stream_lua_create_fake_session(c); if (s == NULL) { + errmsg = "could not create fake session"; goto failed; } @@ -614,31 +637,17 @@ ngx_stream_lua_timer_handler(ngx_event_t *ev) clcf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); -#if defined(nginx_version) && nginx_version >= 1003014 - -# if nginx_version >= 1009000 - +#if defined(nginx_version) && nginx_version >= 1009000 ngx_set_connection_log(s->connection, clcf->error_log); -# else - - -# endif - #else - - c->log->file = clcf->error_log->file; - - if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - c->log->log_level = clcf->error_log->log_level; - } - #endif dd("lmcf: %p", lmcf); ctx = ngx_stream_lua_create_ctx(s); if (ctx == NULL) { + errmsg = "could not create ctx"; goto failed; } @@ -649,6 +658,7 @@ ngx_stream_lua_timer_handler(ngx_event_t *ev) pcln = ngx_pool_cleanup_add(r->pool, 0); if (pcln == NULL) { + errmsg = "could not add vm cleanup"; goto failed; } @@ -662,6 +672,7 @@ ngx_stream_lua_timer_handler(ngx_event_t *ev) cln = ngx_stream_lua_cleanup_add(r, 0); if (cln == NULL) { + errmsg = "could not add request cleanup"; goto failed; } @@ -718,14 +729,26 @@ ngx_stream_lua_timer_handler(ngx_event_t *ev) failed: - if (tctx.co_ref && tctx.co) { - lua_pushlightuserdata(tctx.co, ngx_stream_lua_lightudata_mask( - coroutines_key)); - lua_rawget(tctx.co, LUA_REGISTRYINDEX); - luaL_unref(tctx.co, -1, tctx.co_ref); - lua_settop(tctx.co, 0); + /* co stack: func [args] */ + lua_getinfo(tctx.co, ">Sf", &ar); + + source = ar.source; + + if (source == NULL) { + source = "(unknown)"; } + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "stream lua failed to run timer with function " + "defined at %s:%d: %s", + source, ar.linedefined, errmsg); + + lua_pushlightuserdata(tctx.co, ngx_stream_lua_lightudata_mask( + coroutines_key)); + lua_rawget(tctx.co, LUA_REGISTRYINDEX); + luaL_unref(tctx.co, -1, tctx.co_ref); + lua_settop(tctx.co, 0); + if (tctx.vm_state) { ngx_stream_lua_cleanup_vm(tctx.vm_state); } diff --git a/src/ngx_stream_lua_util.c b/src/ngx_stream_lua_util.c index da242276..d4dd7792 100644 --- a/src/ngx_stream_lua_util.c +++ b/src/ngx_stream_lua_util.c @@ -24,13 +24,10 @@ #include "ngx_stream_lua_util.h" #include "ngx_stream_lua_exception.h" #include "ngx_stream_lua_pcrefix.h" -#include "ngx_stream_lua_regex.h" #include "ngx_stream_lua_args.h" #include "ngx_stream_lua_output.h" -#include "ngx_stream_lua_time.h" #include "ngx_stream_lua_control.h" #include "ngx_stream_lua_log.h" -#include "ngx_stream_lua_variable.h" #include "ngx_stream_lua_string.h" #include "ngx_stream_lua_misc.h" #include "ngx_stream_lua_consts.h" @@ -39,16 +36,15 @@ #include "ngx_stream_lua_socket_tcp.h" #include "ngx_stream_lua_socket_udp.h" #include "ngx_stream_lua_sleep.h" -#include "ngx_stream_lua_phase.h" #include "ngx_stream_lua_probe.h" #include "ngx_stream_lua_uthread.h" #include "ngx_stream_lua_contentby.h" #include "ngx_stream_lua_timer.h" #include "ngx_stream_lua_config.h" -#include "ngx_stream_lua_worker.h" #include "ngx_stream_lua_ssl.h" +#include "ngx_stream_lua_phase.h" #if 1 @@ -94,6 +90,10 @@ char ngx_stream_lua_coroutines_key; static void ngx_stream_lua_init_registry(lua_State *L, ngx_log_t *log); static void ngx_stream_lua_init_globals(lua_State *L, ngx_cycle_t *cycle, ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log); +#ifdef OPENRESTY_LUAJIT +static void ngx_stream_lua_inject_global_write_guard(lua_State *L, + ngx_log_t *log); +#endif static void ngx_stream_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx, const char *fieldname, const char *path, const char *default_path, ngx_log_t *log); @@ -251,7 +251,6 @@ ngx_stream_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle, lua_pushliteral(L, LUA_DEFAULT_PATH ";"); /* package default */ lua_getfield(L, -2, "path"); /* package default old */ - old_path = lua_tolstring(L, -1, &old_path_len); lua_concat(L, 2); /* package new */ lua_setfield(L, -2, "path"); /* package */ #endif @@ -441,11 +440,7 @@ ngx_stream_lua_output_filter(ngx_stream_lua_request_t *r, ngx_chain_t *in) ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); -#if nginx_version >= 1001004 ngx_chain_update_chains(r->pool, -#else - ngx_chain_update_chains( -#endif &ctx->free_bufs, &ctx->busy_bufs, &in, (ngx_buf_tag_t) &ngx_stream_lua_module); @@ -514,7 +509,7 @@ static void ngx_stream_lua_inject_ngx_api(lua_State *L, ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log) { - lua_createtable(L, 0 /* narr */, 117 /* nrec */); /* ngx.* */ + lua_createtable(L, 0 /* narr */, 113 /* nrec */); /* ngx.* */ lua_pushcfunction(L, ngx_stream_lua_get_raw_phase_context); lua_setfield(L, -2, "_phase_ctx"); @@ -524,7 +519,6 @@ ngx_stream_lua_inject_ngx_api(lua_State *L, ngx_stream_lua_main_conf_t *lmcf, ngx_stream_lua_inject_log_api(L); ngx_stream_lua_inject_output_api(L); - ngx_stream_lua_inject_time_api(L); ngx_stream_lua_inject_string_api(L); ngx_stream_lua_inject_control_api(log, L); @@ -532,23 +526,15 @@ ngx_stream_lua_inject_ngx_api(lua_State *L, ngx_stream_lua_main_conf_t *lmcf, ngx_stream_lua_inject_sleep_api(L); ngx_stream_lua_inject_phase_api(L); -#if (NGX_PCRE) - ngx_stream_lua_inject_regex_api(L); -#endif - ngx_stream_lua_inject_req_api(log, L); - ngx_stream_lua_inject_variable_api(L); ngx_stream_lua_inject_shdict_api(lmcf, L); ngx_stream_lua_inject_socket_tcp_api(log, L); ngx_stream_lua_inject_socket_udp_api(log, L); ngx_stream_lua_inject_uthread_api(log, L); ngx_stream_lua_inject_timer_api(L); ngx_stream_lua_inject_config_api(L); - ngx_stream_lua_inject_worker_api(L); - - ngx_stream_lua_inject_misc_api(L); lua_getglobal(L, "package"); /* ngx package */ lua_getfield(L, -1, "loaded"); /* ngx package loaded */ @@ -559,53 +545,55 @@ ngx_stream_lua_inject_ngx_api(lua_State *L, ngx_stream_lua_main_conf_t *lmcf, lua_setglobal(L, "ngx"); ngx_stream_lua_inject_coroutine_api(log, L); +} #ifdef OPENRESTY_LUAJIT - { - int rc; - - const char buf[] = - "local ngx_log = ngx.log\n" - "local ngx_WARN = ngx.WARN\n" - "local tostring = tostring\n" - "local ngx_get_phase = ngx.get_phase\n" - "local traceback = require 'debug'.traceback\n" - "local function newindex(table, key, value)\n" - "rawset(table, key, value)\n" - "local phase = ngx_get_phase()\n" - "if phase == 'init_worker' or phase == 'init' then\n" - "return\n" - "end\n" - "ngx_log(ngx_WARN, 'writing a global lua variable " - "(\\'', tostring(key), '\\') which may lead to " - "race conditions between concurrent requests, so " - "prefer the use of \\'local\\' variables', " - "traceback('', 2))\n" +static void +ngx_stream_lua_inject_global_write_guard(lua_State *L, ngx_log_t *log) +{ + int rc; + + const char buf[] = + "local ngx_log = ngx.log\n" + "local ngx_WARN = ngx.WARN\n" + "local tostring = tostring\n" + "local ngx_get_phase = ngx.get_phase\n" + "local traceback = require 'debug'.traceback\n" + "local function newindex(table, key, value)\n" + "rawset(table, key, value)\n" + "local phase = ngx_get_phase()\n" + "if phase == 'init_worker' or phase == 'init' then\n" + "return\n" "end\n" - "setmetatable(_G, { __newindex = newindex })\n" - ; - - rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=_G write guard"); - - if (rc != 0) { - ngx_log_error(NGX_LOG_ERR, log, 0, - "failed to load Lua code (%i): %s", - rc, lua_tostring(L, -1)); + "ngx_log(ngx_WARN, 'writing a global Lua variable " + "(\\'', tostring(key), '\\') which may lead to " + "race conditions between concurrent requests, so " + "prefer the use of \\'local\\' variables', " + "traceback('', 2))\n" + "end\n" + "setmetatable(_G, { __newindex = newindex })\n" + ; + + rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=_G write guard"); + + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to load Lua code (%i): %s", + rc, lua_tostring(L, -1)); - lua_pop(L, 1); - return; - } + 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 Lua code (%i): %s", - rc, lua_tostring(L, -1)); - lua_pop(L, 1); - } + rc = lua_pcall(L, 0, 0, 0); + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to run Lua code (%i): %s", + rc, lua_tostring(L, -1)); + lua_pop(L, 1); } -#endif } +#endif void @@ -789,10 +777,16 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, /* set Lua VM panic handler */ lua_atpanic(L, ngx_stream_lua_atpanic); - dd("ctx = %p", ctx); - NGX_LUA_EXCEPTION_TRY { + /* + * silence a -Werror=clobbered warning with gcc 5.4 + * due to above setjmp + */ + err = NULL; + msg = NULL; + trace = NULL; + if (ctx->cur_co_ctx->thread_spawn_yielded) { ngx_stream_lua_probe_info("thread spawn yielded"); @@ -802,19 +796,15 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, for ( ;; ) { - dd("calling lua_resume: vm %p, nret %d", ctx->cur_co_ctx->co, - (int) nrets); + dd("ctx: %p, co: %p, co status: %d, co is_wrap: %d", + ctx, ctx->cur_co_ctx->co, ctx->cur_co_ctx->co_status, + ctx->cur_co_ctx->is_wrap); #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ old_pool = ngx_stream_lua_pcre_malloc_init(r->pool); #endif - /* run code */ - dd("ctx: %p", ctx); - dd("cur co: %p", ctx->cur_co_ctx->co); - dd("cur co status: %d", ctx->cur_co_ctx->co_status); - orig_coctx = ctx->cur_co_ctx; #ifdef NGX_LUA_USE_ASSERT @@ -826,10 +816,19 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, #if DDEBUG if (lua_gettop(orig_coctx->co) > 0) { - dd("top elem: %s", luaL_typename(orig_coctx->co, -1)); + dd("co top elem: %s", luaL_typename(orig_coctx->co, -1)); + } + + if (orig_coctx->propagate_error) { + dd("co propagate_error: %d", orig_coctx->propagate_error); } #endif + if (orig_coctx->propagate_error) { + orig_coctx->propagate_error = 0; + goto propagate_error; + } + ngx_stream_lua_assert(orig_coctx->co_top + nrets == lua_gettop(orig_coctx->co)); @@ -969,12 +968,6 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, next_coctx = ctx->cur_co_ctx->parent_co_ctx; next_co = next_coctx->co; - /* - * prepare return values for coroutine.resume - * (true plus any retvals) - */ - lua_pushboolean(next_co, 1); - if (nrets) { dd("moving %d return values to next co", nrets); lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); @@ -983,7 +976,14 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, #endif } - nrets++; /* add the true boolean value */ + if (!ctx->cur_co_ctx->is_wrap) { + /* prepare return values for coroutine.resume + * (true plus any retvals) + */ + lua_pushboolean(next_co, 1); + lua_insert(next_co, 1); + nrets++; /* add the true boolean value */ + } ctx->cur_co_ctx = next_coctx; @@ -1094,12 +1094,6 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, next_co = next_coctx->co; - /* - * ended successful, coroutine.resume returns true plus - * any return values - */ - lua_pushboolean(next_co, success); - if (nrets) { lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); } @@ -1109,7 +1103,15 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, ctx->uthreads--; } - nrets++; + if (!ctx->cur_co_ctx->is_wrap) { + /* ended successfully, coroutine.resume returns true plus + * any return values + */ + lua_pushboolean(next_co, success); + lua_insert(next_co, 1); + nrets++; + } + ctx->cur_co_ctx = next_coctx; ngx_stream_lua_probe_info("set parent running"); @@ -1147,25 +1149,34 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, ctx->cur_co_ctx = orig_coctx; } - if (lua_isstring(ctx->cur_co_ctx->co, -1)) { - dd("user custom error msg"); - msg = lua_tostring(ctx->cur_co_ctx->co, -1); - - } else { - msg = "unknown reason"; - } - ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); ngx_stream_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 0); ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD; - ngx_stream_lua_thread_traceback(L, ctx->cur_co_ctx->co, - ctx->cur_co_ctx); - trace = lua_tostring(L, -1); + if (orig_coctx->is_uthread + || orig_coctx->is_wrap + || ngx_stream_lua_is_entry_thread(ctx)) + { + ngx_stream_lua_thread_traceback(L, orig_coctx->co, + orig_coctx); + trace = lua_tostring(L, -1); + + if (lua_isstring(orig_coctx->co, -1)) { + msg = lua_tostring(orig_coctx->co, -1); + dd("user custom error msg: %s", msg); + + } else { + msg = "unknown reason"; + } + } + +propagate_error: if (ctx->cur_co_ctx->is_uthread) { + ngx_stream_lua_assert(err != NULL && msg != NULL + && trace != NULL); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "stream lua user thread aborted: %s: %s\n%s", @@ -1216,6 +1227,9 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, } if (ngx_stream_lua_is_entry_thread(ctx)) { + ngx_stream_lua_assert(err != NULL && msg != NULL + && trace != NULL); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua entry thread aborted: %s: %s\n%s", err, msg, trace); @@ -1249,19 +1263,25 @@ ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r, next_coctx->co_status = NGX_STREAM_LUA_CO_RUNNING; + ctx->cur_co_ctx = next_coctx; + + if (orig_coctx->is_wrap) { + /* + * coroutine.wrap propagates errors + * to its parent coroutine + */ + next_coctx->propagate_error = 1; + continue; + } + /* * ended with error, coroutine.resume returns false plus * err msg */ lua_pushboolean(next_co, 0); - lua_xmove(ctx->cur_co_ctx->co, next_co, 1); + lua_xmove(orig_coctx->co, next_co, 1); nrets = 2; - ctx->cur_co_ctx = next_coctx; - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "lua coroutine: %s: %s\n%s", err, msg, trace); - /* try resuming on the new coroutine again */ continue; } @@ -1937,7 +1957,8 @@ ngx_stream_lua_req_socket(lua_State *L) /* shouldn't happen */ ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, - "unexpected connection type: %d", r->connection->type); + "stream unexpected connection type: %d", + r->connection->type); ngx_stream_lua_assert(0); @@ -1950,12 +1971,12 @@ ngx_stream_lua_inject_req_api(ngx_log_t *log, lua_State *L) { /* ngx.req table */ - lua_createtable(L, 0 /* narr */, 24 /* nrec */); /* .req */ + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* .req */ + lua_pushcfunction(L, ngx_stream_lua_req_socket); lua_setfield(L, -2, "socket"); - lua_setfield(L, -2, "req"); } @@ -2351,9 +2372,7 @@ ngx_stream_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, of->uniq = ngx_file_uniq(&fi); of->mtime = ngx_file_mtime(&fi); of->size = ngx_file_size(&fi); -#if defined(nginx_version) && nginx_version >= 1000001 of->fs_size = ngx_file_fs_size(&fi); -#endif of->is_dir = ngx_is_dir(&fi); of->is_file = ngx_is_file(&fi); of->is_link = ngx_is_link(&fi); @@ -3266,9 +3285,10 @@ ngx_stream_lua_close_fake_connection(ngx_connection_t *c) } -lua_State * -ngx_stream_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, - ngx_pool_t *pool, ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log, +ngx_int_t +ngx_stream_lua_init_vm(lua_State **new_vm, lua_State *parent_vm, + ngx_cycle_t *cycle, ngx_pool_t *pool, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log, ngx_pool_cleanup_t **pcln) { int rc; @@ -3281,13 +3301,13 @@ ngx_stream_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, cln = ngx_pool_cleanup_add(pool, 0); if (cln == NULL) { - return NULL; + return NGX_ERROR; } /* create new Lua VM instance */ L = ngx_stream_lua_new_state(parent_vm, cycle, lmcf, log); if (L == NULL) { - return NULL; + return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, "lua initialize the " @@ -3298,7 +3318,7 @@ ngx_stream_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, state = ngx_alloc(sizeof(ngx_stream_lua_vm_state_t), log); if (state == NULL) { - return NULL; + return NGX_ERROR; } state->vm = L; state->count = 1; @@ -3331,7 +3351,8 @@ ngx_stream_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, for (i = 0; i < lmcf->preload_hooks->nelts; i++) { - ngx_stream_lua_probe_register_preload_package(L, hook[i].package); + ngx_stream_lua_probe_register_preload_package(L, + hook[i].package); lua_pushcfunction(L, hook[i].loader); lua_setfield(L, -2, (char *) hook[i].package); @@ -3340,22 +3361,21 @@ ngx_stream_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, lua_pop(L, 2); } - if (lmcf->load_resty_core) { - lua_getglobal(L, "require"); - lua_pushstring(L, "resty.core"); + *new_vm = L; - rc = lua_pcall(L, 1, 1, 0); - if (rc != 0) { - ngx_log_error(NGX_LOG_ERR, log, 0, - "lua_load_resty_core failed to load the resty.core " - "module from https://github.com/openresty/lua-resty" - "-core; ensure you are using an OpenResty release " - "from https://openresty.org/en/download.html " - "(rc: %i, reason: %s)", rc, lua_tostring(L, -1)); - } + lua_getglobal(L, "require"); + lua_pushstring(L, "resty.core"); + + rc = lua_pcall(L, 1, 1, 0); + if (rc != 0) { + return NGX_DECLINED; } - return L; +#ifdef OPENRESTY_LUAJIT + ngx_stream_lua_inject_global_write_guard(L, log); +#endif + + return NGX_OK; } diff --git a/src/ngx_stream_lua_util.h b/src/ngx_stream_lua_util.h index 1528558f..232f10ef 100644 --- a/src/ngx_stream_lua_util.h +++ b/src/ngx_stream_lua_util.h @@ -31,19 +31,10 @@ #endif -#ifndef NGX_LUA_NO_FFI_API -typedef struct { - int len; - /* this padding hole on 64-bit systems is expected */ - u_char *data; -} ngx_stream_lua_ffi_str_t; - - typedef struct { ngx_stream_lua_ffi_str_t key; ngx_stream_lua_ffi_str_t value; } ngx_stream_lua_ffi_table_elt_t; -#endif /* NGX_LUA_NO_FFI_API */ /* char whose address we use as the key in Lua vm registry for @@ -56,7 +47,7 @@ extern char ngx_stream_lua_code_cache_key; /* char whose address we use as the key in Lua vm registry for - * regex cache table */ + * regex cache table */ extern char ngx_stream_lua_regex_cache_key; /* char whose address we use as the key in Lua vm registry for @@ -79,13 +70,6 @@ extern char ngx_stream_lua_headers_metatable_key; #endif - - -#if defined(nginx_version) && nginx_version < 1000000 -#define ngx_memmove(dst, src, n) (void) memmove(dst, src, n) -#endif - - #define ngx_stream_lua_context_name(c) \ ((c) == NGX_STREAM_LUA_CONTEXT_CONTENT ? "content_by_lua*" \ : (c) == NGX_STREAM_LUA_CONTEXT_LOG ? "log_by_lua*" \ @@ -139,8 +123,9 @@ ngx_stream_lua_ffi_check_context(ngx_stream_lua_ctx_t *ctx, SSL_get_ex_data(ssl_conn, ngx_stream_lua_ssl_ctx_index) -lua_State *ngx_stream_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, - ngx_pool_t *pool, ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log, +ngx_int_t ngx_stream_lua_init_vm(lua_State **new_vm, lua_State *parent_vm, + ngx_cycle_t *cycle, ngx_pool_t *pool, + ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log, ngx_pool_cleanup_t **pcln); lua_State *ngx_stream_lua_new_thread(ngx_stream_lua_request_t *r, lua_State *l, @@ -279,7 +264,8 @@ ngx_stream_lua_init_ctx(ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx) static ngx_inline ngx_stream_lua_ctx_t * ngx_stream_lua_create_ctx(ngx_stream_session_t *r) { - lua_State *L; + ngx_int_t rc; + lua_State *L = NULL; ngx_stream_lua_ctx_t *ctx; ngx_pool_cleanup_t *cln; ngx_stream_lua_loc_conf_t *llcf; @@ -318,8 +304,8 @@ ngx_stream_lua_create_ctx(ngx_stream_session_t *r) * the correct semantics. */ - L = ngx_stream_lua_init_vm(lmcf->lua, lmcf->cycle, sreq->pool, lmcf, - r->connection->log, &cln); + rc = ngx_stream_lua_init_vm(&L, lmcf->lua, lmcf->cycle, sreq->pool, + lmcf, r->connection->log, &cln); while (cln->next != NULL) { cln = cln->next; @@ -331,11 +317,30 @@ ngx_stream_lua_create_ctx(ngx_stream_session_t *r) sreq->pool->cleanup = cln->next; cln->next = NULL; - if (L == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "failed to initialize Lua VM"); - return NULL; - } + if (rc != NGX_OK) { + if (rc == NGX_DECLINED) { + ngx_stream_lua_assert(L != NULL); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "failed to load the 'resty.core' module " + "(https://github.com/openresty/lua-resty" + "-core); ensure you are using an OpenResty " + "release from https://openresty.org/en/" + "download.html (reason: %s)", + lua_tostring(L, -1)); + + } else { + /* rc == NGX_ERROR */ + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "failed to initialize Lua VM"); + } + + return NULL; + } + + /* rc == NGX_OK */ + + ngx_stream_lua_assert(L != NULL); if (lmcf->init_handler) { if (lmcf->init_handler(r->connection->log, lmcf, L) != NGX_OK) { @@ -474,7 +479,7 @@ ngx_stream_lua_get_flush_chain(ngx_stream_lua_request_t *r, } -#if (nginx_version < 1011002) +#if defined(nginx_version) && nginx_version < 1011002 static ngx_inline in_port_t ngx_inet_get_port(struct sockaddr *sa) { diff --git a/src/ngx_stream_lua_variable.c b/src/ngx_stream_lua_variable.c index a9750bbf..2d3ef869 100644 --- a/src/ngx_stream_lua_variable.c +++ b/src/ngx_stream_lua_variable.c @@ -19,242 +19,11 @@ #include "ddebug.h" -#include "ngx_stream_lua_variable.h" #include "ngx_stream_lua_util.h" -static int ngx_stream_lua_var_get(lua_State *L); -static int ngx_stream_lua_var_set(lua_State *L); -void -ngx_stream_lua_inject_variable_api(lua_State *L) -{ - /* {{{ register reference maps */ - lua_newtable(L); /* ngx.var */ - - lua_createtable(L, 0, 2 /* nrec */); /* metatable for .var */ - lua_pushcfunction(L, ngx_stream_lua_var_get); - lua_setfield(L, -2, "__index"); - lua_pushcfunction(L, ngx_stream_lua_var_set); - lua_setfield(L, -2, "__newindex"); - lua_setmetatable(L, -2); - - lua_setfield(L, -2, "var"); -} - - -/** - * Get nginx internal variables content - * - * @retval Always return a string or nil on Lua stack. Return nil when failed - * to get content, and actual content string when found the specified variable. - * @seealso ngx_stream_lua_var_set - * */ -static int -ngx_stream_lua_var_get(lua_State *L) -{ - ngx_stream_lua_request_t *r; - u_char *p, *lowcase; - size_t len; - ngx_uint_t hash; - ngx_str_t name; - - ngx_stream_variable_value_t *vv; - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - ngx_stream_lua_check_fake_request(L, r); - - - if (lua_type(L, -1) != LUA_TSTRING) { - return luaL_error(L, "bad variable name"); - } - - p = (u_char *) lua_tolstring(L, -1, &len); - - lowcase = lua_newuserdata(L, len); - - hash = ngx_hash_strlow(lowcase, p, len); - - name.len = len; - name.data = lowcase; - - vv = ngx_stream_get_variable(r->session, &name, hash); - - if (vv == NULL || vv->not_found) { - lua_pushnil(L); - return 1; - } - - lua_pushlstring(L, (const char *) vv->data, (size_t) vv->len); - return 1; -} - - -/** - * Set nginx internal variable content - * - * @retval Always return a boolean on Lua stack. Return true when variable - * content was modified successfully, false otherwise. - * @seealso ngx_stream_lua_var_get - * */ -static int -ngx_stream_lua_var_set(lua_State *L) -{ - u_char *p, *lowcase, *val; - size_t len; - ngx_str_t name; - ngx_uint_t hash; - ngx_stream_lua_request_t *r; - int value_type; - const char *msg; - - ngx_stream_variable_t *v; - ngx_stream_variable_value_t *vv; - ngx_stream_core_main_conf_t *cmcf; - - r = ngx_stream_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - ngx_stream_lua_check_fake_request(L, r); - - /* we skip the first argument that is the table */ - - /* we read the variable name */ - - if (lua_type(L, 2) != LUA_TSTRING) { - return luaL_error(L, "bad variable name"); - } - - p = (u_char *) lua_tolstring(L, 2, &len); - - lowcase = lua_newuserdata(L, len + 1); - - hash = ngx_hash_strlow(lowcase, p, len); - lowcase[len] = '\0'; - - name.len = len; - name.data = lowcase; - - /* we read the variable new value */ - - value_type = lua_type(L, 3); - switch (value_type) { - case LUA_TNUMBER: - case LUA_TSTRING: - p = (u_char *) luaL_checklstring(L, 3, &len); - - val = ngx_palloc(r->connection->pool, len); - if (val == NULL) { - return luaL_error(L, "memory allocation error"); - } - - ngx_memcpy(val, p, len); - - break; - - case LUA_TNIL: - /* undef the variable */ - - val = NULL; - len = 0; - - break; - - default: - msg = lua_pushfstring(L, "string, number, or nil expected, " - "but got %s", lua_typename(L, value_type)); - return luaL_argerror(L, 1, msg); - } - - /* we fetch the variable itself */ - - cmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_core_module); - - v = ngx_hash_find(&cmcf->variables_hash, hash, name.data, name.len); - - if (v) { - if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) { - return luaL_error(L, "variable \"%s\" not changeable", lowcase); - } - - if (v->set_handler) { - - dd("set variables with set_handler"); - - vv = ngx_palloc(r->connection->pool, - sizeof(ngx_stream_variable_value_t)); - if (vv == NULL) { - return luaL_error(L, "no memory"); - } - - if (value_type == LUA_TNIL) { - vv->valid = 0; - vv->not_found = 1; - vv->no_cacheable = 0; - vv->data = NULL; - vv->len = 0; - - } else { - vv->valid = 1; - vv->not_found = 0; - vv->no_cacheable = 0; - - vv->data = val; - vv->len = len; - } - - v->set_handler(r->session, vv, v->data); - - return 0; - } - - if (v->flags & NGX_STREAM_VAR_INDEXED) { - vv = &r->session->variables[v->index]; - - dd("set indexed variable"); - - if (value_type == LUA_TNIL) { - vv->valid = 0; - vv->not_found = 1; - vv->no_cacheable = 0; - - vv->data = NULL; - vv->len = 0; - - } else { - vv->valid = 1; - vv->not_found = 0; - vv->no_cacheable = 0; - - vv->data = val; - vv->len = len; - } - - return 0; - } - - return luaL_error(L, "variable \"%s\" cannot be assigned a value", - lowcase); - } - - /* variable not found */ - - return luaL_error(L, "variable \"%s\" not found for writing; " - "maybe it is a built-in variable that is not changeable " - "or you forgot to use \"set $%s '';\" " - "in the config file to define it first", - lowcase, lowcase); -} - - -#ifndef NGX_LUA_NO_FFI_API int ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r, u_char *name_data, size_t name_len, u_char *lowcase_buf, int capture_id, u_char **value, @@ -262,11 +31,6 @@ ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r, u_char *name_data, { ngx_uint_t hash; ngx_str_t name; -#if (NGX_PCRE) - u_char *p; - ngx_uint_t n; - int *cap; -#endif ngx_stream_variable_value_t *vv; @@ -280,36 +44,6 @@ ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r, u_char *name_data, return NGX_ERROR; } -#if (NGX_PCRE) - if (name_data == 0) { - if (capture_id <= 0) { - return NGX_DECLINED; - } - - /* it is a regex capturing variable */ - - n = (ngx_uint_t) capture_id * 2; - - dd("n = %d, ncaptures = %d", (int) n, (int) r->ncaptures); - - if (r->captures == NULL - || r->captures_data == NULL - || n >= r->ncaptures) - { - return NGX_DECLINED; - } - - /* n >= 0 && n < r->ncaptures */ - - cap = r->captures; - p = r->captures_data; - - *value = &p[cap[n]]; - *value_len = (size_t) (cap[n + 1] - cap[n]); - - return NGX_OK; - } -#endif hash = ngx_hash_strlow(lowcase_buf, name_data, name_len); @@ -415,12 +149,12 @@ ngx_stream_lua_ffi_var_set(ngx_stream_lua_request_t *r, u_char *name_data, vv->len = value_len; } - v->set_handler(r, vv, v->data); + v->set_handler(r->session, vv, v->data); return NGX_OK; } if (v->flags & NGX_STREAM_VAR_INDEXED) { - vv = &r->variables[v->index]; + vv = &r->session->variables[v->index]; dd("set indexed variable"); @@ -475,7 +209,6 @@ ngx_stream_lua_ffi_var_set(ngx_stream_lua_request_t *r, u_char *name_data, *errlen = ngx_snprintf(errbuf, *errlen, "no memory") - errbuf; return NGX_ERROR; } -#endif /* NGX_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_variable.h b/src/ngx_stream_lua_variable.h deleted file mode 100644 index 17f61b07..00000000 --- a/src/ngx_stream_lua_variable.h +++ /dev/null @@ -1,28 +0,0 @@ - -/* - * !!! DO NOT EDIT DIRECTLY !!! - * This file was automatically generated from the following template: - * - * src/subsys/ngx_subsys_lua_variable.h.tt2 - */ - - -/* - * Copyright (C) Xiaozhe Wang (chaoslawful) - * Copyright (C) Yichun Zhang (agentzh) - */ - - -#ifndef _NGX_STREAM_LUA_VARIABLE_H_INCLUDED_ -#define _NGX_STREAM_LUA_VARIABLE_H_INCLUDED_ - - -#include "ngx_stream_lua_common.h" - - -void ngx_stream_lua_inject_variable_api(lua_State *L); - - -#endif /* _NGX_STREAM_LUA_VARIABLE_H_INCLUDED_ */ - -/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_worker.c b/src/ngx_stream_lua_worker.c index 2ea0df23..01779df1 100644 --- a/src/ngx_stream_lua_worker.c +++ b/src/ngx_stream_lua_worker.c @@ -18,88 +18,9 @@ #include "ddebug.h" -#include "ngx_stream_lua_worker.h" - - #define NGX_PROCESS_PRIVILEGED_AGENT 99 -static int ngx_stream_lua_ngx_worker_exiting(lua_State *L); -static int ngx_stream_lua_ngx_worker_pid(lua_State *L); -static int ngx_stream_lua_ngx_worker_id(lua_State *L); -static int ngx_stream_lua_ngx_worker_count(lua_State *L); - - -void -ngx_stream_lua_inject_worker_api(lua_State *L) -{ - lua_createtable(L, 0 /* narr */, 4 /* nrec */); /* ngx.worker. */ - - lua_pushcfunction(L, ngx_stream_lua_ngx_worker_exiting); - lua_setfield(L, -2, "exiting"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_worker_pid); - lua_setfield(L, -2, "pid"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_worker_id); - lua_setfield(L, -2, "id"); - - lua_pushcfunction(L, ngx_stream_lua_ngx_worker_count); - lua_setfield(L, -2, "count"); - - lua_setfield(L, -2, "worker"); -} - - -static int -ngx_stream_lua_ngx_worker_exiting(lua_State *L) -{ - lua_pushboolean(L, ngx_exiting); - return 1; -} - - -static int -ngx_stream_lua_ngx_worker_pid(lua_State *L) -{ - lua_pushinteger(L, (lua_Integer) ngx_pid); - return 1; -} - - -static int -ngx_stream_lua_ngx_worker_id(lua_State *L) -{ -#if (nginx_version >= 1009001) - if (ngx_process != NGX_PROCESS_WORKER - && ngx_process != NGX_PROCESS_SINGLE) - { - lua_pushnil(L); - return 1; - } - - lua_pushinteger(L, (lua_Integer) ngx_worker); -#else - lua_pushnil(L); -#endif - return 1; -} - - -static int -ngx_stream_lua_ngx_worker_count(lua_State *L) -{ - ngx_core_conf_t *ccf; - - ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, - ngx_core_module); - - lua_pushinteger(L, (lua_Integer) ccf->worker_processes); - return 1; -} - - -#ifndef NGX_LUA_NO_FFI_API int ngx_stream_lua_ffi_worker_pid(void) { @@ -110,7 +31,7 @@ ngx_stream_lua_ffi_worker_pid(void) int ngx_stream_lua_ffi_worker_id(void) { -#if (nginx_version >= 1009001) +#if defined(nginx_version) && nginx_version >= 1009001 if (ngx_process != NGX_PROCESS_WORKER && ngx_process != NGX_PROCESS_SINGLE) { @@ -146,7 +67,7 @@ ngx_stream_lua_ffi_worker_count(void) int ngx_stream_lua_ffi_master_pid(void) { -#if (nginx_version >= 1013008) +#if defined(nginx_version) && nginx_version >= 1013008 if (ngx_process == NGX_PROCESS_SINGLE) { return (int) ngx_pid; } @@ -161,6 +82,8 @@ ngx_stream_lua_ffi_master_pid(void) int ngx_stream_lua_ffi_get_process_type(void) { + ngx_core_conf_t *ccf; + #if defined(HAVE_PRIVILEGED_PROCESS_PATCH) && !NGX_WIN32 if (ngx_process == NGX_PROCESS_HELPER) { if (ngx_is_privileged_agent) { @@ -169,6 +92,15 @@ ngx_stream_lua_ffi_get_process_type(void) } #endif + if (ngx_process == NGX_PROCESS_SINGLE) { + ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_core_module); + + if (ccf->master) { + return NGX_PROCESS_MASTER; + } + } + return ngx_process; } @@ -198,4 +130,6 @@ ngx_stream_lua_ffi_process_signal_graceful_exit(void) { ngx_quit = 1; } -#endif + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_worker.h b/src/ngx_stream_lua_worker.h deleted file mode 100644 index 8cd88c51..00000000 --- a/src/ngx_stream_lua_worker.h +++ /dev/null @@ -1,25 +0,0 @@ - -/* - * !!! DO NOT EDIT DIRECTLY !!! - * This file was automatically generated from the following template: - * - * src/subsys/ngx_subsys_lua_worker.h.tt2 - */ - - -/* - * Copyright (C) Yichun Zhang (agentzh) - */ - - -#ifndef _NGX_STREAM_LUA_WORKER_H_INCLUDED_ -#define _NGX_STREAM_LUA_WORKER_H_INCLUDED_ - - -#include "ngx_stream_lua_common.h" - - -void ngx_stream_lua_inject_worker_api(lua_State *L); - - -#endif /* _NGX_STREAM_LUA_WORKER_H_INCLUDED_ */ diff --git a/t/013-base64.t b/t/013-base64.t index fac0e914..01b42c9e 100644 --- a/t/013-base64.t +++ b/t/013-base64.t @@ -139,4 +139,4 @@ dw== content_by_lua_block { ngx.say(ngx.encode_base64("w", 0)) } --- stream_response --- error_log eval -qr/bad argument \#2 to 'encode_base64' \(boolean expected, got number\)|\[error\] .*? boolean argument only/ +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):\d+: bad no_padding: boolean expected, got number/ diff --git a/t/025-codecache.t b/t/025-codecache.t index 118901ff..31a056e7 100644 --- a/t/025-codecache.t +++ b/t/025-codecache.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua::Stream; repeat_each(2); -plan tests => repeat_each() * 132; +plan tests => repeat_each() * 140; #$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua'; @@ -12,6 +12,7 @@ no_long_string(); our $HtmlDir = html_dir; +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; check_accum_error_log(); @@ -959,3 +960,160 @@ qr/\[alert\] \S+ stream lua_code_cache is off; this will hurt performance/, "stream lua decrementing the reference count for Lua VM: 1", "stream lua close the global Lua VM", ] + + + +=== TEST 25: make sure inline code keys are correct +--- stream_config + include ../html/a/proxy.conf; + include ../html/b/proxy.conf; + include ../html/c/proxy.conf; +--- stream_server_config + content_by_lua_block { + for _, n in ipairs({ 1, 2, 1, 3 }) do + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx" .. n .. ".sock") + if not ok then + ngx.log(ngx.ERR, "could not connect: ", err) + return + end + + local res, err = sock:receive(11) + if not res then + ngx.log(ngx.ERR, "could not receive: ", err) + return + end + + ngx.say(res) + + sock:close() + end + } +--- user_files +>>> a/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx1.sock; + content_by_lua_block { ngx.say("1 is called") } +} + +>>> b/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock; + content_by_lua_block { ngx.say("2 is called") } +} + +>>> c/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx3.sock; + content_by_lua_block { ngx.say("2 is called") } +} +--- stream_response +1 is called +2 is called +1 is called +2 is called +--- grep_error_log eval: qr/looking up Lua code cache with key '=content_by_lua\(proxy\.conf:\d+\).*?'/ +--- grep_error_log_out eval +[ +"looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +", +"looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_ad58d60a0f20ae31b1a282e74053d356' +looking up Lua code cache with key '=content_by_lua(proxy.conf:3)nhli_9c867c93f28b91041fe132817b43ad07' +"] +--- log_level: debug +--- no_error_log +[error] + + + +=== TEST 26: make sure inline code keys are correct +--- stream_config + include ../html/a/proxy.conf; + include ../html/b/proxy.conf; + include ../html/c/proxy.conf; +--- stream_server_config + content_by_lua_block { + for _, n in ipairs({ 1, 2, 1, 3 }) do + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx" .. n .. ".sock") + if not ok then + ngx.log(ngx.ERR, "could not connect: ", err) + return + end + + local res, err = sock:receive(11) + if not res then + ngx.log(ngx.ERR, "could not receive: ", err) + return + end + + ngx.say(res) + + sock:close() + end + } +--- user_files +>>> a.lua +ngx.say("1 is called") + +>>> b.lua +ngx.say("2 is called") + +>>> c.lua +ngx.say("2 is called") + +>>> a/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx1.sock; + content_by_lua_file html/a.lua; +} + +>>> b/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock; + content_by_lua_file html/b.lua; +} + +>>> c/proxy.conf +server { + listen unix:$TEST_NGINX_HTML_DIR/nginx3.sock; + content_by_lua_file html/c.lua; +} +--- stream_response +1 is called +2 is called +1 is called +2 is called +--- grep_error_log eval: qr/looking up Lua code cache with key 'nhlf_.*?'/ +--- grep_error_log_out eval +[ +"looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_68f5f4e946c3efd1cc206452b807e8b6' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_042c9b3a136fbacbbd0e4b9ad10896b7' +", +"looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_68f5f4e946c3efd1cc206452b807e8b6' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_042c9b3a136fbacbbd0e4b9ad10896b7' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_68f5f4e946c3efd1cc206452b807e8b6' +looking up Lua code cache with key 'nhlf_48a9a7def61143c003a7de1644e026e4' +looking up Lua code cache with key 'nhlf_042c9b3a136fbacbbd0e4b9ad10896b7' +"] +--- log_level: debug +--- no_error_log +[error] diff --git a/t/058-tcp-socket.t b/t/058-tcp-socket.t index d7568b0d..94bb7d08 100644 --- a/t/058-tcp-socket.t +++ b/t/058-tcp-socket.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua::Stream; repeat_each(2); -plan tests => repeat_each() * 191; +plan tests => repeat_each() * 219; our $HtmlDir = html_dir; @@ -3074,7 +3074,65 @@ failed to setkeepalive: closed -=== TEST 57: TEST 62: the upper bound of port range should be 2^16 - 1 +=== TEST 57: options_table is nil +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port, nil) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } +--- stream_response +connected: 1 +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 58: resolver send query failing immediately in connect() +this case did not clear coctx->cleanup properly and would lead to memory invalid accesses. + +this test case requires the following iptables rule to work properly: + +sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT + +--- stream_server_config + resolver 127.0.0.1:10086 ipv6=off; + resolver_timeout 10ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + + for i = 1, 3 do -- retry + local ok, err = sock:connect("www.google.com", 80) + if not ok then + ngx.say("failed to connect: ", err) + end + end + + ngx.say("hello!") + } +--- stream_response_body_like +failed to connect: www.google.com could not be resolved(?: \(\d+: Operation timed out\))? +failed to connect: www.google.com could not be resolved(?: \(\d+: Operation timed out\))? +failed to connect: www.google.com could not be resolved(?: \(\d+: Operation timed out\))? +hello! +--- error_log eval +qr{\[alert\] .*? send\(\) failed \(\d+: Operation not permitted\) while resolving} + + + +=== TEST 59: the upper bound of port range should be 2^16 - 1 --- stream_server_config content_by_lua_block { local sock, err = ngx.socket.connect("127.0.0.1", 65536) @@ -3090,7 +3148,63 @@ failed to connect: bad port number: 65536 -=== TEST 58: TCP socket GC'ed in preread phase without Lua content phase +=== TEST 60: send boolean and nil +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = $TEST_NGINX_SERVER_PORT + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local function send(data) + local bytes, err = sock:send(data) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + end + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\nTest: " + send(req) + send(true) + send(false) + send(nil) + send("\r\n\r\n") + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + else + break + end + end + + ok, err = sock:close() + } +--- config + location /foo { + server_tokens off; + more_clear_headers Date; + echo $http_test; + } +--- stream_response +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Connection: close +received: +received: truefalsenil +--- no_error_log +[error] + + + +=== TEST 61: TCP socket GC'ed in preread phase without Lua content phase --- stream_server_config lua_socket_connect_timeout 1s; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -3124,3 +3238,291 @@ connected: 1 --- error_log cleanup lua tcp socket request GC cycle done + + + +=== TEST 62: receiveany method in cosocket +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = { + '1', + '22', + 'hello world', + } + + local length = 0 + for _, v in ipairs(resp) do + length = length + #v + end + + -- flush http header + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + -- send http body + for _, v in ipairs(resp) do + ngx.print(v) + ngx.flush(true) + ngx.sleep(0.01) + end + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + -- skip http header + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ', err) + return + end + + if #data == 0 then -- read last line of head + break + end + end + + -- receive http body + while true do + local data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + break + end + ngx.say(data) + end + + sock:close() + } +--- stream_response +1 +22 +hello world +--- no_error_log +[error] +--- error_log +lua tcp socket read any + + + +=== TEST 63: receiveany send data after read side closed +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + assert(sock:connect("127.0.0.1", 7658)) + + while true do + local data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + break + end + + local data = "send data after read side closed" + local bytes, err = sock:send(data) + if not bytes then + ngx.say(err) + end + + break + end + ngx.say(data) + end + + sock:close() + } +--- tcp_listen: 7658 +--- tcp_shutdown: 1 +--- tcp_query eval: "send data after read side closed" +--- tcp_query_len: 32 +--- stream_response +--- no_error_log +[error] + + + +=== TEST 64: receiveany with limited, max <= 0 +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + + local function receiveany_say_err(...) + local ok, err = pcall(sock.receiveany, sock, ...) + if not ok then + ngx.say(err) + end + end + + + receiveany_say_err(0) + receiveany_say_err(-1) + receiveany_say_err() + receiveany_say_err(nil) + } +--- stream_response +bad argument #2 to '?' (bad max argument) +bad argument #2 to '?' (bad max argument) +expecting 2 arguments (including the object), but got 1 +bad argument #2 to '?' (bad max argument) +--- no_error_log +[error] + + + +=== TEST 65: receiveany with limited, max is larger than data +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = 'hello world' + local length = #resp + + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + ngx.print(resp) + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ', err) + return + end + + if #data == 0 then -- read last line of head + break + end + end + + local data, err = sock:receiveany(128) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + else + ngx.say(data) + end + + sock:close() + } +--- stream_response +hello world +--- no_error_log +[error] +--- error_log +lua tcp socket calling receiveany() method to read at most 128 bytes + + + +=== TEST 66: receiveany with limited, max is smaller than data +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = 'hello world' + local length = #resp + + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + ngx.print(resp) + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ', err) + return + end + + if #data == 0 then -- read last line of head + break + end + end + + while true do + local data, err = sock:receiveany(7) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + break + + else + ngx.say(data) + end + end + + sock:close() + } +--- stream_response +hello w +orld +--- no_error_log +[error] +--- error_log +lua tcp socket calling receiveany() method to read at most 7 bytes diff --git a/t/062-count.t b/t/062-count.t index 288c28e6..a1e753d5 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -30,7 +30,7 @@ __DATA__ ngx.say("ngx: ", n) } --- stream_response -ngx: 56 +ngx: 53 --- no_error_log [error] @@ -193,7 +193,7 @@ probe process("$LIBLUA_PATH").function("rehashtab") { --- stap_out2 3 --- stream_response -coroutine: 14 +coroutine: 16 --- no_error_log [error] @@ -238,7 +238,24 @@ worker: 4 -=== TEST 13: entries under the metatable of udp sockets +=== TEST 13: entries under the metatable of tcp sockets +--- stream_server_config + content_by_lua_block { + local n = 0 + local sock = ngx.socket.tcp() + for k, v in pairs(getmetatable(sock)) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 14 +--- no_error_log +[error] + + + +=== TEST 14: entries under the metatable of udp sockets --- stream_server_config content_by_lua_block { local n = 0 @@ -255,7 +272,7 @@ n = 6 -=== TEST 14: entries under the metatable of req raw sockets +=== TEST 15: entries under the metatable of req raw sockets --- stream_server_config content_by_lua_block { local n = 0 diff --git a/t/068-socket-keepalive.t b/t/068-socket-keepalive.t new file mode 100644 index 00000000..69131732 --- /dev/null +++ b/t/068-socket-keepalive.t @@ -0,0 +1,2356 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; + +plan tests => repeat_each() * (blocks() * 4) + 32; + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; +$ENV{TEST_NGINX_REDIS_PORT} ||= 6379; +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +log_level('debug'); +no_long_string(); +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- stream_server_config + content_by_lua_block { + local function go(port) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to setkeepalive: ", err) + return + end + end + + go($TEST_NGINX_MEMCACHED_PORT) + go($TEST_NGINX_MEMCACHED_PORT) + } +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +connected: 1, reused: 1 +request sent: 11 +received: OK +--- error_log eval +qq{ +lua tcp socket get keepalive peer: using connection +lua tcp socket keepalive create connection pool for key "127\.0\.0\.1:$ENV{TEST_NGINX_MEMCACHED_PORT}" +} +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for " +] + + + +=== TEST 2: free up the whole connection pool if no active connections +--- stream_server_config + content_by_lua_block { + local function go(port, keepalive) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + if keepalive then + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to setkeepalive: ", err) + return + end + + else + sock:close() + end + end + + go($TEST_NGINX_MEMCACHED_PORT, true) + go($TEST_NGINX_MEMCACHED_PORT, false) + } +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +connected: 1, reused: 1 +request sent: 11 +received: OK +--- error_log eval +[ +"lua tcp socket get keepalive peer: using connection", +"lua tcp socket keepalive: free connection pool for " +] +--- no_error_log +[error] + + + +=== TEST 3: upstream sockets close prematurely +--- config + location /foo { + server_tokens off; + keepalive_timeout 100ms; + echo foo; + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- error_log eval +[ +"lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for " +] +--- no_error_log +[error] +--- timeout: 3 + + + +=== TEST 4: http keepalive +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log eval +[ +"lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for " +] +--- timeout: 4 + + + +=== TEST 5: lua_socket_keepalive_timeout +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 100ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 100 ms", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 6: lua_socket_pool_size +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 100ms; + lua_socket_pool_size 1; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 100 ms", +qr/lua tcp socket connection pool size: 1\b/] +--- timeout: 4 + + + +=== TEST 7: "lua_socket_keepalive_timeout 0" means unlimited +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 0; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive timeout: unlimited", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 8: setkeepalive(timeout) overrides lua_socket_keepalive_timeout +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 60s; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(123) + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 123 ms", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 9: sock:setkeepalive(timeout, size) overrides lua_socket_pool_size +--- config + location /foo { + server_tokens off; + keepalive_timeout 100ms; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 60s; + lua_socket_pool_size 100; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(101, 25) + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 101 ms", +qr/lua tcp socket connection pool size: 25\b/] +--- timeout: 4 + + + +=== TEST 10: setkeepalive() 'pool_size' should be greater than zero +--- stream_server_config + content_by_lua_block { + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not sock then + ngx.say(err) + return + end + + local ok, err = pcall(sock.setkeepalive, sock, 0, 0) + if not ok then + ngx.say(err) + return + end + ngx.say(ok) + } +--- stream_response +bad argument #3 to '?' (bad "pool_size" option value: 0) +--- no_error_log +[error] + + + +=== TEST 11: sock:keepalive_timeout(0) means unlimited +--- config + location /foo { + server_tokens off; + keepalive_timeout 60s; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 1000ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(0) + if not ok then + ngx.say("failed to set reusable: ", err) + return + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- no_error_log +[error] +--- error_log eval +["lua tcp socket keepalive timeout: unlimited", +qr/lua tcp socket connection pool size: 30\b/] +--- timeout: 4 + + + +=== TEST 12: sanity (uds) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + server_tokens off; + + location /foo { + echo foo; + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local function go(path) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:" .. path) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, err = reader() + + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("$TEST_NGINX_HTML_DIR/nginx.sock") + go("$TEST_NGINX_HTML_DIR/nginx.sock") + } +--- stream_response +connected: 1, reused: 0 +request sent: 61 +received response of 119 bytes +connected: 1, reused: 1 +request sent: 61 +received response of 119 bytes +--- error_log eval +[ +"lua tcp socket get keepalive peer: using connection", +'lua tcp socket keepalive create connection pool for key "unix:' +] +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for " +] + + + +=== TEST 13: github issue #108: ngx.locaiton.capture + redis.set_keepalive +--- SKIP: ngx_http_lua only + + + +=== TEST 14: github issue #110: ngx.exit with HTTP_NOT_FOUND causes worker process to exit +--- SKIP: ngx_http_lua only + + + +=== TEST 15: custom pools (different pool for the same host:port) - tcp +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, "A") + go($TEST_NGINX_MEMCACHED_PORT, "B") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 0 +--- error_log +lua tcp socket keepalive create connection pool for key "A" +lua tcp socket keepalive create connection pool for key "B" +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket get keepalive peer: using connection" +] + + + +=== TEST 16: custom pools (same pool for different host:port) - tcp +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, "foo") + go($TEST_NGINX_MEMCACHED_PORT, "foo") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +--- error_log +lua tcp socket keepalive create connection pool for key "foo" +lua tcp socket get keepalive peer: using connection +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +] + + + +=== TEST 17: custom pools (different pool for the same host:port) - unix +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + server_tokens off; + + location /foo { + echo foo; + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local function go(pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock", { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("A") + go("B") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 0 +--- error_log +lua tcp socket keepalive create connection pool for key "A" +lua tcp socket keepalive create connection pool for key "B" +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket get keepalive peer: using connection" +] + + + +=== TEST 18: custom pools (same pool for the same path) - unix +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + server_tokens off; + + location /foo { + echo foo; + more_clear_headers Date; + } + } +--- stream_server_config + content_by_lua_block { + local function go(pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock", { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("A") + go("A") + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +--- error_log +lua tcp socket keepalive create connection pool for key "A" +lua tcp socket get keepalive peer: using connection +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +] + + + +=== TEST 19: numeric pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, 3.14) + go($TEST_NGINX_SERVER_PORT, 3.14) + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +--- error_log +lua tcp socket keepalive create connection pool for key "3.14" +lua tcp socket get keepalive peer: using connection +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +] + + + +=== TEST 20: nil pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, nil) + go($TEST_NGINX_SERVER_PORT, nil) + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 0 +--- no_error_log +[error] + + + +=== TEST 21: (bad) table pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, {}) + go($TEST_NGINX_SERVER_PORT, {}) + } +--- stream_response +--- error_log +bad argument #3 to 'connect' (bad "pool" option type: table) + + + +=== TEST 22: (bad) boolean pool option value +--- stream_server_config + content_by_lua_block { + local function go(port, pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool = pool }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go($TEST_NGINX_MEMCACHED_PORT, true) + go($TEST_NGINX_SERVER_PORT, false) + } +--- stream_response +--- error_log +bad argument #3 to 'connect' (bad "pool" option type: boolean) + + + +=== TEST 23: clear the redis store +--- SKIP: ngx_http_lua only + + + +=== TEST 24: bug in send(): clear the chain writer ctx +--- SKIP +--- stream_server_config + content_by_lua_block { + local test = require "test" + local port = ngx.var.port + test.go($TEST_NGINX_REDIS_PORT) + } +--- user_files +>>> test.lua +module("test", package.seeall) + +function go(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send({}) + if err then + ngx.say("failed to send empty request: ", err) + return + end + + local req = "*2\r\n$3\r\nget\r\n$3\r\ndog\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + + ngx.say("done") +end +--- stap2 +global active +M(stream-lua-socket-tcp-send-start) { + active = 1 + printf("send [%s] %d\n", text_str(user_string_n($arg3, $arg4)), $arg4) +} +M(stream-lua-socket-tcp-receive-done) { + printf("receive [%s]\n", text_str(user_string_n($arg3, $arg4))) +} +F(ngx_output_chain) { + #printf("ctx->in: %s\n", ngx_chain_dump($ctx->in)) + #printf("ctx->busy: %s\n", ngx_chain_dump($ctx->busy)) + printf("output chain: %s\n", ngx_chain_dump($in)) +} +F(ngx_linux_sendfile_chain) { + printf("linux sendfile chain: %s\n", ngx_chain_dump($in)) +} +F(ngx_chain_writer) { + printf("chain writer ctx out: %p\n", $data) + printf("nginx chain writer: %s\n", ngx_chain_dump($in)) +} +F(ngx_stream_lua_socket_tcp_setkeepalive) { + delete active +} +M(stream-lua-socket-tcp-setkeepalive-buf-unread) { + printf("setkeepalive unread: [%s]\n", text_str(user_string_n($arg3, $arg4))) +} +probe syscall.recvfrom { + if (active && pid() == target()) { + printf("recvfrom(%s)", argstr) + } +} +probe syscall.recvfrom.return { + if (active && pid() == target()) { + printf(" = %s, data [%s]\n", retstr, text_str(user_string_n($ubuf, $size))) + } +} +probe syscall.writev { + if (active && pid() == target()) { + printf("writev(%s)", ngx_iovec_dump($vec, $vlen)) + /* + for (i = 0; i < $vlen; i++) { + printf(" %p [%s]", $vec[i]->iov_base, text_str(user_string_n($vec[i]->iov_base, $vec[i]->iov_len))) + } + */ + } +} +probe syscall.writev.return { + if (active && pid() == target()) { + printf(" = %s\n", retstr) + } +} +--- stream_response +received: $-1 +done +--- no_error_log +[error] + + + +=== TEST 25: setkeepalive() with explicit nil args +--- config + location /foo { + server_tokens off; + keepalive_timeout 100ms; + echo foo; + } +--- stream_server_config + lua_socket_keepalive_timeout 100ms; + + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + local reader = sock:receiveuntil("\r\n0\r\n\r\n") + local data, res = reader() + + if not data then + ngx.say("failed to receive response body: ", err) + return + end + + ngx.say("received response of ", #data, " bytes") + + local ok, err = sock:setkeepalive(nil, nil) + if not ok then + ngx.say("failed to set reusable: ", err) + end + + ngx.sleep(1) + + ngx.say("done") + } +--- stream_response +connected: 1 +request sent: 61 +received response of 156 bytes +done +--- error_log eval +[ +"lua tcp socket keepalive close handler", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket keepalive timeout: 100 ms", +qr/lua tcp socket connection pool size: 30\b/ +] +--- no_error_log +[error] +--- timeout: 4 + + + +=== TEST 26: conn queuing: connect() verifies the options for connection pool +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + + local function check_opts_for_connect(opts) + local ok, err = pcall(function() + sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT, opts) + end) + if not ok then + ngx.say(err) + else + ngx.say("ok") + end + end + + check_opts_for_connect({pool_size = 'a'}) + check_opts_for_connect({pool_size = 0}) + check_opts_for_connect({backlog = -1}) + check_opts_for_connect({backlog = 0}) + } +--- stream_response_like +.+ 'connect' \(bad "pool_size" option type: string\) +.+ 'connect' \(bad "pool_size" option value: 0\) +.+ 'connect' \(bad "backlog" option value: -1\) +ok +--- no_error_log +[error] + + + +=== TEST 27: conn queuing: connect() can specify 'pool_size' which overrides setkeepalive() +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT; + + local function go() + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { pool_size = 1 }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local req = "flush_all\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock:setkeepalive(0, 20) + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + -- reuse ok + go() + go() + + local sock1 = ngx.socket.connect("127.0.0.1", port) + local sock2 = ngx.socket.connect("127.0.0.1", port) + local ok, err = sock1:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + local ok, err = sock2:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + + -- the pool_size is 1 instead of 20 + sock1 = ngx.socket.connect("127.0.0.1", port) + sock2 = ngx.socket.connect("127.0.0.1", port) + ngx.say("reused: ", sock1:getreusedtimes()) + ngx.say("reused: ", sock2:getreusedtimes()) + sock1:setkeepalive(0, 20) + sock2:setkeepalive(0, 20) + } +--- stream_response +connected: 1, reused: 0 +request sent: 11 +received: OK +connected: 1, reused: 1 +request sent: 11 +received: OK +reused: 1 +reused: 0 +--- error_log eval +[ +qq{lua tcp socket keepalive create connection pool for key "127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}"}, +"lua tcp socket connection pool size: 1", +] +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket connection pool size: 20" +] + + + +=== TEST 28: conn queuing: connect() can specify 'pool_size' for unix domain socket +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + } +--- stream_server_config + content_by_lua_block { + local path = "unix:" .. "$TEST_NGINX_HTML_DIR/nginx.sock"; + + local function go() + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect(path, { pool_size = 1 }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive(0, 20) + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go() + go() + + local sock1 = ngx.socket.connect(path) + local sock2 = ngx.socket.connect(path) + local ok, err = sock1:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + local ok, err = sock2:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + + -- the pool_size is 1 instead of 20 + sock1 = ngx.socket.connect(path) + sock2 = ngx.socket.connect(path) + ngx.say("reused: ", sock1:getreusedtimes()) + ngx.say("reused: ", sock2:getreusedtimes()) + sock1:setkeepalive(0, 20) + sock2:setkeepalive(0, 20) + } +--- stream_response +connected: 1, reused: 0 +connected: 1, reused: 1 +reused: 1 +reused: 0 +--- error_log eval +[ +"lua tcp socket get keepalive peer: using connection", +'lua tcp socket keepalive create connection pool for key "unix:', +"lua tcp socket connection pool size: 1", +] +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket connection pool size: 20" +] + + + +=== TEST 29: conn queuing: connect() can specify 'pool_size' for custom pool +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + + local function go(pool) + local sock = ngx.socket.tcp() + sock:settimeouts(1000, 1000, 1000) + + local ok, err = sock:connect("127.0.0.1", port, { + pool = pool, + pool_size = 1 + }) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", pool, ", reused: ", sock:getreusedtimes()) + + local ok, err = sock:setkeepalive(0, 20) + if not ok then + ngx.say("failed to set reusable: ", err) + end + end + + go("A") + go("B") + go("A") + go("B") + + local sock1 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + local sock2 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + local ok, err = sock1:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + local ok, err = sock2:setkeepalive(0, 20) + if not ok then + ngx.say(err) + end + + -- the pool_size is 1 instead of 20 + sock1 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + sock2 = ngx.socket.connect("127.0.0.1", port, {pool = 'A'}) + ngx.say("reused: ", sock1:getreusedtimes()) + ngx.say("reused: ", sock2:getreusedtimes()) + sock1:setkeepalive(0, 20) + sock2:setkeepalive(0, 20) + } +--- stream_response +connected: A, reused: 0 +connected: B, reused: 0 +connected: A, reused: 1 +connected: B, reused: 1 +reused: 1 +reused: 0 +--- no_error_log eval +[ +"[error]", +"lua tcp socket keepalive: free connection pool for ", +"lua tcp socket connection pool size: 20" +] +--- error_log eval +[ +qq{lua tcp socket keepalive create connection pool for key "A"}, +qq{lua tcp socket keepalive create connection pool for key "B"}, +"lua tcp socket connection pool size: 1" +] + + + +=== TEST 30: conn queuing: connect() uses lua_socket_pool_size as default if 'backlog' is given +--- stream_server_config + lua_socket_pool_size 1234; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local sock, err = ngx.socket.connect("127.0.0.1", port, { backlog = 0 }) + if not sock then + ngx.say(err) + else + ngx.say("ok") + end + } +--- stream_response +ok +--- error_log +lua tcp socket connection pool size: 1234 +--- no_error_log +[error] + + + +=== TEST 31: conn queuing: more connect operations than 'backlog' size +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + + local opts = {pool_size = 2, backlog = 0} + local sock = ngx.socket.connect("127.0.0.1", port, opts) + local not_reused_socket, err = ngx.socket.connect("127.0.0.1", port, opts) + if not not_reused_socket then + ngx.say(err) + return + end + -- burst + local ok, err = ngx.socket.connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say(err) + return + end + + ok, err = sock:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + ngx.say("reused: ", sock:getreusedtimes()) + -- both queue and pool is full + ok, err = ngx.socket.connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- stream_response +too many waiting connect operations +reused: 1 +too many waiting connect operations +--- no_error_log +[error] + + + +=== TEST 32: conn queuing: once 'pool_size' is reached and pool has 'backlog' +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 2, backlog = 2} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0, function(premature) + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock2 then + ngx.log(ngx.ERR, err) + return + end + + ngx.log(ngx.WARN, "start to handle timer") + ngx.sleep(0.1) + sock2:close() + -- resume connect operation + ngx.log(ngx.WARN, "continue to handle timer") + end) + + ngx.sleep(0.05) + ngx.log(ngx.WARN, "start to handle cosocket") + local sock3, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock3 then + ngx.say(err) + return + end + ngx.log(ngx.WARN, "continue to handle cosocket") + + local req = "flush_all\r\n" + local bytes, err = sock3:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + ngx.say("request sent: ", bytes) + + local line, err, part = sock3:receive() + if line then + ngx.say("received: ", line) + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + end + + local ok, err = sock3:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + ngx.say("setkeepalive: OK") + } +--- stream_response +request sent: 11 +received: OK +setkeepalive: OK +--- no_error_log +[error] +--- error_log +lua tcp socket queue connect operation for connection pool "127.0.0.1 +--- grep_error_log eval: qr/(start|continue) to handle \w+/ +--- grep_error_log_out +start to handle timer +start to handle cosocket +continue to handle timer +continue to handle cosocket + + + +=== TEST 33: conn queuing: do not count failed connect operations +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 0} + + local sock = ngx.socket.tcp() + sock:settimeouts(100, 3000, 3000) + local ok, err = sock:connect("127.0.0.2", 12345, opts) + if not ok then + ngx.say(err) + end + + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.say(err) + end + ngx.say("ok") + } +--- error_log +lua tcp socket connect timed out, when connecting to +--- stream_response +timeout +ok + + + +=== TEST 34: conn queuing: connect until backlog is reached +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0.01, function(premature) + ngx.log(ngx.WARN, "start to handle timer") + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock2 then + ngx.log(ngx.ERR, err) + return + end + + ngx.sleep(0.02) + local ok, err = sock2:close() + if not ok then + ngx.log(ngx.ERR, err) + end + ngx.log(ngx.WARN, "continue to handle timer") + end) + + ngx.sleep(0.02) + local sock3, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock3 then + ngx.say(err) + end + local ok, err = sock1:setkeepalive() + if not ok then + ngx.say(err) + return + end + ngx.sleep(0.01) -- run sock2 + + ngx.log(ngx.WARN, "start to handle cosocket") + local sock3, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock3 then + ngx.say(err) + return + end + ngx.log(ngx.WARN, "continue to handle cosocket") + + local ok, err = sock3:setkeepalive() + if not ok then + ngx.say(err) + end + } +--- stream_response +too many waiting connect operations +--- error_log +lua tcp socket queue connect operation for connection pool "127.0.0.1 +--- no_error_log +[error] +--- grep_error_log eval: qr/queue connect operation for connection pool|(start|continue) to handle \w+/ +--- grep_error_log_out +start to handle timer +queue connect operation for connection pool +start to handle cosocket +queue connect operation for connection pool +continue to handle timer +continue to handle cosocket + + + +=== TEST 35: conn queuing: memory reuse for host in queueing connect operation ctx +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 3} + local sock = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0.01, function(premature) + local sock, err = ngx.socket.connect("0.0.0.0", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + end) + + ngx.timer.at(0.015, function(premature) + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + end) + + ngx.timer.at(0.02, function(premature) + local sock, err = ngx.socket.connect("0.0.0.0", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + end) + + ngx.sleep(0.03) + local ok, err = sock:setkeepalive() + if not ok then + ngx.say(err) + return + end + ngx.say("ok") + } +--- stream_response +ok +--- grep_error_log eval: qr/queue connect operation for connection pool/ +--- grep_error_log_out +queue connect operation for connection pool +queue connect operation for connection pool +queue connect operation for connection pool +--- no_error_log +[error] + + + +=== TEST 36: conn queuing: connect() returns error after connect operation resumed +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + local sock = ngx.socket.connect("127.0.0.1", port, opts) + + ngx.timer.at(0, function(premature) + local sock, err = ngx.socket.connect("", port, opts) + if not sock then + ngx.log(ngx.WARN, err) + end + end) + + ngx.sleep(0.01) + -- use 'close' to force parsing host instead of reusing conn + local ok, err = sock:close() + if not ok then + ngx.say(err) + return + end + ngx.say("ok") + } +--- stream_response +ok +--- error_log +failed to parse host name +--- no_error_log +[error] +--- grep_error_log eval: qr/queue connect operation for connection pool/ +--- grep_error_log_out +queue connect operation for connection pool + + + +=== TEST 37: conn queuing: in uthread +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 2} + + local conn_sock = function() + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.say(err) + return + end + ngx.say("start to handle uthread") + + ngx.sleep(0.01) + sock:close() + ngx.say("continue to handle other uthread") + end + + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local co1 = ngx.thread.spawn(conn_sock) + local co2 = ngx.thread.spawn(conn_sock) + local co3 = ngx.thread.spawn(conn_sock) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.log(ngx.ERR, err) + end + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.thread.wait(co3) + ngx.say("all uthreads ok") + } +--- stream_response +too many waiting connect operations +start to handle uthread +continue to handle other uthread +start to handle uthread +continue to handle other uthread +all uthreads ok +--- no_error_log +[error] +--- grep_error_log eval: qr/queue connect operation for connection pool/ +--- grep_error_log_out +queue connect operation for connection pool +queue connect operation for connection pool + + + +=== TEST 38: conn queuing: in access_by_lua +--- SKIP: ngx_http_lua only + + + +=== TEST 39: conn queuing: in rewrite_by_lua +--- SKIP: ngx_http_lua only + + + +=== TEST 40: conn queuing: in subrequest +--- SKIP: ngx_http_lua only + + + +=== TEST 41: conn queuing: timeouts when 'connect_timeout' is reached +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + local sock2 = ngx.socket.tcp() + sock2:settimeouts(10, 3000, 3000) + local ok, err = sock2:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- stream_response +timeout +--- error_log eval +"lua tcp socket queued connect timed out, when trying to connect to 127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}" + + + +=== TEST 42: conn queuing: set timeout via lua_socket_connect_timeout +--- stream_server_config + lua_socket_connect_timeout 10ms; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + local sock2 = ngx.socket.tcp() + local ok, err = sock2:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- stream_response +timeout +--- error_log eval +"lua tcp socket queued connect timed out, when trying to connect to 127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}" + + + +=== TEST 43: conn queuing: client aborting while connect operation is queued +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool_size = 1, backlog = 1} + local sock1 = ngx.socket.connect("127.0.0.1", port, opts) + + local sock2 = ngx.socket.tcp() + sock2:settimeouts(3000, 3000, 3000) + local ok, err = sock2:connect("127.0.0.1", port, opts) + if not ok then + ngx.say(err) + end + } +--- ignore_stream_response +--- timeout: 0.1 +--- abort +--- no_error_log +[error] + + + +=== TEST 44: conn queuing: resume next connect operation if resumed connect failed immediately +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 2} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + ok, err = sock:connect("", port, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local sock, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock then + ngx.log(ngx.ERR, err) + return + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + local ok, err = sock:close() + if not ok then + ngx.log(ngx.ERR, err) + end + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +failed to parse host name "": no host +connected in uthread +ok +--- no_error_log +[error] + + + +=== TEST 45: conn queuing: resume connect operation if resumed connect failed (timeout) +--- stream_server_config + resolver $TEST_NGINX_RESOLVER ipv6=off; + resolver_timeout 3s; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + sock:settimeouts(100, 3000, 3000) + ok, err = sock:connect("127.0.0.2", 12345, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +timeout +connected in uthread +ok +--- error_log +queue connect operation for connection pool "test" +lua tcp socket connect timed out, when connecting to + + + +=== TEST 46: conn queuing: resume connect operation if resumed connect failed (could not be resolved) +--- stream_server_config + resolver 127.0.0.2:12345 ipv6=off; + resolver_timeout 1s; + + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + sock:settimeouts(1, 3000, 3000) + ok, err = sock:connect("agentzh.org", 80, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +agentzh.org could not be resolved (110: Operation timed out) +connected in uthread +ok +--- error_log +queue connect operation for connection pool "test" + + + +=== TEST 47: conn queuing: resume connect operation if resumed connect failed (connection refused) +--- stream_server_config + content_by_lua_block { + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + local conn_sock = function(should_timeout) + local sock = ngx.socket.tcp() + local ok, err + if should_timeout then + sock:settimeouts(100, 3000, 3000) + ok, err = sock:connect("127.0.0.1", 62345, opts) + else + ok, err = sock:connect("127.0.0.1", port, opts) + end + if not ok then + ngx.say(err) + return + end + ngx.say("connected in uthread") + sock:close() + end + + local co1 = ngx.thread.spawn(conn_sock, true) + local co2 = ngx.thread.spawn(conn_sock) + + ngx.thread.wait(co1) + ngx.thread.wait(co2) + ngx.say("ok") + } +--- stream_response +connection refused +connected in uthread +ok +--- error_log +queue connect operation for connection pool "test" + + + +=== TEST 48: conn queuing: resume connect operation if resumed connect failed (uthread aborted while resolving) +--- stream_server_config + resolver 127.0.0.1 ipv6=off; + resolver_timeout 100s; + + content_by_lua_block { + local function sub() + local semaphore = require "ngx.semaphore" + local sem = semaphore.new() + + local function f() + sem:wait(0.1) + ngx.exit(0) + end + + local port = $TEST_NGINX_MEMCACHED_PORT + local opts = {pool = "test", pool_size = 1, backlog = 1} + + ngx.timer.at(0, function() + sem:post() + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + package.loaded.for_timer_to_resume:post() + if not sock2 then + ngx.log(ngx.ALERT, "resume connect failed: ", err) + return + end + + ngx.log(ngx.INFO, "resume success") + end) + + ngx.thread.spawn(f) + local sock1, err = ngx.socket.connect("openresty.org", 80, opts) + if not sock1 then + ngx.say(err) + return + end + end + + local function t() + local semaphore = require "ngx.semaphore" + local for_timer_to_resume = semaphore.new() + package.loaded.for_timer_to_resume = for_timer_to_resume + + ngx.thread.spawn(sub) + for_timer_to_resume:wait(0.1) + end + + t() + } +--- no_error_log +[alert] +--- error_log +resume success + + + +=== TEST 49: conn queuing: resume connect operation if resumed connect failed (uthread killed while resolving) +--- stream_server_config + resolver 127.0.0.1 ipv6=off; + resolver_timeout 100s; + + content_by_lua_block { + local opts = {pool = "test", pool_size = 1, backlog = 1} + local port = $TEST_NGINX_MEMCACHED_PORT + + local function resolve() + local sock1, err = ngx.socket.connect("openresty.org", 80, opts) + if not sock1 then + ngx.say(err) + return + end + end + + local th = ngx.thread.spawn(resolve) + local ok, err = ngx.thread.kill(th) + if not ok then + ngx.log(ngx.ALERT, "kill thread failed: ", err) + return + end + + local sock2, err = ngx.socket.connect("127.0.0.1", port, opts) + if not sock2 then + ngx.log(ngx.ALERT, "resume connect failed: ", err) + return + end + + ngx.log(ngx.INFO, "resume success") + } +--- no_error_log +[alert] +--- error_log +resume success + + + +=== TEST 50: conn queuing: increase the counter for connections created before creating the pool with setkeepalive() +--- stream_server_config + content_by_lua_block { + local function connect() + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not sock then + error("connect failed: " .. err) + end + + return sock + end + + local sock1 = connect() + local sock2 = connect() + assert(sock1:setkeepalive()) + assert(sock2:setkeepalive()) + + local sock1 = connect() + local sock2 = connect() + assert(sock1:close()) + assert(sock2:close()) + + ngx.say("ok") + } +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 51: conn queuing: only decrease the counter for connections which were counted by the pool +--- stream_server_config + content_by_lua_block { + local function connect() + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not sock then + error("connect failed: " .. err) + end + + return sock + end + + local sock1 = connect() + local sock2 = connect() + assert(sock1:setkeepalive(1000, 1)) + assert(sock2:setkeepalive(1000, 1)) + + local sock1 = connect() + local sock2 = connect() + assert(sock1:close()) + assert(sock2:close()) + + ngx.say("ok") + } +--- stream_response +ok +--- no_error_log +[error] + + + +=== TEST 52: conn queuing: clean up pending connect operations which are in queue +--- stream_server_config + content_by_lua_block { + local function sub() + local opts = {pool = "test", pool_size = 1, backlog = 1} + local sock, err = ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT, opts) + if not sock then + ngx.say("connect failed: " .. err) + return + end + + local function f() + assert(ngx.socket.connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT, opts)) + end + + local th = ngx.thread.spawn(f) + local ok, err = ngx.thread.kill(th) + if not ok then + ngx.log(ngx.ERR, "kill thread failed: ", err) + return + end + + sock:close() + end + + local function t() + ngx.thread.spawn(sub) + -- let pending connect operation resumes first + ngx.sleep(0) + ngx.say("ok") + end + + t() + } +--- stream_response +ok +--- error_log +lua tcp socket abort queueing +--- no_error_log +[error] diff --git a/t/087-udp-socket.t b/t/087-udp-socket.t index 5520aa3a..e768ad57 100644 --- a/t/087-udp-socket.t +++ b/t/087-udp-socket.t @@ -792,10 +792,9 @@ qr/runtime error: content_by_lua\(nginx\.conf:\d+\):13: bad request/ else ngx.say("peer set") end - local function f() local sock = test.get_sock() - sock:send("a") + sock:close() end ngx.timer.at(0, f) ngx.sleep(0.001) @@ -818,73 +817,71 @@ end peer set --- error_log eval -qr/runtime error: content_by_lua\(nginx\.conf:\d+\):13: bad request/ +qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ --- no_error_log [alert] -=== TEST 17: bad request tries to receive +=== TEST 17: the upper bound of port range should be 2^16 - 1 --- stream_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- stream_server_config content_by_lua_block { - local test = require "test" - local sock = test.new_sock() - local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + local sock = ngx.socket.udp() + local ok, err = sock:setpeername("127.0.0.1", 65536) if not ok then - ngx.say("failed to set peer: ", err) - else - ngx.say("peer set") - end - function f() - local sock = test.get_sock() - sock:close() + ngx.say("failed to connect: ", err) end - ngx.timer.at(0, f) - ngx.sleep(0.001) } ---- user_files ->>> test.lua -module("test", package.seeall) - -local sock - -function new_sock() - sock = ngx.socket.udp() - return sock -end - -function get_sock() - return sock -end --- stream_response -peer set - ---- error_log eval -qr/runtime error: content_by_lua\(nginx\.conf:\d+\):12: bad request/ - +failed to connect: bad port number: 65536 --- no_error_log -[alert] +[error] -=== TEST 18: the upper bound of port range should be 2^16 - 1 ---- stream_config eval - "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +=== TEST 18: send boolean and nil --- stream_server_config content_by_lua_block { - local sock = ngx.socket.udp() - local ok, err = sock:setpeername("127.0.0.1", 65536) + local socket = ngx.socket + local udp = socket.udp() + local port = ngx.var.port + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) if not ok then ngx.say("failed to connect: ", err) + return end + + local function send(data) + local bytes, err = udp:send(data) + if not bytes then + ngx.say("failed to send: ", err) + return + end + ngx.say("sent ok") + end + + send(true) + send(false) + send(nil) } --- stream_response -failed to connect: bad port number: 65536 +sent ok +sent ok +sent ok --- no_error_log [error] +--- grep_error_log eval +qr/sendto: fd:\d+ \d+ of \d+/ +--- grep_error_log_out eval +qr/sendto: fd:\d+ 4 of 4 +sendto: fd:\d+ 5 of 5 +sendto: fd:\d+ 3 of 3/ +--- log_level: debug diff --git a/t/091-coroutine.t b/t/091-coroutine.t index ebe1d05c..fa6b85a7 100644 --- a/t/091-coroutine.t +++ b/t/091-coroutine.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua::Stream; repeat_each(2); -plan tests => repeat_each() * (blocks() * 3 + 4); +plan tests => repeat_each() * (blocks() * 3 + 27); $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; @@ -449,22 +449,24 @@ done === TEST 10: thread traceback (multi-thread) +Note: only coroutine.wrap propagates errors to the parent coroutine +(and thus produces a traceback) --- stream_server_config content_by_lua_block { local f = function(cr) coroutine.resume(cr) end -- emit a error local g = function() unknown.unknown = 1 end - local l1 = coroutine.create(f) - local l2 = coroutine.create(g) - coroutine.resume(l1, l2) + local l1 = coroutine.wrap(f) + local l2 = coroutine.wrap(g) + local l3 = coroutine.wrap(function() l1(l2) end) + l3() ngx.say("hello") } - ---- config --- stream_response -hello --- error_log eval ["stack traceback:", "coroutine 0:", "coroutine 1:", "coroutine 2:"] +--- no_error +[crit] @@ -779,8 +781,8 @@ qr/^child: resume: falsecontent_by_lua\(nginx\.conf:\d+\):4: bad child: status: dead parent: status: running $/s ---- error_log eval -qr/lua coroutine: runtime error: content_by_lua\(nginx\.conf:\d+\):4: bad/ +--- no_error_log +[error] @@ -1090,3 +1092,315 @@ Hello, 2 *** --- no_error_log [error] + + + +=== TEST 28: coroutine.wrap propagates errors to parent coroutine +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + print("in wrapped coroutine") + error("something went wrong") + end) + + co() + + ngx.say("ok") + } +--- stream_response +--- error_log eval +[ + qr/\[notice\] .*? in wrapped coroutine/, + qr/\[error\] .*? lua entry thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):\d+: something went wrong/, + "stack traceback:", + "coroutine 0:", + "coroutine 1:" +] + + + +=== TEST 29: coroutine.wrap propagates nested errors to parent coroutine +Note: in this case, both the error message and the traceback are constructed +from co1's stack level. +--- stream_server_config + content_by_lua_block { + local co1 = coroutine.wrap(function() + error("something went wrong in co1") + end) + + local co2 = coroutine.wrap(function() + co1() + end) + + co2() + } +--- stream_response +--- error_log eval +[ + qr/\[error\] .*? lua entry thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):\d+: something went wrong in co1/, + "stack traceback:", + "coroutine 0:", + "coroutine 1:", + "coroutine 2:" +] + + + +=== TEST 30: coroutine.wrap propagates nested errors with stack level to parent coroutine +Note: in this case, the error message is constructed at the entry thread stack +level, and the traceback is constructed from co1's stack level. +--- stream_server_config + content_by_lua_block { + local co1 = coroutine.wrap(function() + error("something went wrong in co1", 2) + end) + + local co2 = coroutine.wrap(function() + co1() + end) + + co2() + } +--- stream_response +--- error_log eval +[ + qr/\[error\] .*? lua entry thread aborted: runtime error: something went wrong in co1/, + "stack traceback:", + "coroutine 0:", + "coroutine 1:", + "coroutine 2:" +] + + + +=== TEST 31: coroutine.wrap runtime errors do not log errors +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + print("in wrapped coroutine") + error("something went wrong") + end) + + co() + } +--- stream_response +--- no_error_log eval +[ + qr/\[error\] .*? lua coroutine: runtime error:/, + "[alert]", + "[crit]", + "[emerg]", + "[warn]", +] + + + +=== TEST 32: coroutine.wrap does not return status boolean on yield +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + coroutine.yield("ok", "err") + end) + + local ret1, ret2 = co() + + ngx.say(ret1, ", ", ret2) + } +--- stream_response +ok, err +--- no_error_log +[error] + + + +=== TEST 33: coroutine.wrap does not return status boolean on done +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() end) + + local ret1 = co() + + ngx.say(ret1) + } +--- stream_response +nil +--- no_error_log +[emerg] +[alert] +[crit] +[error] +[warn] + + + +=== TEST 34: coroutine.wrap does not return status boolean on error +--- SKIP: not supported +--- stream_server_config + content_by_lua_block { + local co = coroutine.wrap(function() + error("something went wrong") + end) + + local ret1, ret2 = pcall(co) + + ngx.say(ret1, ", ", ret2) + } +--- response_body +false, something went wrong +--- no_error_log +[error] + + + +=== TEST 35: coroutine.wrap creates different function refs +--- stream_server_config + content_by_lua_block { + local f = function() end + local co = coroutine.wrap(f) + local co2 = coroutine.wrap(f) + + ngx.say("co == co2: ", co == co2) + } +--- stream_response +co == co2: false +--- no_error_log +[emerg] +[alert] +[crit] +[error] +[warn] + + + +=== TEST 36: coroutine.wrap supports yielding and resuming +--- stream_server_config + content_by_lua_block { + local function f() + local cnt = 0 + for i = 1, 20 do + ngx.say("co yield: ", cnt) + coroutine.yield() + cnt = cnt + 1 + end + end + + local f = coroutine.wrap(f) + for i = 1, 3 do + ngx.say("co resume") + f() + end + } +--- stream_response +co resume +co yield: 0 +co resume +co yield: 1 +co resume +co yield: 2 +--- no_error_log +[error] + + + +=== TEST 37: coroutine.wrap return values +--- stream_server_config + content_by_lua_block { + local function f() + local cnt = 0 + for i = 1, 20 do + coroutine.yield(cnt, cnt + 1) + cnt = cnt + 1 + end + end + + local f = coroutine.wrap(f) + for i = 1, 3 do + ngx.say("co resume") + local ret1, ret2 = f() + ngx.say("co yield: ", ret1, ", ", ret2) + end + } +--- stream_response +co resume +co yield: 0, 1 +co resume +co yield: 1, 2 +co resume +co yield: 2, 3 +--- no_error_log +[error] + + + +=== TEST 38: coroutine.wrap arguments +--- stream_server_config + content_by_lua_block { + local function f(step) + local cnt = 0 + for i = 1, 20 do + ngx.say("co yield: ", cnt) + coroutine.yield() + cnt = cnt + step + end + end + + local f = coroutine.wrap(f) + for i = 1, 3 do + ngx.say("co resume") + f(i) + end + } +--- stream_response +co resume +co yield: 0 +co resume +co yield: 1 +co resume +co yield: 2 +--- no_error_log +[error] + + + +=== TEST 39: coroutine.wrap in init_by_lua propagates errors (orig coroutine.wrap) +--- stream_config + init_by_lua_block { + local co = coroutine.wrap(function() + print("in wrapped coroutine") + error("something went wrong") + end) + + local err = co() + + ngx.log(ngx.CRIT, "err: ", err) + } +--- stream_server_config + content_by_lua_block { + ngx.say("ok") + } +--- must_die +--- grep_error_log eval: qr/init_by_lua error: .*? something went wrong/ +--- grep_error_log_out +init_by_lua error: init_by_lua:7: init_by_lua:4: something went wrong + + + +=== TEST 40: coroutine.resume runtime errors do not log errors +--- stream_server_config + content_by_lua_block { + local function f() + error("something went wrong") + end + + local ret1, ret2 = coroutine.resume(coroutine.create(f)) + ngx.say(ret1) + ngx.say(ret2) + } +--- stream_response_like +false +content_by_lua\(nginx.conf:\d+\):\d+: something went wrong +--- no_error_log eval +[ + qr/\[error\] .*? lua coroutine: runtime error:", + "stack traceback:", +] diff --git a/t/106-timer.t b/t/106-timer.t index f5752fed..8b66a0cb 100644 --- a/t/106-timer.t +++ b/t/106-timer.t @@ -12,7 +12,7 @@ our $StapScript = $t::StapThread::StapScript; repeat_each(2); -plan tests => repeat_each() * (blocks() * 8 + 49); +plan tests => repeat_each() * (blocks() * 8 + 46); #no_diff(); no_long_string(); @@ -1787,3 +1787,126 @@ ok --- error_log Bad bad bad --- skip_nginx: 4: < 1.7.1 + + + +=== TEST 30: log function location when failed to run a timer +--- stream_config + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local function g() + ngx.sleep(0.01) + end + + local function f() + ngx.sleep(0.01) + end + + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to create timer f: ", err) + return + end + + local ok, err = ngx.timer.at(0, g) + if not ok then + ngx.say("failed to create timer g: ", err) + return + end + + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.1 +--- error_log eval +qr/\[alert\] .*? lua failed to run timer with function defined at =content_by_lua\(nginx.conf:\d+\):2: stream lua: 1 lua_max_running_timers are not enough/ +--- no_error_log +[emerg] +[crit] +[error] +[warn] + + + +=== TEST 31: log function location when failed to run a timer (anonymous function) +--- stream_config + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local function f() + ngx.sleep(0.01) + end + + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer f: ", err) + return + end + + local ok, err = ngx.timer.at(0, function() + ngx.sleep(0.01) + end) + + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.1 +--- error_log eval +qr/\[alert\] .*? lua failed to run timer with function defined at =content_by_lua\(nginx.conf:\d+\):12: stream lua: 1 lua_max_running_timers are not enough/ +--- no_error_log +[emerg] +[crit] +[error] +[warn] + + + +=== TEST 32: log function location when failed to run a timer (lua file) +--- user_files +>>> test.lua +local _M = {} + +function _M.run() + ngx.sleep(0.01) +end + +return _M +--- stream_config + lua_package_path '$TEST_NGINX_HTML_DIR/?.lua;./?.lua;;'; + lua_max_running_timers 1; +--- stream_server_config + content_by_lua_block { + local test = require "test" + + local ok, err = ngx.timer.at(0, test.run) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + local ok, err = ngx.timer.at(0, test.run) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + + ngx.say("ok") + } +--- stream_response +ok +--- wait: 0.1 +--- error_log eval +qr/\[alert\] .*? lua failed to run timer with function defined at @.+\/test.lua:3: stream lua: 1 lua_max_running_timers are not enough/ +--- no_error_log +[emerg] +[crit] +[error] +[warn] diff --git a/t/124-init-worker.t b/t/124-init-worker.t index f356c162..e80f5203 100644 --- a/t/124-init-worker.t +++ b/t/124-init-worker.t @@ -699,3 +699,21 @@ This also affects merge_loc_conf ok --- no_error_log [error] + + + +=== TEST 20: ensure it does not mutate another module's main_conf (github issue #1553) +https://github.com/openresty/lua-nginx-module/issues/1553 +--- stream_config + init_worker_by_lua_block { + return + } +--- stream_server_config + content_by_lua_block { + ngx.say("fake_var = ", ngx.var.fake_var) + } +--- stream_response +fake_var = 1 +--- no_error_log +[error] +[alert] diff --git a/t/156-slow-network.t b/t/156-slow-network.t new file mode 100644 index 00000000..c153d491 --- /dev/null +++ b/t/156-slow-network.t @@ -0,0 +1,131 @@ +BEGIN { + if (!defined $ENV{LD_PRELOAD}) { + $ENV{LD_PRELOAD} = ''; + } + + if ($ENV{LD_PRELOAD} !~ /\bmockeagain\.so\b/) { + $ENV{LD_PRELOAD} = "mockeagain.so $ENV{LD_PRELOAD}"; + } + + if ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; +} + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4); + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + + +log_level("debug"); +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: receiveany returns anything once socket receives +--- config + location = /foo { + server_tokens off; + + content_by_lua_block { + local resp = { + '1', + 'hello', + } + + local length = 0 + for _, v in ipairs(resp) do + length = length + #v + end + + -- flush http header + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + -- send http body bytes by bytes + for _, v in ipairs(resp) do + ngx.print(v) + ngx.flush(true) + ngx.sleep(0.01) + end + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + + assert(sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + + -- skip http header + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ' .. err) + return + end + if #data == 0 then -- read last line of head + break + end + end + + -- receive http body + while true do + local data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + break + end + ngx.say(data) + end + + sock:close() + } +--- stream_response +1 +h +e +l +l +o +--- grep_error_log eval +qr/lua tcp socket read any/ +--- grep_error_log_out +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any diff --git a/t/157-socket-keepalive-hup.t b/t/157-socket-keepalive-hup.t new file mode 100644 index 00000000..00f54fe9 --- /dev/null +++ b/t/157-socket-keepalive-hup.t @@ -0,0 +1,80 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use Test::Nginx::Socket::Lua::Stream $SkipReason ? (skip_all => $SkipReason) : (); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 8); + +worker_connections(1024); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: exiting +--- stream_server_config + content_by_lua_block { + local f, err = io.open("t/servroot/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local i = 0 + + local function f(premature) + print("timer prematurely expired: ", premature) + + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + print("failed to connect: ", err) + return + end + + local ok, err = sock:setkeepalive() + if not ok then + print("failed to setkeepalive: ", err) + return + end + + print("setkeepalive successfully") + end + local ok, err = ngx.timer.at(3, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + os.execute("kill -HUP " .. pid) + } +--- stream_response +registered timer +--- wait: 0.3 +--- no_error_log +[error] +[alert] +[crit] +--- error_log +timer prematurely expired: true +setkeepalive successfully +lua tcp socket set keepalive while process exiting, closing connection diff --git a/t/158-global-var.t b/t/158-global-var.t index 8b3de761..6188ee44 100644 --- a/t/158-global-var.t +++ b/t/158-global-var.t @@ -94,9 +94,9 @@ qr/^(2|3)$/ --- stream_response_like chomp \A[12]\n\z --- grep_error_log eval -qr/(old foo: \d+|\[\w+\].*?writing a global lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk)/ +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk)/ --- grep_error_log_out eval -[qr/\A\[warn\] .*?writing a global lua variable \('foo'\) +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) preread_by_lua\(nginx\.conf:\d+\):3: in main chunk/, "old foo: 1\n"] @@ -115,9 +115,9 @@ preread_by_lua\(nginx\.conf:\d+\):3: in main chunk/, "old foo: 1\n"] --- stream_response_like chomp \A[12]\n\z --- grep_error_log eval -qr/(old foo: \d+|\[\w+\].*?writing a global lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk, )/ +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk, )/ --- grep_error_log_out eval -[qr/\A\[warn\] .*?writing a global lua variable \('foo'\) +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) content_by_lua\(nginx\.conf:\d+\):3: in main chunk, \n\z/, "old foo: 1\n"] @@ -138,9 +138,9 @@ content_by_lua\(nginx\.conf:\d+\):3: in main chunk, \n\z/, "old foo: 1\n"] --- stream_response_like chomp \A(?:nil|1)\n\z --- grep_error_log eval -qr/(old foo: \d+|\[\w+\].*?writing a global lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk)/ +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk)/ --- grep_error_log_out eval -[qr/\A\[warn\] .*?writing a global lua variable \('foo'\) +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) log_by_lua\(nginx\.conf:\d+\):3: in main chunk/, "old foo: 1\n"] @@ -167,9 +167,9 @@ log_by_lua\(nginx\.conf:\d+\):3: in main chunk/, "old foo: 1\n"] --- stream_response_like chomp \A[12]\n\z --- grep_error_log eval -qr/(old foo: \d+|\[\w+\].*?writing a global lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in\b)/ +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in\b)/ --- grep_error_log_out eval -[qr/\A\[warn\] .*?writing a global lua variable \('foo'\) +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) content_by_lua\(nginx\.conf:\d+\):4: in\n\z/, "old foo: 1\n"] @@ -196,9 +196,9 @@ content_by_lua\(nginx\.conf:\d+\):4: in\n\z/, "old foo: 1\n"] --- stream_response_like chomp \A[12]\n\z --- grep_error_log eval -qr/(old foo: \d+|writing a global lua variable \('\w+'\))/ +qr/(old foo: \d+|writing a global Lua variable \('\w+'\))/ --- grep_error_log_out eval -["writing a global lua variable \('foo'\)\n", "old foo: 1\n"] +["writing a global Lua variable \('foo'\)\n", "old foo: 1\n"] @@ -217,9 +217,9 @@ qr/(old foo: \d+|writing a global lua variable \('\w+'\))/ } --- stream_server_config proxy_pass backend; ---- grep_error_log eval: qr/(old foo: \d+|writing a global lua variable \('\w+'\))/ +--- grep_error_log eval: qr/(old foo: \d+|writing a global Lua variable \('\w+'\))/ --- grep_error_log_out eval -["writing a global lua variable \('foo'\)\n", "old foo: 1\n"] +["writing a global Lua variable \('foo'\)\n", "old foo: 1\n"] --- error_log connect() to 0.0.0.1:1234 failed @@ -239,9 +239,9 @@ connect() to 0.0.0.1:1234 failed } --- stream_response 0 ---- grep_error_log eval: qr/writing a global lua variable \('\w+'\)/ +--- grep_error_log eval: qr/writing a global Lua variable \('\w+'\)/ --- grep_error_log_out eval -["writing a global lua variable \('foo'\)\n", "writing a global lua variable \('1'\)\n"] +["writing a global Lua variable \('foo'\)\n", "writing a global Lua variable \('1'\)\n"] diff --git a/t/161-load-resty-core.t b/t/161-load-resty-core.t index fc1a1156..c2394924 100644 --- a/t/161-load-resty-core.t +++ b/t/161-load-resty-core.t @@ -19,7 +19,7 @@ run_tests(); __DATA__ -=== TEST 1: lua_load_resty_core is enabled by default +=== TEST 1: lua_load_resty_core is automatically loaded in the Lua VM --- stream_server_config content_by_lua_block { local loaded_resty_core = package.loaded["resty.core"] @@ -32,36 +32,37 @@ resty.core loaded: true -=== TEST 2: lua_load_resty_core can be disabled +=== TEST 2: resty.core is automatically loaded in the Lua VM when 'lua_shared_dict' is used --- stream_config - lua_load_resty_core off; + lua_shared_dict dogs 128k; --- stream_server_config content_by_lua_block { local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" - ngx.say("resty.core loaded: ", loaded_resty_core ~= nil) + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) } --- stream_response -resty.core loaded: false +resty.core loaded: true -=== TEST 3: lua_load_resty_core is effective when using lua_shared_dict +=== TEST 3: resty.core is automatically loaded in the Lua VM with 'lua_code_cache off' --- stream_config - lua_shared_dict dogs 128k; + lua_code_cache off; --- stream_server_config content_by_lua_block { local loaded_resty_core = package.loaded["resty.core"] local resty_core = require "resty.core" - ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + ngx.say("resty.core loaded: ", loaded_resty_core ~= nil) } --- stream_response resty.core loaded: true -=== TEST 4: lua_load_resty_core honors the lua_package_path directive +=== TEST 4: resty.core loading honors the lua_package_path directive --- stream_config eval "lua_package_path '$::HtmlDir/?.lua;;';" --- stream_server_config @@ -86,208 +87,63 @@ return { -=== TEST 5: lua_load_resty_core 'on' in stream block and 'off' in http block ---- http_config - lua_load_resty_core off; ---- config - location = /t2 { - content_by_lua_block { - local loaded_resty_core = package.loaded["resty.core"] - - local msg = "resty.core loaded in http: " .. tostring(loaded_resty_core ~= nil) - ngx.header["Content-Length"] = #msg - ngx.say(msg) - } - } +=== TEST 5: resty.core not loading aborts the initialization +--- stream_config eval + "lua_package_path '$::HtmlDir/?.lua;';" --- stream_server_config content_by_lua_block { - local loaded_resty_core = package.loaded["resty.core"] - local resty_core = require "resty.core" - - ngx.say("resty.core loaded in stream: ", loaded_resty_core == resty_core) - - local sock = ngx.socket.tcp() - local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) - if not ok then - ngx.say("failed to connect: ", err) - return - end - - local req = "GET /t2 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" - - local bytes, err = sock:send(req) - if not bytes then - ngx.say("failed to send request: ", err) - return - end - - local cl - - while true do - local line, err, part = sock:receive("*l") - if err then - ngx.say("failed to receive headers: ", err, " [", part, "]") - break - end - - local k, v = line:match("([^:]*):%s*(.*)") - if k == "Content-Length" then - cl = v - end - - if line == "" then - local body, err = sock:receive(cl) - if err then - ngx.say("failed to receive body: ", err) - break - end - - ngx.say(body) - break - end - end - - ok, err = sock:close() + ngx.say("ok") } ---- stream_response -resty.core loaded in stream: true -resty.core loaded in http: false +--- must_die +--- error_log eval +qr/\[alert\] .*? failed to load the 'resty\.core' module .*? \(reason: module 'resty\.core' not found:/ -=== TEST 6: lua_load_resty_core 'off' in stream block and 'on' in http block ---- config - location = /t2 { - content_by_lua_block { - local loaded_resty_core = package.loaded["resty.core"] - local resty_core = require "resty.core" +=== TEST 6: resty.core not loading produces an error with 'lua_code_cache off' +--- stream_config + lua_code_cache off; - local msg = "resty.core loaded in http: " .. tostring(loaded_resty_core == resty_core) - ngx.header["Content-Length"] = #msg - ngx.say(msg) - } + init_by_lua_block { + package.path = "" } ---- stream_config - lua_load_resty_core off; --- stream_server_config content_by_lua_block { - local loaded_resty_core = package.loaded["resty.core"] - - ngx.say("resty.core loaded in stream: ", loaded_resty_core ~= nil) - - local sock = ngx.socket.tcp() - local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) - if not ok then - ngx.say("failed to connect: ", err) - return - end - - local req = "GET /t2 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" - - local bytes, err = sock:send(req) - if not bytes then - ngx.say("failed to send request: ", err) - return - end - - local cl - - while true do - local line, err, part = sock:receive("*l") - if err then - ngx.say("failed to receive headers: ", err, " [", part, "]") - break - end - - local k, v = line:match("([^:]*):%s*(.*)") - if k == "Content-Length" then - cl = v - end - - if line == "" then - local body, err = sock:receive(cl) - if err then - ngx.say("failed to receive body: ", err) - break - end - - ngx.say(body) - break - end - end - - ok, err = sock:close() + ngx.say("ok") } ---- stream_response -resty.core loaded in stream: false -resty.core loaded in http: true +--- error_log eval +qr/\[error\] .*? failed to load the 'resty\.core' module .*? \(reason: module 'resty\.core' not found:/ +--- no_error_log eval +qr/\[alert\] .*? failed to load the 'resty\.core' module/ -=== TEST 7: lua_load_resty_core 'off' in stream block and 'off' in http block ---- http_config - lua_load_resty_core off; ---- config - location = /t2 { - content_by_lua_block { - local loaded_resty_core = package.loaded["resty.core"] - - local msg = "resty.core loaded in http: " .. tostring(loaded_resty_core ~= nil) - ngx.header["Content-Length"] = #msg - ngx.say(msg) - } +=== TEST 7: lua_load_resty_core logs a deprecation warning when specified (on) +--- stream_config + lua_load_resty_core on; +--- stream_server_config + content_by_lua_block { + ngx.say("ok") } +--- grep_error_log eval: qr/\[warn\] .*? lua_load_resty_core is deprecated.*/ +--- grep_error_log_out eval +[ +qr/\[warn\] .*? lua_load_resty_core is deprecated \(the lua-resty-core library is required since ngx_stream_lua v0\.0\.8\) in .*?nginx\.conf:\d+/, +"" +] + + + +=== TEST 8: lua_load_resty_core logs a deprecation warning when specified (off) --- stream_config lua_load_resty_core off; --- stream_server_config content_by_lua_block { - local loaded_resty_core = package.loaded["resty.core"] - - ngx.say("resty.core loaded in stream: ", loaded_resty_core ~= nil) - - local sock = ngx.socket.tcp() - local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) - if not ok then - ngx.say("failed to connect: ", err) - return - end - - local req = "GET /t2 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" - - local bytes, err = sock:send(req) - if not bytes then - ngx.say("failed to send request: ", err) - return - end - - local cl - - while true do - local line, err, part = sock:receive("*l") - if err then - ngx.say("failed to receive headers: ", err, " [", part, "]") - break - end - - local k, v = line:match("([^:]*):%s*(.*)") - if k == "Content-Length" then - cl = v - end - - if line == "" then - local body, err = sock:receive(cl) - if err then - ngx.say("failed to receive body: ", err) - break - end - - ngx.say(body) - break - end - end - - ok, err = sock:close() + ngx.say("ok") } ---- stream_response -resty.core loaded in stream: false -resty.core loaded in http: false +--- grep_error_log eval: qr/\[warn\] .*? lua_load_resty_core is deprecated.*/ +--- grep_error_log_out eval +[ +qr/\[warn\] .*? lua_load_resty_core is deprecated \(the lua-resty-core library is required since ngx_stream_lua v0\.0\.8\) in .*?nginx\.conf:\d+/, +"" +] diff --git a/t/data/fake-merge-module/config b/t/data/fake-merge-module/config new file mode 100644 index 00000000..a05c2570 --- /dev/null +++ b/t/data/fake-merge-module/config @@ -0,0 +1,3 @@ +ngx_addon_name=ngx_stream_fake_merge_module +STREAM_MODULES="$STREAM_MODULES $ngx_addon_name" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_stream_fake_merge_module.c" diff --git a/t/data/fake-merge-module/ngx_stream_fake_merge_module.c b/t/data/fake-merge-module/ngx_stream_fake_merge_module.c new file mode 100644 index 00000000..c20106f8 --- /dev/null +++ b/t/data/fake-merge-module/ngx_stream_fake_merge_module.c @@ -0,0 +1,184 @@ +/* + * This fake module was used to reproduce a bug in ngx_lua's init_worker_by_lua + * implementation. The bug would cause this module's main_conf->a to be reset + * to 0 due to init_worker_by_lua invoking merge_srv_conf with a brand new + * srv_conf, whose a member would be 0. + * The test case for this bug is in 124-init-worker.t. + */ + + +#include +#include +#include +#include + + +typedef struct { + ngx_flag_t a; +} ngx_stream_fake_merge_main_conf_t; + + +typedef struct { + ngx_flag_t a; +} ngx_stream_fake_merge_srv_conf_t; + + +static ngx_int_t ngx_stream_fake_merge_var(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_fake_merge_add_variables(ngx_conf_t *cf); +static ngx_int_t ngx_stream_fake_merge_init(ngx_conf_t *cf); +static void *ngx_stream_fake_merge_create_main_conf(ngx_conf_t *cf); +static void *ngx_stream_fake_merge_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_fake_merge_merge_srv_conf(ngx_conf_t *cf, void *prev, + void *conf); + + +static ngx_stream_module_t ngx_stream_fake_merge_module_ctx = { + ngx_stream_fake_merge_init, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_stream_fake_merge_create_main_conf, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_fake_merge_create_srv_conf, /* create server configuration */ + ngx_stream_fake_merge_merge_srv_conf, /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_fake_merge_module = { + NGX_MODULE_V1, + &ngx_stream_fake_merge_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_stream_variable_t ngx_stream_fake_merge_variables[] = { + + { ngx_string("fake_var"), NULL, + ngx_stream_fake_merge_var, 0, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +static ngx_int_t +ngx_stream_fake_merge_var(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + static char *str[] = {"0", "1"}; + ngx_stream_fake_merge_main_conf_t *fmcf; + + fmcf = ngx_stream_get_module_main_conf(s, ngx_stream_fake_merge_module); + if (fmcf == NULL) { + return NGX_ERROR; + } + + v->len = 1; + v->data = (u_char *) str[fmcf->a]; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_fake_merge_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_fake_merge_variables; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_fake_merge_init(ngx_conf_t *cf) +{ + ngx_stream_fake_merge_srv_conf_t *fscf; + + fscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_fake_merge_module); + if (fscf == NULL) { + return NGX_ERROR; + } + + if (ngx_stream_fake_merge_add_variables(cf) != NGX_OK) { + return NGX_ERROR; + } + + fscf->a = 1; + + return NGX_OK; +} + + +static void * +ngx_stream_fake_merge_create_main_conf(ngx_conf_t *cf) +{ + ngx_stream_fake_merge_main_conf_t *fmcf; + + fmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_fake_merge_main_conf_t)); + if (fmcf == NULL) { + return NULL; + } + + fmcf->a = NGX_CONF_UNSET; + + return fmcf; +} + + +static void * +ngx_stream_fake_merge_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_fake_merge_srv_conf_t *fscf; + + fscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_fake_merge_srv_conf_t)); + if (fscf == NULL) { + return NULL; + } + + fscf->a = NGX_CONF_UNSET; + + return fscf; +} + + +static char * +ngx_stream_fake_merge_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_fake_merge_srv_conf_t *conf = child; + ngx_stream_fake_merge_srv_conf_t *prev = parent; + ngx_stream_fake_merge_main_conf_t *fmcf; + + ngx_conf_merge_value(conf->a, prev->a, 0); + + fmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_fake_merge_module); + if (fmcf == NULL) { + return NGX_CONF_ERROR; + } + + fmcf->a = conf->a; + + return NGX_CONF_OK; +} diff --git a/util/build.sh b/util/build.sh index 6f73d732..0ea7f89c 100755 --- a/util/build.sh +++ b/util/build.sh @@ -10,6 +10,8 @@ version=$1 force=$2 home=~ +add_fake_merge_module="--add-module=$root/t/data/fake-merge-module" + #--add-module=$root/../stream-echo-nginx-module \ ngx-build $force $version \ --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE_INC -I$OPENSSL_INC" \ @@ -33,6 +35,7 @@ ngx-build $force $version \ --add-module=$root/../echo-nginx-module \ --add-module=$root/../memc-nginx-module \ --add-module=$root/../headers-more-nginx-module \ + $add_fake_merge_module \ --add-module=$root $opts \ --with-poll_module \ --without-http_ssi_module \