diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml new file mode 100644 index 0000000000..12b87cb3c1 --- /dev/null +++ b/.github/workflows/semantic-pull-request.yml @@ -0,0 +1,30 @@ +name: "Lint PR" + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Configure which types are allowed. + # Default: https://github.com/commitizen/conventional-commit-types + types: | + bugfix # bug fixes + change # backward incompatible changes + doc # documentation changes including code comments + editor # code editor related configurations + feature # implementing a new feature + optimize # performance optimizations + refactor # code refactoring and other code rearrangement + style # coding style changes + tests # test suite changes diff --git a/.mergify.yml b/.mergify.yml index c89be4fb54..f1a8a2e255 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -33,3 +33,11 @@ pull_request_rules: label: remove: - needs-test-cases + - name: add label could-be-merged + conditions: + - "#approved-reviews-by>=2" + - status-success=Travis CI - Pull Request + actions: + label: + add: + - could-be-merged diff --git a/.travis.yml b/.travis.yml index d9990ddd9d..4aebcf6996 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,8 @@ -dist: xenial +dist: focal + +branches: + only: + - "master" os: linux @@ -6,24 +10,33 @@ language: c compiler: - gcc - - clang addons: apt: packages: - - axel - - cpanminus - - libtest-base-perl - - libtext-diff-perl - - liburi-perl - - libwww-perl - - libtest-longstring-perl - - liblist-moreutils-perl - - libgd-dev + - ack + - axel + - cpanminus + - libtest-base-perl + - libtext-diff-perl + - liburi-perl + - libwww-perl + - libtest-longstring-perl + - liblist-moreutils-perl + - libgd-dev + - time + - cmake + - libunwind-dev + - wget + - libbrotli1 + - lsb-release + - wget + - gnupg + - ca-certificates cache: directories: - - download-cache + - download-cache env: global: @@ -33,11 +46,12 @@ env: - LUAJIT_LIB=$LUAJIT_PREFIX/lib - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 - LUA_INCLUDE_DIR=$LUAJIT_INC - - PCRE_VER=8.44 - - PCRE_PREFIX=/opt/pcre - - PCRE_LIB=$PCRE_PREFIX/lib - - PCRE_INC=$PCRE_PREFIX/include - - OPENSSL_PREFIX=/opt/ssl + #- PCRE2_PREFIX=/usr/local/openresty/pcre2 + - PCRE2_PREFIX=/opt/pcre2 + - PCRE2_LIB=$PCRE2_PREFIX/lib + - PCRE2_INC=$PCRE2_PREFIX/include + #- OPENSSL_PREFIX=/usr/local/openresty/openssl3 + - OPENSSL_PREFIX=/opt/openssl3 - OPENSSL_LIB=$OPENSSL_PREFIX/lib - OPENSSL_INC=$OPENSSL_PREFIX/include - LIBDRIZZLE_PREFIX=/opt/drizzle @@ -46,26 +60,34 @@ env: - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH - DRIZZLE_VER=2011.07.21 - TEST_NGINX_SLEEP=0.006 + - MALLOC_PERTURB_=9 jobs: - - NGINX_VERSION=1.17.8 OPENSSL_VER=1.0.2u OPENSSL_PATCH_VER=1.0.2h - - NGINX_VERSION=1.19.3 OPENSSL_VER=1.0.2u OPENSSL_PATCH_VER=1.0.2h - - NGINX_VERSION=1.19.3 OPENSSL_VER=1.1.0l OPENSSL_PATCH_VER=1.1.0d - - NGINX_VERSION=1.19.3 OPENSSL_VER=1.1.1h OPENSSL_PATCH_VER=1.1.1f + - NGINX_VERSION=1.29.2 OPENSSL_VER=3.5.4 OPENSSL_PATCH_VER=3.5.4 TEST_NGINX_TIMEOUT=5 PCRE2_VER=10.46 + - NGINX_VERSION=1.29.2 OPENSSL_VER=3.5.4 OPENSSL_PATCH_VER=3.5.4 TEST_NGINX_TIMEOUT=5 PCRE2_VER=10.46 TEST_NGINX_USE_HTTP2=1 + - NGINX_VERSION=1.29.2 OPENSSL_VER=3.5.4 OPENSSL_PATCH_VER=3.5.4 TEST_NGINX_USE_HTTP3=1 TEST_NGINX_QUIC_IDLE_TIMEOUT=3 PCRE2_VER=10.46 + - NGINX_VERSION=1.29.2 BORINGSSL=1 TEST_NGINX_USE_HTTP3=1 TEST_NGINX_QUIC_IDLE_TIMEOUT=3 PCRE2_VER=10.46 services: - - memcached - - redis - - mysql + - memcached + - redis + - mysql before_install: - '! grep -n -P ''(?<=.{80}).+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Found C source lines exceeding 80 columns." > /dev/stderr; exit 1)' - '! grep -n -P ''\t+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Cannot use tabs." > /dev/stderr; exit 1)' - - sudo cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1) + - /usr/bin/env perl $(command -v cpanm) --sudo --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1) + - wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add - + - echo "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list + - sudo apt-get update + - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openresty-pcre2 openresty-openssl3 openresty-pcre2-dev openresty-openssl3-dev + install: - - if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then wget -P download-cache http://openresty.org/download/drizzle7-$DRIZZLE_VER.tar.gz; fi - - if [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache https://ftp.pcre.org/pub/pcre/pcre-$PCRE_VER.tar.gz; fi - - if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi + - if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then wget -P download-cache https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-$DRIZZLE_VER.tar.gz; fi + - if [ -n "$PCRE2_VER" ] && [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then wget -P download-cache https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz; fi + - if [ -n "$OPENSSL_VER" ] && [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://github.com/openssl/openssl/releases/download/openssl-$OPENSSL_VER/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi + - wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/boringssl-20230902-x64-focal.tar.gz + - wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/curl-h3-x64-focal.tar.gz - git clone https://github.com/openresty/test-nginx.git - git clone https://github.com/openresty/openresty.git ../openresty - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx @@ -87,17 +109,24 @@ install: - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql + - git clone https://github.com/spacewander/lua-resty-rsa.git ../lua-resty-rsa - git clone https://github.com/openresty/lua-resty-string.git ../lua-resty-string - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2 before_script: - - mysql -uroot -e 'create database ngx_test; grant all on ngx_test.* to "ngx_test"@"%" identified by "ngx_test"; flush privileges;' + - mysql -uroot -e "create database ngx_test; CREATE USER 'ngx_test'@'%' IDENTIFIED WITH mysql_native_password BY 'ngx_test'; grant all on ngx_test.* to 'ngx_test'@'%'; flush privileges;" script: + - sudo tar -C / -xf curl-h3-x64-focal.tar.gz + - export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:/opt/curl-h3/bin:$PATH + - ngx-releng > check.txt || true + - lines=`wc -l check.txt | awk '{print $1}'`; if [ $lines -gt 5 ]; then cat check.txt; exit 1; fi - sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT - sudo iptables -I OUTPUT -p tcp --dst 127.0.0.2 --dport 12345 -j DROP - sudo iptables -I OUTPUT -p udp --dst 127.0.0.2 --dport 12345 -j DROP + - sudo ip route add prohibit 0.0.0.1/32 + - sudo sysctl -w kernel.pid_max=10000 - cd luajit2/ - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2' > build.log 2>&1 || (cat build.log && exit 1) - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1) @@ -108,28 +137,23 @@ script: - sudo make install-libdrizzle-1.0 > build.log 2>&1 || (cat build.log && exit 1) - cd ../mockeagain/ && make CC=$CC -j$JOBS && cd .. - cd lua-cjson/ && make -j$JOBS && sudo make install && cd .. - - tar zxf download-cache/pcre-$PCRE_VER.tar.gz - - cd pcre-$PCRE_VER/ - - ./configure --prefix=$PCRE_PREFIX --enable-jit --enable-utf --enable-unicode-properties > build.log 2>&1 || (cat build.log && exit 1) - - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1) - - sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1) - - cd .. - - tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz - - cd openssl-$OPENSSL_VER/ - - patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch - - ./config shared enable-ssl3 enable-ssl3-method -g --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1) - - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1) - - sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1) - - cd .. - - export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH + - if [ -n "$PCRE2_VER" ]; then tar zxf download-cache/pcre2-$PCRE2_VER.tar.gz; cd pcre2-$PCRE2_VER/; ./configure --prefix=$PCRE2_PREFIX --enable-jit --enable-utf > build.log 2>&1 || (cat build.log && exit 1); make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1); cd ..; fi + - if [ -n "$OPENSSL_VER" ]; then tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz; cd openssl-$OPENSSL_VER/; patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch; ./config shared enable-ssl3 enable-ssl3-method -g --prefix=$OPENSSL_PREFIX --libdir=lib -DPURIFY > build.log 2>&1 || (cat build.log && exit 1); make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1); cd ..; fi + - if [ -n "$BORINGSSL" ]; then sudo rm -fr /usr/local/openresty/openssl3/ && sudo mkdir -p /usr/local/openresty/openssl3 && sudo tar -C /usr/local/openresty/openssl3 -xf boringssl-20230902-x64-focal.tar.gz --strip-components=1; fi - export NGX_BUILD_CC=$CC + - sh util/build-without-ssl.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1) + - sh util/build-with-dd.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1) + - rm -fr buildroot - sh util/build.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1) - nginx -V + - python3 ./util/nc_server.py & - ldd `which nginx`|grep -E 'luajit|ssl|pcre' - export LD_PRELOAD=$PWD/mockeagain/mockeagain.so - export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH + - export TEST_NGINX_HTTP3_CRT=$PWD/t/cert/http3/http3.crt + - export TEST_NGINX_HTTP3_KEY=$PWD/t/cert/http3/http3.key - export TEST_NGINX_RESOLVER=8.8.4.4 - dig +short myip.opendns.com @resolver1.opendns.com || exit 0 - dig +short @$TEST_NGINX_RESOLVER openresty.org || exit 0 - dig +short @$TEST_NGINX_RESOLVER agentzh.org || exit 0 - - prove -Itest-nginx/lib -r t + - /usr/bin/env perl $(command -v prove) -I. -Itest-nginx/inc -Itest-nginx/lib -r t/ diff --git a/README.markdown b/README.markdown index 5bb986991f..961ecb593a 100644 --- a/README.markdown +++ b/README.markdown @@ -1,15 +1,10 @@ - - Name ==== ngx_http_lua_module - Embed the power of Lua into Nginx HTTP Servers. This module is a core component of [OpenResty](https://openresty.org). If you are using this module, -then you are essentially using OpenResty. +then you are essentially using OpenResty :) *This module is not distributed with the Nginx source.* See [the installation instructions](#installation). @@ -50,6 +45,7 @@ Table of Contents * [Missing data on short circuited requests](#missing-data-on-short-circuited-requests) * [TODO](#todo) * [Changes](#changes) +* [Build And Test](#build-and-test) * [Test Suite](#test-suite) * [Copyright and License](#copyright-and-license) * [See Also](#see-also) @@ -68,8 +64,8 @@ Version ======= This document describes ngx_lua -[v0.10.19](https://github.com/openresty/lua-nginx-module/tags), which was released -on 3 Nov, 2020. +[v0.10.28](https://github.com/openresty/lua-nginx-module/tags), which was released +on 17 Jan, 2025. Videos ====== @@ -86,6 +82,14 @@ Videos [![OpenResty's resty Command-Line Utility Demo](https://img.youtube.com/vi/L1c7aw4mSOo/0.jpg)](https://youtu.be/L1c7aw4mSOo) +* YouTube video "[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)" + + [![Measure Execution Time of Lua Code Correctly in OpenResty](https://img.youtube.com/vi/VkRYW_qLoME/0.jpg)](https://youtu.be/VkRYW_qLoME) + +* YouTube video "[Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://youtu.be/EP7c0BM2yNo)" + + [![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo) + You are welcome to subscribe to our [official YouTube channel, OpenResty](https://www.youtube.com/channel/UCXVmwF-UCScv2ftsGoMqxhw). [Back to TOC](#table-of-contents) @@ -266,7 +270,7 @@ memory use. Request contexts are segregated using lightweight Lua coroutines. Loaded Lua modules persist in the Nginx worker process level resulting in a small memory footprint in Lua even when under heavy loads. -This module is plugged into Nginx's "http" subsystem so it can only speaks +This module is plugged into Nginx's "http" subsystem so it can only speak downstream communication protocols in the HTTP family (HTTP 0.9/1.0/1.1/2.0, WebSockets, etc...). If you want to do generic TCP communications with the downstream clients, then you should use the @@ -280,7 +284,7 @@ Typical Uses Just to name a few: -* Mashup'ing and processing outputs of various Nginx upstream outputs (proxy, drizzle, postgres, redis, memcached, and etc) in Lua, +* Mashup'ing and processing outputs of various Nginx upstream outputs (proxy, drizzle, postgres, redis, memcached, etc.) in Lua, * doing arbitrarily complex access control and security checks in Lua before requests actually reach the upstream backends, * manipulating response headers in an arbitrary way (by Lua) * fetching backend information from external storage backends (like redis, memcached, mysql, postgresql) and use that information to choose which upstream backend to access on-the-fly, @@ -304,6 +308,10 @@ Nginx Compatibility The latest version of this module is compatible with the following versions of Nginx: +* 1.29.x (last tested: 1.29.2) +* 1.27.x (last tested: 1.27.1) +* 1.25.x (last tested: 1.25.1) +* 1.21.x (last tested: 1.21.4) * 1.19.x (last tested: 1.19.3) * 1.17.x (last tested: 1.17.8) * 1.15.x (last tested: 1.15.8) @@ -332,7 +340,7 @@ It is discouraged to build this module with Nginx yourself since it is tricky to set up exactly right. 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 +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 @@ -344,6 +352,8 @@ Alternatively, ngx_lua can be manually compiled into Nginx: 1. Download the latest version of the ngx_devel_kit (NDK) module [HERE](https://github.com/simplresty/ngx_devel_kit/tags) 1. Download the latest version of ngx_lua [HERE](https://github.com/openresty/lua-nginx-module/tags) 1. Download the latest supported version of Nginx [HERE](https://nginx.org/) (See [Nginx Compatibility](#nginx-compatibility)) +1. Download the latest version of the lua-resty-core [HERE](https://github.com/openresty/lua-resty-core) +1. Download the latest version of the lua-resty-lrucache [HERE](https://github.com/openresty/lua-resty-lrucache) Build the source with this module: @@ -371,10 +381,22 @@ Build the source with this module: # current nginx build. # You can get usually those options using command nginx -V - # you can change the parallism number 2 below to fit the number of spare CPU cores in your + # you can change the parallelism number 2 below to fit the number of spare CPU cores in your # machine. make -j2 make install + + # Note that this version of lug-nginx-module not allow to set `lua_load_resty_core off;` any more. + # So, you have to install `lua-resty-core` and `lua-resty-lrucache` manually as below. + + cd lua-resty-core + make install PREFIX=/opt/nginx + cd lua-resty-lrucache + make install PREFIX=/opt/nginx + + # add necessary `lua_package_path` directive to `nginx.conf`, in the http context + + lua_package_path "/opt/nginx/lib/lua/?.lua;;"; ``` [Back to TOC](#table-of-contents) @@ -402,7 +424,7 @@ While building this module either via OpenResty or with the Nginx core, you can * `NGX_LUA_USE_ASSERT` When defined, will enable assertions in the ngx_lua C code base. Recommended for debugging or testing builds. It can introduce some (small) runtime overhead when enabled. This macro was first introduced in the `v0.9.10` release. * `NGX_LUA_ABORT_AT_PANIC` - When the LuaJIT VM panics, ngx_lua will instruct the current nginx worker process to quit gracefully by default. By specifying this C macro, ngx_lua will abort the current nginx worker process (which usually result in a core dump file) immediately. This option is useful for debugging VM panics. This option was first introduced in the `v0.9.8` release. + When the LuaJIT VM panics, ngx_lua will instruct the current nginx worker process to quit gracefully by default. By specifying this C macro, ngx_lua will abort the current nginx worker process (which usually results in a core dump file) immediately. This option is useful for debugging VM panics. This option was first introduced in the `v0.9.8` release. To enable one or more of these macros, just pass extra C compiler options to the `./configure` script of either Nginx or OpenResty. For instance, @@ -452,6 +474,10 @@ Please submit bug reports, wishlists, or patches by LuaJIT bytecode support ======================= +Watch YouTube video "[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)" + +[![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo) + As from the `v0.5.0rc32` release, all `*_by_lua_file` configure directives (such as [content_by_lua_file](#content_by_lua_file)) support loading LuaJIT 2.0/2.1 raw bytecode files directly: ```bash @@ -896,6 +922,8 @@ As noted earlier, PCRE sequences presented within `*_by_lua_block {}` directives # evaluates to "1234" ``` +**NOTE** You are recommended to use `by_lua_file` when the Lua code is very long. + [Back to TOC](#table-of-contents) Mixing with SSI Not Supported @@ -939,16 +967,12 @@ TODO * cosocket: implement LuaSocket's unconnected UDP API. * cosocket: add support in the context of [init_by_lua*](#init_by_lua). -* cosocket: implement the `bind()` method for stream-typed cosockets. * cosocket: review and merge aviramc's [patch](https://github.com/openresty/lua-nginx-module/pull/290) for adding the `bsdrecv` method. * cosocket: add configure options for different strategies of handling the cosocket connection exceeding in the pools. -* review and apply vadim-pavlov's patch for [ngx.location.capture](#ngxlocationcapture)'s `extra_headers` option -* use `ngx_hash_t` to optimize the built-in header look-up process for [ngx.req.set_header](#ngxreqset_header), [ngx.header.HEADER](#ngxheaderheader), and etc. -* add directives to run Lua codes when Nginx stops. +* use `ngx_hash_t` to optimize the built-in header look-up process for [ngx.req.set_header](#ngxreqset_header), and etc. * add `ignore_resp_headers`, `ignore_resp_body`, and `ignore_resp` options to [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) methods, to allow micro performance tuning on the user side. * add automatic Lua code time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks. * add `stat` mode similar to [mod_lua](https://httpd.apache.org/docs/trunk/mod/mod_lua.html). -* cosocket: add client SSL certificate support. [Back to TOC](#table-of-contents) @@ -961,6 +985,23 @@ The changes made in every release of this module are listed in the change logs o [Back to TOC](#table-of-contents) +Build And Test +============== + +This module uses `.travis.yml` as the CI configuration. +You can always check `.travis.yml` for the latest CI configuration. + +For developers, you need to run tests locally. You can use `util/run-ci.sh` +to easily set up the environment and execute the test suite. + +To run the Test from the beginning: + +```shell +git clone https://github.com/openresty/lua-nginx-module.git +cd lua-nginx-module +bash util/run-ci.sh +``` + Test Suite ========== @@ -990,7 +1031,7 @@ The order in which these modules are added during configuration is important bec filtering chain determines the final output, for example. The correct adding order is shown above. * 3rd-party Lua libraries: - * [lua-cjson](http://www.kyne.com.au/~mark/software/lua-cjson.php) + * [lua-cjson](https://www.kyne.au/~mark/software/lua-cjson.php) * Applications: * mysql: create database 'ngx_test', grant all privileges to user 'ngx_test', password is 'ngx_test' @@ -1005,7 +1046,6 @@ To run the whole test suite in the default testing mode: export PATH=/path/to/your/nginx/sbin:$PATH prove -I/path/to/test-nginx/lib -r t - To run specific test files: cd /path/to/lua-nginx-module @@ -1026,7 +1066,7 @@ This module is licensed under the BSD license. Copyright (C) 2009-2017, by Xiaozhe Wang (chaoslawful) . -Copyright (C) 2009-2019, by Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. +Copyright (C) 2009-2025, by Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. All rights reserved. @@ -1103,6 +1143,8 @@ Directives * [content_by_lua](#content_by_lua) * [content_by_lua_block](#content_by_lua_block) * [content_by_lua_file](#content_by_lua_file) +* [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) +* [server_rewrite_by_lua_file](#server_rewrite_by_lua_file) * [rewrite_by_lua](#rewrite_by_lua) * [rewrite_by_lua_block](#rewrite_by_lua_block) * [rewrite_by_lua_file](#rewrite_by_lua_file) @@ -1120,13 +1162,18 @@ Directives * [log_by_lua_file](#log_by_lua_file) * [balancer_by_lua_block](#balancer_by_lua_block) * [balancer_by_lua_file](#balancer_by_lua_file) +* [balancer_keepalive](#balancer_keepalive) * [lua_need_request_body](#lua_need_request_body) +* [ssl_client_hello_by_lua_block](#ssl_client_hello_by_lua_block) +* [ssl_client_hello_by_lua_file](#ssl_client_hello_by_lua_file) * [ssl_certificate_by_lua_block](#ssl_certificate_by_lua_block) * [ssl_certificate_by_lua_file](#ssl_certificate_by_lua_file) * [ssl_session_fetch_by_lua_block](#ssl_session_fetch_by_lua_block) * [ssl_session_fetch_by_lua_file](#ssl_session_fetch_by_lua_file) * [ssl_session_store_by_lua_block](#ssl_session_store_by_lua_block) * [ssl_session_store_by_lua_file](#ssl_session_store_by_lua_file) +* [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block) +* [proxy_ssl_verify_by_lua_file](#proxy_ssl_verify_by_lua_file) * [lua_shared_dict](#lua_shared_dict) * [lua_socket_connect_timeout](#lua_socket_connect_timeout) * [lua_socket_send_timeout](#lua_socket_send_timeout) @@ -1139,8 +1186,13 @@ Directives * [lua_ssl_ciphers](#lua_ssl_ciphers) * [lua_ssl_crl](#lua_ssl_crl) * [lua_ssl_protocols](#lua_ssl_protocols) +* [lua_ssl_certificate](#lua_ssl_certificate) +* [lua_ssl_certificate_key](#lua_ssl_certificate_key) * [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate) * [lua_ssl_verify_depth](#lua_ssl_verify_depth) +* [lua_ssl_key_log](#lua_ssl_key_log) +* [lua_ssl_conf_command](#lua_ssl_conf_command) +* [lua_upstream_skip_openssl_default_verify](#lua_upstream_skip_openssl_default_verify) * [lua_http10_buffering](#lua_http10_buffering) * [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) * [access_by_lua_no_postpone](#access_by_lua_no_postpone) @@ -1149,12 +1201,13 @@ Directives * [lua_max_pending_timers](#lua_max_pending_timers) * [lua_max_running_timers](#lua_max_running_timers) * [lua_sa_restart](#lua_sa_restart) +* [lua_worker_thread_vm_pool_size](#lua_worker_thread_vm_pool_size) The basic building blocks of scripting Nginx with Lua are directives. Directives are used to specify when the user Lua code is run and how the result will be used. Below is a diagram showing the order in which directives are executed. -![Lua Nginx Modules Directives](https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png) +![Lua Nginx Modules Directives](./doc/images/lua_nginx_modules_directives.drawio.png) [Back to TOC](#table-of-contents) @@ -1332,7 +1385,7 @@ A zero value of `` disables the cache. Note that this feature requires OpenResty's LuaJIT with the new C API `lua_resetthread`. -This feature was first introduced in verson `v0.10.9`. +This feature was first introduced in version `v0.10.9`. [Back to TOC](#directives) @@ -1425,9 +1478,33 @@ init_by_lua **NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [init_by_lua_block](#init_by_lua_block) directive instead. -Runs the Lua code specified by the argument `` on the global Lua VM level when the Nginx master process (if any) is loading the Nginx config file. +Similar to the [init_by_lua_block](#init_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + +```nginx + + init_by_lua ' + print("I need no extra escaping here, for example: \r\nblah") + ' +``` + +This directive was first introduced in the `v0.5.5` release. + +[Back to TOC](#directives) + +init_by_lua_block +----------------- + +**syntax:** *init_by_lua_block { lua-script }* + +**context:** *http* + +**phase:** *loading-config* -When Nginx receives the `HUP` signal and starts reloading the config file, the Lua VM will also be re-created and `init_by_lua` will run again on the new Lua VM. In case that the [lua_code_cache](#lua_code_cache) directive is turned off (default on), the `init_by_lua` handler will run upon every request because in this special mode a standalone Lua VM is always created for each request. + +When Nginx receives the `HUP` signal and starts reloading the config file, the Lua VM will also be re-created and `init_by_lua_block` will run again on the new Lua VM. In case that the [lua_code_cache](#lua_code_cache) directive is turned off (default on), the `init_by_lua_block` handler will run upon every request because in this special mode a standalone Lua VM is always created for each request. Usually you can pre-load Lua modules at server start-up by means of this hook and take advantage of modern operating systems' copy-on-write (COW) optimization. Here is an example for pre-loading Lua modules: @@ -1468,11 +1545,11 @@ You can also initialize the [lua_shared_dict](#lua_shared_dict) shm storage at t } ``` -But note that, the [lua_shared_dict](#lua_shared_dict)'s shm storage will not be cleared through a config reload (via the `HUP` signal, for example). So if you do *not* want to re-initialize the shm storage in your `init_by_lua` code in this case, then you just need to set a custom flag in the shm storage and always check the flag in your `init_by_lua` code. +But note that, the [lua_shared_dict](#lua_shared_dict)'s shm storage will not be cleared through a config reload (via the `HUP` signal, for example). So if you do *not* want to re-initialize the shm storage in your `init_by_lua_block` code in this case, then you just need to set a custom flag in the shm storage and always check the flag in your `init_by_lua_block` code. Because the Lua code in this context runs before Nginx forks its worker processes (if any), data or code loaded here will enjoy the [Copy-on-write (COW)](https://en.wikipedia.org/wiki/Copy-on-write) feature provided by many operating systems among all the worker processes, thus saving a lot of memory. -Do *not* initialize your own Lua global variables in this context because use of Lua global variables have performance penalties and can lead to global namespace pollution (see the [Lua Variable Scope](#lua-variable-scope) section for more details). The recommended way is to use proper [Lua module](https://www.lua.org/manual/5.1/manual.html#5.3) files (but do not use the standard Lua function [module()](https://www.lua.org/manual/5.1/manual.html#pdf-module) to define Lua modules because it pollutes the global namespace as well) and call [require()](https://www.lua.org/manual/5.1/manual.html#pdf-require) to load your own module files in `init_by_lua` or other contexts ([require()](https://www.lua.org/manual/5.1/manual.html#pdf-require) does cache the loaded Lua modules in the global `package.loaded` table in the Lua registry so your modules will only loaded once for the whole Lua VM instance). +Do *not* initialize your own Lua global variables in this context because use of Lua global variables have performance penalties and can lead to global namespace pollution (see the [Lua Variable Scope](#lua-variable-scope) section for more details). The recommended way is to use proper [Lua module](https://www.lua.org/manual/5.1/manual.html#5.3) files (but do not use the standard Lua function [module()](https://www.lua.org/manual/5.1/manual.html#pdf-module) to define Lua modules because it pollutes the global namespace as well) and call [require()](https://www.lua.org/manual/5.1/manual.html#pdf-require) to load your own module files in `init_by_lua_block` or other contexts ([require()](https://www.lua.org/manual/5.1/manual.html#pdf-require) does cache the loaded Lua modules in the global `package.loaded` table in the Lua registry so your modules will only loaded once for the whole Lua VM instance). Only a small set of the [Nginx API for Lua](#nginx-api-for-lua) is supported in this context: @@ -1485,7 +1562,7 @@ Basically you can safely use Lua libraries that do blocking I/O in this very con You should be very careful about potential security vulnerabilities in your Lua code registered in this context because the Nginx master process is often run under the `root` account. -This directive was first introduced in the `v0.5.5` release. +This directive was first introduced in the `v0.9.17` release. See also the following blog posts for more details on OpenResty and Nginx's shared memory zones: @@ -1494,33 +1571,6 @@ See also the following blog posts for more details on OpenResty and Nginx's shar [Back to TOC](#directives) -init_by_lua_block ------------------ - -**syntax:** *init_by_lua_block { lua-script }* - -**context:** *http* - -**phase:** *loading-config* - -Similar to the [init_by_lua](#init_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - -```nginx - - init_by_lua_block { - print("I need no extra escaping here, for example: \r\nblah") - } -``` - -This directive was first introduced in the `v0.9.17` release. - -[Back to TOC](#directives) - init_by_lua_file ---------------- @@ -1530,7 +1580,7 @@ init_by_lua_file **phase:** *loading-config* -Equivalent to [init_by_lua](#init_by_lua), except that the file specified by `` contains the Lua code or [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +Equivalent to [init_by_lua_block](#init_by_lua_block), except that the file specified by `` contains the Lua code or [LuaJIT bytecode](#luajit-bytecode-support) to be executed. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. @@ -1549,13 +1599,40 @@ init_worker_by_lua **NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [init_worker_by_lua_block](#init_worker_by_lua_block) directive instead. -Runs the specified Lua code upon every Nginx worker process's startup when the master process is enabled. When the master process is disabled, this hook will just run after [init_by_lua*](#init_by_lua). +Similar to the [init_worker_by_lua_block](#init_worker_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). -This hook is often used to create per-worker reoccurring timers (via the [ngx.timer.at](#ngxtimerat) Lua API), either for backend health-check or other timed routine work. Below is an example, +For instance, ```nginx init_worker_by_lua ' + print("I need no extra escaping here, for example: \r\nblah") + '; +``` + +This directive was first introduced in the `v0.9.5` release. + +This hook no longer runs in the cache manager and cache loader processes since the `v0.10.12` release. + +[Back to TOC](#directives) + +init_worker_by_lua_block +------------------------ + +**syntax:** *init_worker_by_lua_block { lua-script }* + +**context:** *http* + +**phase:** *starting-worker* + +Runs the specified Lua code upon every Nginx worker process's startup when the master process is enabled. When the master process is disabled, this hook will just run after [init_by_lua*](#init_by_lua_block). + +This hook is often used to create per-worker reoccurring timers (via the [ngx.timer.at](#ngxtimerat) Lua API), either for backend health-check or other timed routine work. Below is an example, + +```nginx + + init_worker_by_lua_block { local delay = 3 -- in seconds local new_timer = ngx.timer.at local log = ngx.log @@ -1582,35 +1659,6 @@ This hook is often used to create per-worker reoccurring timers (via the [ngx.ti end -- other job in init_worker_by_lua - '; -``` - -This directive was first introduced in the `v0.9.5` release. - -This hook no longer runs in the cache manager and cache loader processes since the `v0.10.12` release. - -[Back to TOC](#directives) - -init_worker_by_lua_block ------------------------- - -**syntax:** *init_worker_by_lua_block { lua-script }* - -**context:** *http* - -**phase:** *starting-worker* - -Similar to the [init_worker_by_lua](#init_worker_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - -```nginx - - init_worker_by_lua_block { - print("I need no extra escaping here, for example: \r\nblah") } ``` @@ -1629,7 +1677,7 @@ init_worker_by_lua_file **phase:** *starting-worker* -Similar to [init_worker_by_lua](#init_worker_by_lua), but accepts the file path to a Lua source file or Lua bytecode file. +Similar to [init_worker_by_lua_block](#init_worker_by_lua_block), but accepts the file path to a Lua source file or Lua bytecode file. This directive was first introduced in the `v0.9.5` release. @@ -1648,7 +1696,7 @@ exit_worker_by_lua_block Runs the specified Lua code upon every Nginx worker process's exit when the master process is enabled. When the master process is disabled, this hook will run before the Nginx process exits. -This hook is often used to release resources allocated by each worker (e.g. resources allocated by [init_worker_by_lua*](#init_worker_by_lua)), or to prevent workers from exiting abnormally. +This hook is often used to release resources allocated by each worker (e.g. resources allocated by [init_worker_by_lua*](#init_worker_by_lua_block)), or to prevent workers from exiting abnormally. For example, @@ -1659,6 +1707,8 @@ For example, } ``` +It's not allowed to create a timer (even a 0-delay timer) here since it runs after all timers have been processed. + This directive was first introduced in the `v0.10.18` release. [Back to TOC](#directives) @@ -1689,14 +1739,41 @@ set_by_lua **NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [set_by_lua_block](#set_by_lua_block) directive instead. -Executes code specified in `` with optional input arguments `$arg1 $arg2 ...`, and returns string output to `$res`. -The code in `` can make [API calls](#nginx-api-for-lua) and can retrieve input arguments from the `ngx.arg` table (index starts from `1` and increases sequentially). +Similar to the [set_by_lua_block](#set_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping), and +1. this directive support extra arguments after the Lua script. + +For example, + +```nginx + + set_by_lua $res ' return 32 + math.cos(32) '; + # $res now has the value "32.834223360507" or alike. +``` + +As from the `v0.5.0rc29` release, Nginx variable interpolation is disabled in the `` argument of this directive and therefore, the dollar sign character (`$`) can be used directly. + +This directive requires the [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit) module. + +[Back to TOC](#directives) + +set_by_lua_block +---------------- + +**syntax:** *set_by_lua_block $res { lua-script }* + +**context:** *server, server if, location, location if* + +**phase:** *rewrite* + +Executes code specified inside a pair of curly braces (`{}`), and returns string output to `$res`. +The code inside a pair of curly braces (`{}`) can make [API calls](#nginx-api-for-lua) and can retrieve input arguments from the `ngx.arg` table (index starts from `1` and increases sequentially). This directive is designed to execute short, fast running code blocks as the Nginx event loop is blocked during code execution. Time consuming code sequences should therefore be avoided. This directive is implemented by injecting custom commands into the standard [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)'s command list. Because [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html) does not support nonblocking I/O in its commands, Lua APIs requiring yielding the current Lua "light thread" cannot work in this directive. -At least the following API functions are currently disabled within the context of `set_by_lua`: +At least the following API functions are currently disabled within the context of `set_by_lua_block`: * Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers)) * Control API functions (e.g., [ngx.exit](#ngxexit)) @@ -1712,13 +1789,13 @@ a time. However, a workaround is possible using the [ngx.var.VARIABLE](#ngxvarva location /foo { set $diff ''; # we have to predefine the $diff variable here - set_by_lua $sum ' + set_by_lua_block $sum { local a = 32 local b = 56 ngx.var.diff = a - b -- write to $diff directly return a + b -- return the $sum value normally - '; + } echo "sum = $sum, diff = $diff"; } @@ -1729,42 +1806,14 @@ This directive can be freely mixed with all directives of the [ngx_http_rewrite_ ```nginx set $foo 32; - set_by_lua $bar 'return tonumber(ngx.var.foo) + 1'; + set_by_lua_block $bar { return tonumber(ngx.var.foo) + 1 } set $baz "bar: $bar"; # $baz == "bar: 33" ``` -As from the `v0.5.0rc29` release, Nginx variable interpolation is disabled in the `` argument of this directive and therefore, the dollar sign character (`$`) can be used directly. +No special escaping is required in the Lua code block. This directive requires the [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit) module. -[Back to TOC](#directives) - -set_by_lua_block ----------------- - -**syntax:** *set_by_lua_block $res { lua-script }* - -**context:** *server, server if, location, location if* - -**phase:** *rewrite* - -Similar to the [set_by_lua](#set_by_lua) directive except that - -1. this directive inlines the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping), and -1. this directive does not support extra arguments after the Lua script as in [set_by_lua](#set_by_lua). - -For example, - -```nginx - - set_by_lua_block $res { return 32 + math.cos(32) } - # $res now has the value "32.834223360507" or alike. -``` - -No special escaping is required in the Lua code block. - This directive was first introduced in the `v0.9.17` release. [Back to TOC](#directives) @@ -1778,7 +1827,7 @@ set_by_lua_file **phase:** *rewrite* -Equivalent to [set_by_lua](#set_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +Equivalent to [set_by_lua_block](#set_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. Nginx variable interpolation is supported in the `` argument string of this directive. But special care must be taken for injection attacks. @@ -1804,10 +1853,17 @@ content_by_lua **NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [content_by_lua_block](#content_by_lua_block) directive instead. -Acts as a "content handler" and executes Lua code string specified in `` for every request. -The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). +Similar to the [content_by_lua_block](#content_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). -Do not use this directive and other content handler directives in the same location. For example, this directive and the [proxy_pass](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) directive should not be used in the same location. +For instance, + +```nginx + + content_by_lua ' + ngx.say("I need no extra escaping here, for example: \r\nblah") + '; +``` [Back to TOC](#directives) @@ -1820,11 +1876,6 @@ content_by_lua_block **phase:** *content* -Similar to the [content_by_lua](#content_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). - For instance, ```nginx @@ -1834,6 +1885,11 @@ For instance, } ``` +Acts as a "content handler" and executes Lua code string specified in `{ lua-script }` for every request. +The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). + +Do not use this directive and other content handler directives in the same location. For example, this directive and the [proxy_pass](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) directive should not be used in the same location. + This directive was first introduced in the `v0.9.17` release. [Back to TOC](#directives) @@ -1847,7 +1903,9 @@ content_by_lua_file **phase:** *content* -Equivalent to [content_by_lua](#content_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +Equivalent to [content_by_lua_block](#content_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. + +If the file is not found, a `404 Not Found` status code will be returned, and a `503 Service Temporarily Unavailable` status code will be returned in case of errors in reading other files. Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. @@ -1874,82 +1932,200 @@ But be very careful about malicious user inputs and always carefully validate or [Back to TOC](#directives) -rewrite_by_lua --------------- - -**syntax:** *rewrite_by_lua <lua-script-str>* +server_rewrite_by_lua_block +--------------------------- -**context:** *http, server, location, location if* +**syntax:** *server_rewrite_by_lua_block { lua-script }* -**phase:** *rewrite tail* +**context:** *http, server* -**NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [rewrite_by_lua_block](#rewrite_by_lua_block) directive instead. +**phase:** *server rewrite* -Acts as a rewrite phase handler and executes Lua code string specified in `` for every request. +Acts as a server rewrite phase handler and executes Lua code string specified in `{ lua-script }` for every request. The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). -Note that this handler always runs *after* the standard [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html). So the following will work as expected: - ```nginx - location /foo { - set $a 12; # create and initialize $a - set $b ""; # create and initialize $b - rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; - echo "res = $b"; + server { + ... + + server_rewrite_by_lua_block { + ngx.ctx.a = "server_rewrite_by_lua_block in http" + } + + location /lua { + content_by_lua_block { + ngx.say(ngx.ctx.a) + ngx.log(ngx.INFO, ngx.ctx.a) + } + } } ``` -because `set $a 12` and `set $b ""` run *before* [rewrite_by_lua](#rewrite_by_lua). - -On the other hand, the following will not work as expected: +Just as any other rewrite phase handlers, [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) also runs in subrequests. ```nginx - ? location /foo { - ? set $a 12; # create and initialize $a - ? set $b ''; # create and initialize $b - ? rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; - ? if ($b = '13') { - ? rewrite ^ /bar redirect; - ? break; - ? } - ? - ? echo "res = $b"; - ? } + server { + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "is_subrequest:", ngx.is_subrequest) + } + + location /lua { + content_by_lua_block { + local res = ngx.location.capture("/sub") + ngx.print(res.body) + } + } + + location /sub { + content_by_lua_block { + ngx.say("OK") + } + } + } ``` -because `if` runs *before* [rewrite_by_lua](#rewrite_by_lua) even if it is placed after [rewrite_by_lua](#rewrite_by_lua) in the config. +Note that when calling `ngx.exit(ngx.OK)` within a [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) handler, call [ngx.exit](#ngxexit) with status >= 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. -The right way of doing this is as follows: ```nginx - location /foo { - set $a 12; # create and initialize $a - set $b ''; # create and initialize $b - rewrite_by_lua ' - ngx.var.b = tonumber(ngx.var.a) + 1 - if tonumber(ngx.var.b) == 13 then - return ngx.redirect("/bar") - end - '; + server_rewrite_by_lua_block { + ngx.exit(503) + } - echo "res = $b"; + location /bar { + ... + # never exec } ``` -Note that the [ngx_eval](http://www.grid.net.ru/nginx/eval.en.html) module can be approximated by using [rewrite_by_lua](#rewrite_by_lua). For example, -```nginx +[Back to TOC](#directives) - location / { - eval $res { - proxy_pass http://foo.com/check-spam; - } +server_rewrite_by_lua_file +-------------------------- - if ($res = 'spam') { - rewrite ^ /terms-of-use.html redirect; +**syntax:** *server_rewrite_by_lua_file <path-to-lua-script-file>* + +**context:** *http, server* + +**phase:** *server rewrite* + +Equivalent to [server_rewrite_by_lua_block](#server_rewrite_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.10.22` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. + +Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. + +When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. + +When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid reloading Nginx. + +[Back to TOC](#directives) + +rewrite_by_lua +-------------- + +**syntax:** *rewrite_by_lua <lua-script-str>* + +**context:** *http, server, location, location if* + +**phase:** *rewrite tail* + +**NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [rewrite_by_lua_block](#rewrite_by_lua_block) directive instead. + +Similar to the [rewrite_by_lua_block](#rewrite_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + +```nginx + + rewrite_by_lua ' + do_something("hello, world!\nhiya\n") + '; +``` + +[Back to TOC](#directives) + +rewrite_by_lua_block +-------------------- + +**syntax:** *rewrite_by_lua_block { lua-script }* + +**context:** *http, server, location, location if* + +**phase:** *rewrite tail* + +Acts as a rewrite phase handler and executes Lua code string specified in `{ lua-script }` for every request. +The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). + +Note that this handler always runs *after* the standard [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html). So the following will work as expected: + +```nginx + + location /foo { + set $a 12; # create and initialize $a + set $b ""; # create and initialize $b + rewrite_by_lua_block { + ngx.var.b = tonumber(ngx.var.a) + 1 + } + echo "res = $b"; + } +``` + +because `set $a 12` and `set $b ""` run *before* [rewrite_by_lua_block](#rewrite_by_lua_block). + +On the other hand, the following will not work as expected: + +```nginx + + ? location /foo { + ? set $a 12; # create and initialize $a + ? set $b ''; # create and initialize $b + ? rewrite_by_lua_block { + ? ngx.var.b = tonumber(ngx.var.a) + 1 + ? } + ? if ($b = '13') { + ? rewrite ^ /bar redirect; + ? break; + ? } + ? + ? echo "res = $b"; + ? } +``` + +because `if` runs *before* [rewrite_by_lua_block](#rewrite_by_lua_block) even if it is placed after [rewrite_by_lua_block](#rewrite_by_lua_block) in the config. + +The right way of doing this is as follows: + +```nginx + + location /foo { + set $a 12; # create and initialize $a + set $b ''; # create and initialize $b + rewrite_by_lua_block { + ngx.var.b = tonumber(ngx.var.a) + 1 + if tonumber(ngx.var.b) == 13 then + return ngx.redirect("/bar") + end + } + + echo "res = $b"; + } +``` + +Note that the [ngx_eval](http://www.grid.net.ru/nginx/eval.en.html) module can be approximated by using [rewrite_by_lua_block](#rewrite_by_lua_block). For example, + +```nginx + + location / { + eval $res { + proxy_pass http://foo.com/check-spam; + } + + if ($res = 'spam') { + rewrite ^ /terms-of-use.html redirect; } fastcgi_pass ...; @@ -1966,62 +2142,39 @@ can be implemented in ngx_lua as: } location / { - rewrite_by_lua ' + rewrite_by_lua_block { local res = ngx.location.capture("/check-spam") if res.body == "spam" then return ngx.redirect("/terms-of-use.html") end - '; + } fastcgi_pass ...; } ``` -Just as any other rewrite phase handlers, [rewrite_by_lua](#rewrite_by_lua) also runs in subrequests. +Just as any other rewrite phase handlers, [rewrite_by_lua_block](#rewrite_by_lua_block) also runs in subrequests. -Note that when calling `ngx.exit(ngx.OK)` within a [rewrite_by_lua](#rewrite_by_lua) handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [rewrite_by_lua](#rewrite_by_lua) handler, call [ngx.exit](#ngxexit) with status >= 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. +Note that when calling `ngx.exit(ngx.OK)` within a [rewrite_by_lua_block](#rewrite_by_lua_block) handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [rewrite_by_lua_block](#rewrite_by_lua_block) handler, call [ngx.exit](#ngxexit) with status >= 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. -If the [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)'s [rewrite](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite) directive is used to change the URI and initiate location re-lookups (internal redirections), then any [rewrite_by_lua](#rewrite_by_lua) or [rewrite_by_lua_file](#rewrite_by_lua_file) code sequences within the current location will not be executed. For example, +If the [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)'s [rewrite](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite) directive is used to change the URI and initiate location re-lookups (internal redirections), then any [rewrite_by_lua_block](#rewrite_by_lua_block) or [rewrite_by_lua_file_block](#rewrite_by_lua_file_block) code sequences within the current location will not be executed. For example, ```nginx location /foo { rewrite ^ /bar; - rewrite_by_lua 'ngx.exit(503)'; + rewrite_by_lua_block { + ngx.exit(503) + } } location /bar { ... } ``` -Here the Lua code `ngx.exit(503)` will never run. This will be the case if `rewrite ^ /bar last` is used as this will similarly initiate an internal redirection. If the `break` modifier is used instead, there will be no internal redirection and the `rewrite_by_lua` code will be executed. - -The `rewrite_by_lua` code will always run at the end of the `rewrite` request-processing phase unless [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) is turned on. - -[Back to TOC](#directives) - -rewrite_by_lua_block --------------------- +Here the Lua code `ngx.exit(503)` will never run. This will be the case if `rewrite ^ /bar last` is used as this will similarly initiate an internal redirection. If the `break` modifier is used instead, there will be no internal redirection and the `rewrite_by_lua_block` code will be executed. -**syntax:** *rewrite_by_lua_block { lua-script }* - -**context:** *http, server, location, location if* - -**phase:** *rewrite tail* - -Similar to the [rewrite_by_lua](#rewrite_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - -```nginx - - rewrite_by_lua_block { - do_something("hello, world!\nhiya\n") - } -``` +The `rewrite_by_lua_block` code will always run at the end of the `rewrite` request-processing phase unless [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) is turned on. This directive was first introduced in the `v0.9.17` release. @@ -2036,7 +2189,7 @@ rewrite_by_lua_file **phase:** *rewrite tail* -Equivalent to [rewrite_by_lua](#rewrite_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +Equivalent to [rewrite_by_lua_block](#rewrite_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. @@ -2061,7 +2214,30 @@ access_by_lua **NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [access_by_lua_block](#access_by_lua_block) directive instead. -Acts as an access phase handler and executes Lua code string specified in `` for every request. +Similar to the [access_by_lua_block](#access_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + +```nginx + + access_by_lua ' + do_something("hello, world!\nhiya\n") + '; +``` + +[Back to TOC](#directives) + +access_by_lua_block +------------------- + +**syntax:** *access_by_lua_block { lua-script }* + +**context:** *http, server, location, location if* + +**phase:** *access tail* + +Acts as an access phase handler and executes Lua code string specified in `{ = 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. +Note that when calling `ngx.exit(ngx.OK)` within a [access_by_lua_block](#access_by_lua_block) handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [access_by_lua_block](#access_by_lua_block) handler, call [ngx.exit](#ngxexit) with status >= 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. Starting from the `v0.9.20` release, you can use the [access_by_lua_no_postpone](#access_by_lua_no_postpone) directive to control when to run this handler inside the "access" request-processing phase of Nginx. -[Back to TOC](#directives) - -access_by_lua_block -------------------- - -**syntax:** *access_by_lua_block { lua-script }* - -**context:** *http, server, location, location if* - -**phase:** *access tail* - -Similar to the [access_by_lua](#access_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - -```nginx - - access_by_lua_block { - do_something("hello, world!\nhiya\n") - } -``` - This directive was first introduced in the `v0.9.17` release. [Back to TOC](#directives) @@ -2165,7 +2316,7 @@ access_by_lua_file **phase:** *access tail* -Equivalent to [access_by_lua](#access_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +Equivalent to [access_by_lua_block](#access_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. @@ -2190,23 +2341,16 @@ header_filter_by_lua **NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [header_filter_by_lua_block](#header_filter_by_lua_block) directive instead. -Uses Lua code specified in `` to define an output header filter. - -Note that the following API functions are currently disabled within this context: - -* Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers)) -* Control API functions (e.g., [ngx.redirect](#ngxredirect) and [ngx.exec](#ngxexec)) -* Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi)) -* Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)). +Similar to the [header_filter_by_lua_block](#header_filter_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). -Here is an example of overriding a response header (or adding one if absent) in our Lua header filter: +For instance, ```nginx - location / { - proxy_pass http://mybackend; - header_filter_by_lua 'ngx.header.Foo = "blah"'; - } + header_filter_by_lua ' + ngx.header["content-length"] = nil + '; ``` This directive was first introduced in the `v0.2.1rc20` release. @@ -2222,17 +2366,24 @@ header_filter_by_lua_block **phase:** *output-header-filter* -Similar to the [header_filter_by_lua](#header_filter_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). +Uses Lua code specified in `{ lua-script }` to define an output header filter. -For instance, +Note that the following API functions are currently disabled within this context: + +* Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers)) +* Control API functions (e.g., [ngx.redirect](#ngxredirect) and [ngx.exec](#ngxexec)) +* Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi)) +* Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)). + +Here is an example of overriding a response header (or adding one if absent) in our Lua header filter: ```nginx - header_filter_by_lua_block { - ngx.header["content-length"] = nil + location / { + proxy_pass http://mybackend; + header_filter_by_lua_block { + ngx.header.Foo = "blah" + } } ``` @@ -2249,7 +2400,7 @@ header_filter_by_lua_file **phase:** *output-header-filter* -Equivalent to [header_filter_by_lua](#header_filter_by_lua), except that the file specified by `` contains the Lua code, or as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +Equivalent to [header_filter_by_lua_block](#header_filter_by_lua_block), except that the file specified by `` contains the Lua code, or as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. @@ -2268,7 +2419,32 @@ body_filter_by_lua **NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [body_filter_by_lua_block](#body_filter_by_lua_block) directive instead. -Uses Lua code specified in `` to define an output body filter. +Similar to the [body_filter_by_lua_block](#body_filter_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + +```nginx + + body_filter_by_lua ' + local data, eof = ngx.arg[1], ngx.arg[2] + '; +``` + +This directive was first introduced in the `v0.5.0rc32` release. + +[Back to TOC](#directives) + +body_filter_by_lua_block +------------------------ + +**syntax:** *body_filter_by_lua_block { lua-script-str }* + +**context:** *http, server, location, location if* + +**phase:** *output-body-filter* + +Uses Lua code specified in `{ lua-script }` to define an output body filter. The input data chunk is passed via [ngx.arg](#ngxarg)\[1\] (as a Lua string value) and the "eof" flag indicating the end of the response body data stream is passed via [ngx.arg](#ngxarg)\[2\] (as a Lua boolean value). @@ -2289,7 +2465,9 @@ The Lua code can pass its own modified version of the input data chunk to the do location / { proxy_pass http://mybackend; - body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])'; + body_filter_by_lua_block { + ngx.arg[1] = string.upper(ngx.arg[1]) + } } ``` @@ -2303,7 +2481,7 @@ Likewise, new "eof" flag can also be specified by setting a boolean value to [ng echo hello world; echo hiya globe; - body_filter_by_lua ' + body_filter_by_lua_block { local chunk = ngx.arg[1] if string.match(chunk, "hello") then ngx.arg[2] = true -- new eof @@ -2312,7 +2490,7 @@ Likewise, new "eof" flag can also be specified by setting a boolean value to [ng -- just throw away any remaining chunk data ngx.arg[1] = nil - '; + } } ``` @@ -2331,8 +2509,12 @@ When the Lua code may change the length of the response body, then it is require location /foo { # fastcgi_pass/proxy_pass/... - header_filter_by_lua_block { ngx.header.content_length = nil } - body_filter_by_lua 'ngx.arg[1] = string.len(ngx.arg[1]) .. "\\n"'; + header_filter_by_lua_block { + ngx.header.content_length = nil + } + body_filter_by_lua_block { + ngx.arg[1] = string.len(ngx.arg[1]) .. "\n" + } } ``` @@ -2345,66 +2527,64 @@ Note that the following API functions are currently disabled within this context Nginx output filters may be called multiple times for a single request because response body may be delivered in chunks. Thus, the Lua code specified by in this directive may also run multiple times in the lifetime of a single HTTP request. -This directive was first introduced in the `v0.5.0rc32` release. +This directive was first introduced in the `v0.9.17` release. [Back to TOC](#directives) -body_filter_by_lua_block ------------------------- +body_filter_by_lua_file +----------------------- -**syntax:** *body_filter_by_lua_block { lua-script-str }* +**syntax:** *body_filter_by_lua_file <path-to-lua-script-file>* **context:** *http, server, location, location if* **phase:** *output-body-filter* -Similar to the [body_filter_by_lua](#body_filter_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - -```nginx +Equivalent to [body_filter_by_lua_block](#body_filter_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. - body_filter_by_lua_block { - local data, eof = ngx.arg[1], ngx.arg[2] - } -``` +When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. -This directive was first introduced in the `v0.9.17` release. +This directive was first introduced in the `v0.5.0rc32` release. [Back to TOC](#directives) -body_filter_by_lua_file ------------------------ +log_by_lua +---------- -**syntax:** *body_filter_by_lua_file <path-to-lua-script-file>* +**syntax:** *log_by_lua <lua-script-str>* **context:** *http, server, location, location if* -**phase:** *output-body-filter* +**phase:** *log* -Equivalent to [body_filter_by_lua](#body_filter_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +**NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [log_by_lua_block](#log_by_lua_block) directive instead. -When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. +Similar to the [log_by_lua_block](#log_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). -This directive was first introduced in the `v0.5.0rc32` release. +For instance, + +```nginx + + log_by_lua ' + print("I need no extra escaping here, for example: \r\nblah") + '; +``` + +This directive was first introduced in the `v0.5.0rc31` release. [Back to TOC](#directives) -log_by_lua ----------- +log_by_lua_block +---------------- -**syntax:** *log_by_lua <lua-script-str>* +**syntax:** *log_by_lua_block { lua-script }* **context:** *http, server, location, location if* **phase:** *log* -**NOTE** Use of this directive is *discouraged* following the `v0.9.17` release. Use the [log_by_lua_block](#log_by_lua_block) directive instead. - -Runs the Lua source code inlined as the `` at the `log` request processing phase. This does not replace the current access logs, but runs before. +Runs the Lua source code inlined as the `{ lua-script }` at the `log` request processing phase. This does not replace the current access logs, but runs before. Note that the following API functions are currently disabled within this context: @@ -2423,7 +2603,7 @@ Here is an example of gathering average data for [$upstream_response_time](http: location / { proxy_pass http://mybackend; - log_by_lua ' + log_by_lua_block { local log_dict = ngx.shared.log_dict local upstream_time = tonumber(ngx.var.upstream_response_time) @@ -2436,7 +2616,7 @@ Here is an example of gathering average data for [$upstream_response_time](http: log_dict:add("upstream_time-nb", 0) log_dict:incr("upstream_time-nb", 1) end - '; + } } location = /status { @@ -2456,33 +2636,6 @@ Here is an example of gathering average data for [$upstream_response_time](http: } ``` -This directive was first introduced in the `v0.5.0rc31` release. - -[Back to TOC](#directives) - -log_by_lua_block ----------------- - -**syntax:** *log_by_lua_block { lua-script }* - -**context:** *http, server, location, location if* - -**phase:** *log* - -Similar to the [log_by_lua](#log_by_lua) directive except that this directive inlines -the Lua source directly -inside a pair of curly braces (`{}`) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - -```nginx - - log_by_lua_block { - print("I need no extra escaping here, for example: \r\nblah") - } -``` - This directive was first introduced in the `v0.9.17` release. [Back to TOC](#directives) @@ -2496,7 +2649,7 @@ log_by_lua_file **phase:** *log* -Equivalent to [log_by_lua](#log_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. +Equivalent to [log_by_lua_block](#log_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. @@ -2581,6 +2734,29 @@ This directive was first introduced in the `v0.10.0` release. [Back to TOC](#directives) +balancer_keepalive +------------------ + +**syntax:** *balancer_keepalive <total-connections>* + +**context:** *upstream* + +**phase:** *loading-config* + +The `total-connections` parameter sets the maximum number of idle +keepalive connections to upstream servers that are preserved in the cache of +each worker process. When this number is exceeded, the least recently used +connections are closed. + +It should be particularly noted that the keepalive directive does not limit the +total number of connections to upstream servers that an nginx worker process +can open. The connections parameter should be set to a number small enough to +let upstream servers process new incoming connections as well. + +This directive was first introduced in the `v0.10.21` release. + +[Back to TOC](#directives) + lua_need_request_body --------------------- @@ -2609,6 +2785,129 @@ This also applies to [access_by_lua*](#access_by_lua). [Back to TOC](#directives) +ssl_client_hello_by_lua_block +----------------------------- + +**syntax:** *ssl_client_hello_by_lua_block { lua-script }* + +**context:** *http, server* + +**phase:** *right-after-client-hello-message-was-processed* + +This directive runs user Lua code when Nginx is about to post-process the SSL client hello message for the downstream +SSL (https) connections. + +It is particularly useful for dynamically setting the SSL protocols according to the SNI. + +It is also useful to do some custom operations according to the per-connection information in the client hello message. + +For example, one can parse custom client hello extension and do the corresponding handling in pure Lua. + +This Lua handler will always run whether the SSL session is resumed (via SSL session IDs or TLS session tickets) or not. +While the `ssl_certificate_by_lua*` Lua handler will only runs when initiating a full SSL handshake. + +The [ngx.ssl.clienthello](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md) Lua modules +provided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme) +library are particularly useful in this context. + +Note that this handler runs in extremely early stage of SSL handshake, before the SSL client hello extensions are parsed. +So you can not use some Lua API like `ssl.server_name()` which is dependent on the later stage's processing. + +Also note that only the directive in default server is valid for several virtual servers with the same IP address and port. + +Below is a trivial example using the +[ngx.ssl.clienthello](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md) module +at the same time: + +```nginx + + server { + listen 443 ssl; + server_name test.com; + ssl_certificate /path/to/cert.crt; + ssl_certificate_key /path/to/key.key; + ssl_client_hello_by_lua_block { + local ssl_clt = require "ngx.ssl.clienthello" + local host, err = ssl_clt.get_client_hello_server_name() + if host == "test.com" then + ssl_clt.set_protocols({"TLSv1", "TLSv1.1"}) + elseif host == "test2.com" then + ssl_clt.set_protocols({"TLSv1.2", "TLSv1.3"}) + elseif not host then + ngx.log(ngx.ERR, "failed to get the SNI name: ", err) + ngx.exit(ngx.ERROR) + else + ngx.log(ngx.ERR, "unknown SNI name: ", host) + ngx.exit(ngx.ERROR) + end + } + ... + } + server { + listen 443 ssl; + server_name test2.com; + ssl_certificate /path/to/cert.crt; + ssl_certificate_key /path/to/key.key; + ... + } +``` + +See more information in the [ngx.ssl.clienthello](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md) +Lua modules' official documentation. + +Uncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the +[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`. + +This Lua code execution context *does* support yielding, so Lua APIs that may yield +(like cosockets, sleeping, and "light threads") +are enabled in this context + +Note, you need to configure the [ssl_certificate](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate) +and [ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key) +to avoid the following error while starting NGINX: + + + nginx: [emerg] no ssl configured for the server + + +This directive requires OpenSSL 1.1.1 or greater. + +If you are using the [official pre-built +packages](https://openresty.org/en/linux-packages.html) for +[OpenResty](https://openresty.org/) 1.21.4.1 or later, then everything should +work out of the box. + +If you are not using the Nginx core shipped with +[OpenResty](https://openresty.org) 1.21.4.1 or later, you will need to apply +patches to the standard Nginx core: + + + +**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch) + +This directive was first introduced in the `v0.10.21` release. + +[Back to TOC](#directives) + +ssl_client_hello_by_lua_file +---------------------------- + +**syntax:** *ssl_client_hello_by_lua_file <path-to-lua-script-file>* + +**context:** *http, server* + +**phase:** *right-after-client-hello-message-was-processed* + +Equivalent to [ssl_client_hello_by_lua_block](#ssl_client_hello_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. + +When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. + +**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch) + +This directive was first introduced in the `v0.10.21` release. + +[Back to TOC](#directives) + ssl_certificate_by_lua_block ---------------------------- @@ -2692,19 +2991,14 @@ packages](https://openresty.org/en/linux-packages.html) for [OpenResty](https://openresty.org/) 1.9.7.2 or later, then everything should work out of the box. -If you are not using one of the [OpenSSL -packages](https://openresty.org/en/linux-packages.html) provided by -[OpenResty](https://openresty.org), you will need to apply patches to OpenSSL -in order to use this directive: - - - -Similarly, if you are not using the Nginx core shipped with +If you are not using the Nginx core shipped with [OpenResty](https://openresty.org) 1.9.7.2 or later, you will need to apply patches to the standard Nginx core: +**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch) + This directive was first introduced in the `v0.10.0` release. [Back to TOC](#directives) @@ -2722,6 +3016,8 @@ Equivalent to [ssl_certificate_by_lua_block](#ssl_certificate_by_lua_block), exc When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. +**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch) + This directive was first introduced in the `v0.10.0` release. [Back to TOC](#directives) @@ -2873,6 +3169,84 @@ Note that: this directive is only allowed to used in **http context** from the ` [Back to TOC](#directives) +proxy_ssl_verify_by_lua_block +----------------------------- + +**syntax:** *proxy_ssl_verify_by_lua_block { lua-script }* + +**context:** *location* + +**phase:** *right-after-server-certificate-message-was-processed* + +This directive runs user Lua code when Nginx is about to post-process the SSL server certificate message for the upstream SSL (https) connections. + +It is particularly useful to parse upstream server certificate and do some custom operations in pure lua. + +The [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) Lua modules provided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme) +library are particularly useful in this context. + +Below is a trivial example using the +[ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) module +at the same time: + +```nginx + + server { + listen 443 ssl; + server_name test.com; + ssl_certificate /path/to/cert.crt; + ssl_certificate_key /path/to/key.key; + + location /t { + proxy_ssl_certificate /path/to/cert.crt; + proxy_ssl_certificate_key /path/to/key.key; + proxy_pass https://upstream; + + proxy_ssl_verify_by_lua_block { + local proxy_ssl_vfy = require "ngx.ssl.proxysslverify" + local cert = proxy_ssl_vfy.get_verify_cert() + + -- ocsp to verify cert + -- check crl + proxy_ssl_vfy.set_verify_result() + ... + } + } + ... + } +``` + +See more information in the [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) +Lua modules' official documentation. + +Uncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the +[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`. + +This Lua code execution context *does* support yielding, so Lua APIs that may yield +(like cosockets, sleeping, and "light threads") +are enabled in this context + +Note, `ngx.ctx` in proxy_ssl_verify_by_lua_block is belonging to upstream connection, not downstream connection, so it's different from `ngx.ctx` in contexts like ssl_certificate_by_lua etc. + +This directive requires OpenSSL 3.0.2 or greater. + +[Back to TOC](#directives) + +proxy_ssl_verify_by_lua_file +---------------------------- + +**syntax:** *proxy_ssl_verify_by_lua_file <path-to-lua-script-file>* + +**context:** *location* + +**phase:** *right-after-server-certificate-message-was-processed* + +Equivalent to [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. + +When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. + +[Back to TOC](#directives) + lua_shared_dict --------------- @@ -3039,90 +3413,188 @@ lua_socket_log_errors This directive can be used to toggle error logging when a failure occurs for the TCP or UDP cosockets. If you are already doing proper error handling and logging in your Lua code, then it is recommended to turn this directive off to prevent data flushing in your Nginx error log files (which is usually rather expensive). -This directive was first introduced in the `v0.5.13` release. +This directive was first introduced in the `v0.5.13` release. + +[Back to TOC](#directives) + +lua_ssl_ciphers +--------------- + +**syntax:** *lua_ssl_ciphers <ciphers>* + +**default:** *lua_ssl_ciphers DEFAULT* + +**context:** *http, server, location* + +Specifies the enabled ciphers for requests to a SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. The ciphers are specified in the format understood by the OpenSSL library. + +The full list can be viewed using the “openssl ciphers” command. + +This directive was first introduced in the `v0.9.11` release. + +[Back to TOC](#directives) + +lua_ssl_crl +----------- + +**syntax:** *lua_ssl_crl <file>* + +**default:** *no* + +**context:** *http, server, location* + +Specifies a file with revoked certificates (CRL) in the PEM format used to verify the certificate of the SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +This directive was first introduced in the `v0.9.11` release. + +[Back to TOC](#directives) + +lua_ssl_protocols +----------------- + +**syntax:** *lua_ssl_protocols \[SSLv2\] \[SSLv3\] \[TLSv1\] [TLSv1.1] [TLSv1.2] [TLSv1.3]* + +**default:** *lua_ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3* + +**context:** *http, server, location* + +Enables the specified protocols for requests to a SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +The support for the `TLSv1.3` parameter requires version `v0.10.12` *and* OpenSSL 1.1.1. +From version v0.10.25, the default value change from `SSLV3 TLSv1 TLSv1.1 TLSv1.2` to `TLSv1 TLSv1.1 TLSv1.2 TLSv1.3`. + +This directive was first introduced in the `v0.9.11` release. + +[Back to TOC](#directives) + +lua_ssl_certificate +------------------- + +**syntax:** *lua_ssl_certificate <file>* + +**default:** *none* + +**context:** *http, server, location* + +Specifies the file path to the SSL/TLS certificate in PEM format used for the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +This directive allows you to specify the SSL/TLS certificate that will be presented to server during the SSL/TLS handshake process. + +This directive was first introduced in the `v0.10.26` release. + +See also [lua_ssl_certificate_key](#lua_ssl_certificate_key) and [lua_ssl_verify_depth](#lua_ssl_verify_depth). + +[Back to TOC](#directives) + +lua_ssl_certificate_key +----------------------- + +**syntax:** *lua_ssl_certificate_key <file>* + +**default:** *none* + +**context:** *http, server, location* + +Specifies the file path to the private key associated with the SSL/TLS certificate used in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +This directive allows you to specify the private key file corresponding to the SSL/TLS certificate specified by lua_ssl_certificate. The private key should be in PEM format and must match the certificate. + +This directive was first introduced in the `v0.10.26` release. + +See also [lua_ssl_certificate](#lua_ssl_certificate) and [lua_ssl_verify_depth](#lua_ssl_verify_depth). + +[Back to TOC](#directives) + +lua_ssl_trusted_certificate +--------------------------- + +**syntax:** *lua_ssl_trusted_certificate <file>* + +**default:** *none* + +**context:** *http, server, location* + +Specifies a file path with trusted CA certificates in the PEM format used to verify the certificate of the SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +This directive was first introduced in the `v0.9.11` release. + +See also [lua_ssl_verify_depth](#lua_ssl_verify_depth). [Back to TOC](#directives) -lua_ssl_ciphers ---------------- +lua_ssl_verify_depth +-------------------- -**syntax:** *lua_ssl_ciphers <ciphers>* +**syntax:** *lua_ssl_verify_depth <number>* -**default:** *lua_ssl_ciphers DEFAULT* +**default:** *lua_ssl_verify_depth 1* **context:** *http, server, location* -Specifies the enabled ciphers for requests to a SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. The ciphers are specified in the format understood by the OpenSSL library. - -The full list can be viewed using the “openssl ciphers” command. +Sets the verification depth in the server certificates chain. This directive was first introduced in the `v0.9.11` release. +See also [lua_ssl_certificate](#lua_ssl_certificate), [lua_ssl_certificate_key](#lua_ssl_certificate_key) and [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate). + [Back to TOC](#directives) -lua_ssl_crl ------------ +lua_ssl_key_log +--------------- -**syntax:** *lua_ssl_crl <file>* +**syntax:** *lua_ssl_key_log <file>* -**default:** *no* +**default:** *none* **context:** *http, server, location* -Specifies a file with revoked certificates (CRL) in the PEM format used to verify the certificate of the SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. - -This directive was first introduced in the `v0.9.11` release. +Enables logging of client connection SSL keys in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method and specifies the path to the key log file. Keys are logged in the SSLKEYLOGFILE format compatible with Wireshark. [Back to TOC](#directives) -lua_ssl_protocols ------------------ +lua_ssl_conf_command +-------------------- -**syntax:** *lua_ssl_protocols \[SSLv2\] \[SSLv3\] \[TLSv1\] [TLSv1.1] [TLSv1.2] [TLSv1.3]* +**syntax:** *lua_ssl_conf_command <command>* -**default:** *lua_ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2* +**default:** *no* **context:** *http, server, location* -Enables the specified protocols for requests to a SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. - -The support for the `TLSv1.3` parameter requires version `v0.10.12` *and* OpenSSL 1.1.1. +Sets arbitrary OpenSSL configuration [commands](https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html). -This directive was first introduced in the `v0.9.11` release. +The directive is supported when using OpenSSL 1.0.2 or higher and nginx 1.19.4 or higher. According to the specify command, higher OpenSSL version may be needed. -[Back to TOC](#directives) +Several `lua_ssl_conf_command` directives can be specified on the same level: -lua_ssl_trusted_certificate ---------------------------- +```nginx -**syntax:** *lua_ssl_trusted_certificate <file>* + lua_ssl_conf_command Options PrioritizeChaCha; + lua_ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256; +``` -**default:** *no* +Configuration commands are applied after OpenResty own configuration for SSL, so they can be used to override anything set by OpenResty. -**context:** *http, server, location* +Note though that configuring OpenSSL directly with `lua_ssl_conf_command` might result in a behaviour OpenResty does not expect, and should be done with care. -Specifies a file path with trusted CA certificates in the PEM format used to verify the certificate of the SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. +This directive was first introduced in the `v0.10.21` release. -This directive was first introduced in the `v0.9.11` release. -See also [lua_ssl_verify_depth](#lua_ssl_verify_depth). [Back to TOC](#directives) -lua_ssl_verify_depth +lua_upstream_skip_openssl_default_verify -------------------- -**syntax:** *lua_ssl_verify_depth <number>* - -**default:** *lua_ssl_verify_depth 1* +**syntax:** *lua_upstream_skip_openssl_default_verify on|off* -**context:** *http, server, location* +**default:** *lua_upstream_skip_openssl_default_verify off* -Sets the verification depth in the server certificates chain. +**context:** *location, location-if* -This directive was first introduced in the `v0.9.11` release. +When using proxy_ssl_verify_by_lua directive, `lua_upstream_skip_openssl_default_verify` controls whether to skip default openssl's verify function, that means using pure Lua code to verify upstream server certificate. -See also [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate). +This directive is turned `off` by default. [Back to TOC](#directives) @@ -3186,7 +3658,7 @@ lua_transform_underscores_in_response_headers **context:** *http, server, location, location-if* -Controls whether to transform underscores (`_`) in the response header names specified in the [ngx.header.HEADER](#ngxheaderheader) API to hypens (`-`). +Controls whether to transform underscores (`_`) in the response header names specified in the [ngx.header.HEADER](#ngxheaderheader) API to hyphens (`-`). This directive was first introduced in the `v0.5.0rc32` release. @@ -3257,7 +3729,7 @@ lua_max_running_timers Controls the maximum number of "running timers" allowed. -Running timers are those timers whose user callback functions are still running. +Running timers are those timers whose user callback functions are still running or `lightthreads` spawned in callback functions are still running. When exceeding this limit, Nginx will stop running the callbacks of newly expired timers and log an error message "N lua_max_running_timers are not enough" where "N" is the current value of this directive. @@ -3282,6 +3754,27 @@ This directive was first introduced in the `v0.10.14` release. [Back to TOC](#directives) +lua_worker_thread_vm_pool_size +------------------------------ + +**syntax:** *lua_worker_thread_vm_pool_size <size>* + +**default:** *lua_worker_thread_vm_pool_size 10* + +**context:** *http* + +Specifies the size limit of the Lua VM pool (default 100) that will be used in the [ngx.run_worker_thread](#ngxrun_worker_thread) API. + +Also, it is not allowed to create Lua VMs that exceeds the pool size limit. + +The Lua VM in the VM pool is used to execute Lua code in separate thread. + +The pool is global at Nginx worker level. And it is used to reuse Lua VMs between requests. + +**Warning:** Each worker thread uses a separate Lua VM and caches the Lua VM for reuse in subsequent operations. Configuring too many worker threads can result in consuming a lot of memory. + +[Back to TOC](#directives) + Nginx API for Lua ================= @@ -3339,6 +3832,7 @@ Nginx API for Lua * [ngx.decode_args](#ngxdecode_args) * [ngx.encode_base64](#ngxencode_base64) * [ngx.decode_base64](#ngxdecode_base64) +* [ngx.decode_base64mime](#ngxdecode_base64mime) * [ngx.crc32_short](#ngxcrc32_short) * [ngx.crc32_long](#ngxcrc32_long) * [ngx.hmac_sha1](#ngxhmac_sha1) @@ -3384,6 +3878,7 @@ Nginx API for Lua * [ngx.shared.DICT.capacity](#ngxshareddictcapacity) * [ngx.shared.DICT.free_space](#ngxshareddictfree_space) * [ngx.socket.udp](#ngxsocketudp) +* [udpsock:bind](#udpsockbind) * [udpsock:setpeername](#udpsocksetpeername) * [udpsock:send](#udpsocksend) * [udpsock:receive](#udpsockreceive) @@ -3391,7 +3886,10 @@ Nginx API for Lua * [udpsock:settimeout](#udpsocksettimeout) * [ngx.socket.stream](#ngxsocketstream) * [ngx.socket.tcp](#ngxsockettcp) +* [tcpsock:bind](#tcpsockbind) * [tcpsock:connect](#tcpsockconnect) +* [tcpsock:getfd](#getfd) +* [tcpsock:setclientcert](#tcpsocksetclientcert) * [tcpsock:sslhandshake](#tcpsocksslhandshake) * [tcpsock:send](#tcpsocksend) * [tcpsock:receive](#tcpsockreceive) @@ -3421,6 +3919,7 @@ Nginx API for Lua * [ngx.config.ngx_lua_version](#ngxconfigngx_lua_version) * [ngx.worker.exiting](#ngxworkerexiting) * [ngx.worker.pid](#ngxworkerpid) +* [ngx.worker.pids](#ngxworkerpids) * [ngx.worker.count](#ngxworkercount) * [ngx.worker.id](#ngxworkerid) * [ngx.semaphore](#ngxsemaphore) @@ -3434,6 +3933,7 @@ Nginx API for Lua * [coroutine.wrap](#coroutinewrap) * [coroutine.running](#coroutinerunning) * [coroutine.status](#coroutinestatus) +* [ngx.run_worker_thread](#ngxrun_worker_thread) [Back to TOC](#table-of-contents) @@ -3543,7 +4043,8 @@ For example: } ``` -That is, Nginx variables cannot be created on-the-fly. +That is, Nginx variables cannot be created on-the-fly. Here is a list of pre-defined +[Nginx variables](http://nginx.org/en/docs/varindex.html). Some special Nginx variables like `$args` and `$limit_rate` can be assigned a value, many others are not, like `$query_string`, `$arg_PARAMETER`, and `$http_NAME`. @@ -3577,7 +4078,7 @@ This API requires a relatively expensive metamethod call and it is recommended t Core constants -------------- -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, *log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, *log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** ```lua @@ -3604,7 +4105,7 @@ The `ngx.DECLINED` constant was first introduced in the `v0.5.0rc19` release. HTTP method constants --------------------- -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** ngx.HTTP_GET @@ -3631,7 +4132,7 @@ These constants are usually used in [ngx.location.capture](#ngxlocationcapture) HTTP status constants --------------------- -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** ```nginx @@ -3664,7 +4165,8 @@ HTTP status constants value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release) value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release) value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) - value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) + value = ngx.HTTP_NOT_IMPLEMENTED (501) + value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) (kept for compatibility) value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release) value = ngx.HTTP_SERVICE_UNAVAILABLE (503) value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release) @@ -3677,7 +4179,7 @@ HTTP status constants Nginx log level constants ------------------------- -**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** ```lua @@ -3701,7 +4203,7 @@ print **syntax:** *print(...)* -**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Writes argument values into the Nginx `error.log` file with the `ngx.NOTICE` log level. @@ -3786,7 +4288,7 @@ Then `GET /main` will give the output Here, modification of the `ngx.ctx.blah` entry in the subrequest does not affect the one in the parent request. This is because they have two separate versions of `ngx.ctx.blah`. -Internal redirection will destroy the original request `ngx.ctx` data (if any) and the new request will have an empty `ngx.ctx` table. For instance, +Internal redirects (triggered by nginx configuration directives like `error_page`, `try_files`, `index`, etc.) will destroy the original request `ngx.ctx` data (if any) and the new request will have an empty `ngx.ctx` table. For instance, ```nginx @@ -3814,10 +4316,10 @@ Then `GET /orig` will give rather than the original `"hello"` value. Because HTTP request is created after SSL handshake, the `ngx.ctx` created -in [ssl_certificate_by_lua*](#ssl_certificate_by_lua), [ssl_session_store_by_lua*](#ssl_session_store_by_lua) and [ssl_session_fetch_by_lua*](#ssl_session_fetch_by_lua) +in [ssl_certificate_by_lua*](#ssl_certificate_by_lua), [ssl_session_store_by_lua*](#ssl_session_store_by_lua), [ssl_session_fetch_by_lua*](#ssl_session_fetch_by_lua) and [ssl_client_hello_by_lua*](#ssl_client_hello_by_lua) is not available in the following phases like [rewrite_by_lua*](#rewrite_by_lua). -Since `dev`, the `ngx.ctx` created during a SSL handshake +Since `v0.10.18`, the `ngx.ctx` created during a SSL handshake will be inherited by the requests which share the same TCP connection established by the handshake. Note that overwrite values in `ngx.ctx` in the http request phases (like `rewrite_by_lua*`) will only take affect in the current http request. @@ -3941,6 +4443,8 @@ argument, which supports the options: specify the subrequest's request body (string value only). * `args` specify the subrequest's URI query arguments (both string value and Lua tables are accepted) +* `headers` + specify the subrequest's request headers (Lua table only). this headers will override the original headers of the subrequest. * `ctx` specify a Lua table to be the [ngx.ctx](#ngxctx) table for the subrequest. It can be the current request's [ngx.ctx](#ngxctx) table, which effectively makes the parent and its subrequest to share exactly the same context table. This option was first introduced in the `v0.3.1rc25` release. * `vars` @@ -4092,6 +4596,33 @@ Accessing `/lua` will yield the output dog = hello cat = 32 +The `headers` option can be used to specify the request headers for the subrequest. The value of this option should be a Lua table where the keys are the header names and the values are the header values. For example, + +```lua + +location /foo { + content_by_lua_block { + ngx.print(ngx.var.http_x_test) + } +} + +location /lua { + content_by_lua_block { + local res = ngx.location.capture("/foo", { + headers = { + ["X-Test"] = "aa", + } + }) + ngx.print(res.body) + } +} +``` + +Accessing `/lua` will yield the output + + + aa + The `ctx` option can be used to specify a custom Lua table to serve as the [ngx.ctx](#ngxctx) table for the subrequest. @@ -4207,7 +4738,9 @@ Lua tables can be used for both requests and responses when the number of subreq table.insert(reqs, { "/memcached" }) -- issue all the requests at once and wait until they all return - local resps = { ngx.location.capture_multi(reqs) } + local resps = { + ngx.location.capture_multi(reqs) + } -- loop over the responses table for i, resp in ipairs(resps) do @@ -4447,7 +4980,7 @@ ngx.req.http_version Returns the HTTP version number for the current request as a Lua number. -Current possible values are 2.0, 1.0, 1.1, and 0.9. Returns `nil` for unrecognized values. +Current possible values are 3.0, 2.0, 1.0, 1.1, and 0.9. Returns `nil` for unrecognized values. This method was first introduced in the `v0.7.17` release. @@ -4686,7 +5219,9 @@ Multi-value arguments are also supported: ngx.req.set_uri_args({ a = 3, b = {5, 6} }) ``` -which will result in a query string like `a=3&b=5&b=6`. +which will result in a query string like `a=3&b=5&b=6` or `b=5&b=6&a=3`. + +**Note that when using Lua table as the `arg` argument, the order of the arguments in the result query string which change from time to time. If you would like to get an ordered result, you need to use Lua string as the `arg` argument.** This interface was first introduced in the `v0.3.1rc13` release. @@ -4697,11 +5232,12 @@ See also [ngx.req.set_uri](#ngxreqset_uri). ngx.req.get_uri_args -------------------- -**syntax:** *args, err = ngx.req.get_uri_args(max_args?)* +**syntax:** *args, err = ngx.req.get_uri_args(max_args?, tab?)* **context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua** -Returns a Lua table holding all the current request URL query arguments. +Returns a Lua table holding all the current request URL query arguments. An optional `tab` argument +can be used to reuse the table returned by this method. ```nginx @@ -5010,6 +5546,8 @@ For security considerations, this method will automatically escape " ", """, "(" By default, all the subrequests subsequently initiated by [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) will inherit the new header. +It is not a Lua's equivalent of nginx `proxy_set_header` directive (same is true about [ngx.req.clear_header](#ngxreqclear_header)). `proxy_set_header` only affects the upstream request while `ngx.req.set_header` change the incoming request. Record the http headers in the access log file will show the difference. But you still can use it as an alternative of nginx `proxy_set_header` directive as long as you know the difference. + Here is an example of setting the `Content-Type` header: ```lua @@ -5119,12 +5657,14 @@ See also [ngx.req.read_body](#ngxreqread_body). ngx.req.get_body_data --------------------- -**syntax:** *data = ngx.req.get_body_data()* +**syntax:** *data = ngx.req.get_body_data(max_bytes?)* **context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, log_by_lua** Retrieves in-memory request body data. It returns a Lua string rather than a Lua table holding all the parsed query arguments. Use the [ngx.req.get_post_args](#ngxreqget_post_args) function instead if a Lua table is required. +The optional `max_bytes` argument can be used when you don't need the entire body. + This function returns `nil` if 1. the request body has not been read, @@ -5162,6 +5702,8 @@ If the request body has been read into memory, try calling the [ngx.req.get_body To force in-file request bodies, try turning on [client_body_in_file_only](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_in_file_only). +Note that this function is also work for balancer phase but it needs to call [balancer.recreate_request](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md#recreate_request) to make the change take effect after set the request body data or headers. + This function was first introduced in the `v0.3.1rc17` release. See also [ngx.req.get_body_data](#ngxreqget_body_data). @@ -5173,7 +5715,7 @@ ngx.req.set_body_data **syntax:** *ngx.req.set_body_data(data)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua*,* Set the current request's request body using the in-memory data specified by the `data` argument. @@ -5181,6 +5723,8 @@ If the request body has not been read yet, call [ngx.req.read_body](#ngxreqread_ Whether the previous request body has been read into memory or buffered into a disk file, it will be freed or the disk file will be cleaned up immediately, respectively. +Note that this function is also work for balancer phase but it needs to call [balancer.recreate_request](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md#recreate_request) to make the change take effect after set the request body data or headers. + This function was first introduced in the `v0.3.1rc18` release. See also [ngx.req.set_body_file](#ngxreqset_body_file). @@ -5192,7 +5736,7 @@ ngx.req.set_body_file **syntax:** *ngx.req.set_body_file(file_name, auto_clean?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua*,* Set the current request's request body using the in-file data specified by the `file_name` argument. @@ -5217,7 +5761,7 @@ ngx.req.init_body **context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua** -Creates a new blank request body for the current request and inializes the buffer for later request body data writing via the [ngx.req.append_body](#ngxreqappend_body) and [ngx.req.finish_body](#ngxreqfinish_body) APIs. +Creates a new blank request body for the current request and initializes the buffer for later request body data writing via the [ngx.req.append_body](#ngxreqappend_body) and [ngx.req.finish_body](#ngxreqfinish_body) APIs. If the `buffer_size` argument is specified, then its value will be used for the size of the memory buffer for body writing with [ngx.req.append_body](#ngxreqappend_body). If the argument is omitted, then the value specified by the standard [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) directive will be used instead. @@ -5293,6 +5837,8 @@ Returns a read-only cosocket object that wraps the downstream connection. Only [ In case of error, `nil` will be returned as well as a string describing the error. +**Note:** This method will block while waiting for client request body to be fully received. Block time depends on the [client_body_timeout](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_timeout) directive and maximum body size specified by the [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) directive. If read timeout occurs or client body size exceeds the defined limit, this function will not return and `408 Request Time-out` or `413 Request Entity Too Large` response will be returned to the client instead. + The socket object returned by this method is usually used to read the current request's body in a streaming fashion. Do not turn on the [lua_need_request_body](#lua_need_request_body) directive, and do not mix this call with [ngx.req.read_body](#ngxreqread_body) and [ngx.req.discard_body](#ngxreqdiscard_body). If any request body data has been pre-read into the Nginx core request header buffer, the resulting cosocket object will take care of this to avoid potential data loss resulting from such pre-reading. @@ -5556,7 +6102,7 @@ ngx.log **syntax:** *ngx.log(log_level, ...)* -**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Log arguments concatenated to error.log with the given logging level. @@ -5594,7 +6140,7 @@ ngx.exit **syntax:** *ngx.exit(status)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** When `status >= 200` (i.e., `ngx.HTTP_OK` and above), it will interrupt the execution of the current request and return status code to Nginx. @@ -5686,7 +6232,7 @@ ngx.sleep **syntax:** *ngx.sleep(seconds)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Sleeps for the specified seconds without blocking. One can specify time resolution up to 0.001 seconds (i.e., one millisecond). @@ -5703,7 +6249,7 @@ ngx.escape_uri **syntax:** *newstr = ngx.escape_uri(str, type?)* -**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Since `v0.10.16`, this function accepts an optional `type` argument. It accepts the following values (defaults to `2`): @@ -5722,7 +6268,7 @@ ngx.unescape_uri **syntax:** *newstr = ngx.unescape_uri(str)* -**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, exit_worker_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Unescape `str` as an escaped URI component. @@ -5739,6 +6285,23 @@ gives the output b r56 7 +Invalid escaping sequences are handled in a conventional way: `%`s are left unchanged. Also, characters that should not appear in escaped string are simply left unchanged. + +For example, + +```lua + + ngx.say(ngx.unescape_uri("try %search%%20%again%")) +``` + +gives the output + + + try %search% %again% + + +(Note that `%20` following `%` got unescaped, even it can be considered a part of invalid sequence.) + [Back to TOC](#nginx-api-for-lua) ngx.encode_args @@ -5746,7 +6309,7 @@ ngx.encode_args **syntax:** *str = ngx.encode_args(table)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_client_hello_by_lua** Encode the Lua table to a query args string according to the URI encoded rules. @@ -5804,7 +6367,7 @@ ngx.decode_args **syntax:** *table, err = ngx.decode_args(str, max_args?)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Decodes a URI encoded query-string into a Lua table. This is the inverse function of [ngx.encode_args](#ngxencode_args). @@ -5828,9 +6391,9 @@ ngx.encode_base64 **syntax:** *newstr = ngx.encode_base64(str, no_padding?)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** -Encodes `str` to a base64 digest. +Encodes `str` to a base64 digest. For base64url encoding use [`base64.encode_base64url`](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/base64.md#encode_base64url). Since the `0.9.16` release, an optional boolean-typed `no_padding` argument can be specified to control whether the base64 padding should be appended to the resulting digest (default to `false`, i.e., with padding enabled). @@ -5841,9 +6404,27 @@ ngx.decode_base64 **syntax:** *newstr = ngx.decode_base64(str)* +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** + +Decodes the `str` argument as a base64 digest to the raw form. For base64url decoding use [`base64.decode_base64url`](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/base64.md#decode_base64url). + +The `str` should be standard 'base64' encoding for RFC 3548 or RFC 4648, and will returns `nil` if is not well formed or any characters not in the base encoding alphabet. Padding may be omitted from the input. + +[Back to TOC](#nginx-api-for-lua) + +ngx.decode_base64mime +--------------------- +**syntax:** *newstr = ngx.decode_base64mime(str)* + **context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** -Decodes the `str` argument as a base64 digest to the raw form. Returns `nil` if `str` is not well formed. +**requires:** `resty.core.base64` or `resty.core` + +Decodes the `str` argument as a base64 digest to the raw form. +The `str` follows base64 transfer encoding for MIME (RFC 2045), and will discard characters outside the base encoding alphabet. +Returns `nil` if `str` is not well formed. + + '''Note:''' This method requires the resty.core.base64 or resty.core modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library. [Back to TOC](#nginx-api-for-lua) @@ -5852,7 +6433,7 @@ ngx.crc32_short **syntax:** *intval = ngx.crc32_short(str)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Calculates the CRC-32 (Cyclic Redundancy Code) digest for the `str` argument. @@ -5869,7 +6450,7 @@ ngx.crc32_long **syntax:** *intval = ngx.crc32_long(str)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Calculates the CRC-32 (Cyclic Redundancy Code) digest for the `str` argument. @@ -5886,7 +6467,7 @@ ngx.hmac_sha1 **syntax:** *digest = ngx.hmac_sha1(secret_key, str)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Computes the [HMAC-SHA1](https://en.wikipedia.org/wiki/HMAC) digest of the argument `str` and turns the result using the secret key ``. @@ -5919,7 +6500,7 @@ ngx.md5 **syntax:** *digest = ngx.md5(str)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Returns the hexadecimal representation of the MD5 digest of the `str` argument. @@ -5928,7 +6509,9 @@ For example, ```nginx location = /md5 { - content_by_lua_block { ngx.say(ngx.md5("hello")) } + content_by_lua_block { + ngx.say(ngx.md5("hello")) + } } ``` @@ -5947,7 +6530,7 @@ ngx.md5_bin **syntax:** *digest = ngx.md5_bin(str)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Returns the binary form of the MD5 digest of the `str` argument. @@ -5960,7 +6543,7 @@ ngx.sha1_bin **syntax:** *digest = ngx.sha1_bin(str)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Returns the binary form of the SHA-1 digest of the `str` argument. @@ -5975,7 +6558,7 @@ ngx.quote_sql_str **syntax:** *quoted_value = ngx.quote_sql_str(raw_value)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Returns a quoted SQL string literal according to the MySQL quoting rules. @@ -5986,7 +6569,7 @@ ngx.today **syntax:** *str = ngx.today()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns current date (in the format `yyyy-mm-dd`) from the Nginx cached time (no syscall involved unlike Lua's date library). @@ -5999,7 +6582,7 @@ ngx.time **syntax:** *secs = ngx.time()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns the elapsed seconds from the epoch for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library). @@ -6012,7 +6595,7 @@ ngx.now **syntax:** *secs = ngx.now()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns a floating-point number for the elapsed time in seconds (including milliseconds as the decimal part) from the epoch for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library). @@ -6027,7 +6610,7 @@ ngx.update_time **syntax:** *ngx.update_time()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Forcibly updates the Nginx current time cache. This call involves a syscall and thus has some overhead, so do not abuse it. @@ -6040,7 +6623,7 @@ ngx.localtime **syntax:** *str = ngx.localtime()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns the current time stamp (in the format `yyyy-mm-dd hh:mm:ss`) of the Nginx cached time (no syscall involved unlike Lua's [os.date](https://www.lua.org/manual/5.1/manual.html#pdf-os.date) function). @@ -6053,7 +6636,7 @@ ngx.utctime **syntax:** *str = ngx.utctime()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns the current time stamp (in the format `yyyy-mm-dd hh:mm:ss`) of the Nginx cached time (no syscall involved unlike Lua's [os.date](https://www.lua.org/manual/5.1/manual.html#pdf-os.date) function). @@ -6066,7 +6649,7 @@ ngx.cookie_time **syntax:** *str = ngx.cookie_time(sec)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns a formatted string can be used as the cookie expiration time. The parameter `sec` is the time stamp in seconds (like those returned from [ngx.time](#ngxtime)). @@ -6083,9 +6666,9 @@ ngx.http_time **syntax:** *str = ngx.http_time(sec)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** -Returns a formated string can be used as the http header time (for example, being used in `Last-Modified` header). The parameter `sec` is the time stamp in seconds (like those returned from [ngx.time](#ngxtime)). +Returns a formatted string can be used as the http header time (for example, being used in `Last-Modified` header). The parameter `sec` is the time stamp in seconds (like those returned from [ngx.time](#ngxtime)). ```nginx @@ -6100,7 +6683,7 @@ ngx.parse_http_time **syntax:** *sec = ngx.parse_http_time(str)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Parse the http time string (as returned by [ngx.http_time](#ngxhttp_time)) into seconds. Returns the seconds or `nil` if the input string is in bad forms. @@ -6130,7 +6713,7 @@ ngx.re.match **syntax:** *captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Matches the `subject` string using the Perl compatible regular expression `regex` with the optional `options`. @@ -6289,7 +6872,7 @@ ngx.re.find **syntax:** *from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Similar to [ngx.re.match](#ngxrematch) but only returns the beginning index (`from`) and end index (`to`) of the matched substring. The returned indexes are 1-based and can be fed directly into the [string.sub](https://www.lua.org/manual/5.1/manual.html#pdf-string.sub) API function to obtain the matched substring. @@ -6344,7 +6927,7 @@ ngx.re.gmatch **syntax:** *iterator, err = ngx.re.gmatch(subject, regex, options?)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Similar to [ngx.re.match](#ngxrematch), but returns a Lua iterator instead, so as to let the user programmer iterate all the matches over the `` string argument with the PCRE `regex`. @@ -6423,7 +7006,7 @@ ngx.re.sub **syntax:** *newstr, n, err = ngx.re.sub(subject, regex, replace, options?)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Substitutes the first match of the Perl compatible regular expression `regex` on the `subject` argument string with the string or function argument `replace`. The optional `options` argument has exactly the same meaning as in [ngx.re.match](#ngxrematch). @@ -6491,7 +7074,7 @@ ngx.re.gsub **syntax:** *newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Just like [ngx.re.sub](#ngxresub), but does global substitution. @@ -6532,7 +7115,7 @@ ngx.shared.DICT **syntax:** *dict = ngx.shared\[name_var\]* -**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Fetching the shm-based Lua dictionary object for the shared memory zone named `DICT` defined by the [lua_shared_dict](#lua_shared_dict) directive. @@ -6617,7 +7200,7 @@ ngx.shared.DICT.get **syntax:** *value, flags = ngx.shared.DICT:get(key)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Retrieving the value in the dictionary [ngx.shared.DICT](#ngxshareddict) for the key `key`. If the key does not exist or has expired, then `nil` will be returned. @@ -6656,7 +7239,7 @@ ngx.shared.DICT.get_stale **syntax:** *value, flags, stale = ngx.shared.DICT:get_stale(key)* -**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Similar to the [get](#ngxshareddictget) method but returns the value even if the key has already expired. @@ -6675,7 +7258,7 @@ ngx.shared.DICT.set **syntax:** *success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Unconditionally sets a key-value pair into the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). Returns three values: @@ -6728,7 +7311,7 @@ ngx.shared.DICT.safe_set **syntax:** *ok, err = ngx.shared.DICT:safe_set(key, value, exptime?, flags?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Similar to the [set](#ngxshareddictset) method, but never overrides the (least recently used) unexpired items in the store when running out of storage in the shared memory zone. In this case, it will immediately return `nil` and the string "no memory". @@ -6743,7 +7326,7 @@ ngx.shared.DICT.add **syntax:** *success, err, forcible = ngx.shared.DICT:add(key, value, exptime?, flags?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Just like the [set](#ngxshareddictset) method, but only stores the key-value pair into the dictionary [ngx.shared.DICT](#ngxshareddict) if the key does *not* exist. @@ -6760,7 +7343,7 @@ ngx.shared.DICT.safe_add **syntax:** *ok, err = ngx.shared.DICT:safe_add(key, value, exptime?, flags?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Similar to the [add](#ngxshareddictadd) method, but never overrides the (least recently used) unexpired items in the store when running out of storage in the shared memory zone. In this case, it will immediately return `nil` and the string "no memory". @@ -6775,7 +7358,7 @@ ngx.shared.DICT.replace **syntax:** *success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Just like the [set](#ngxshareddictset) method, but only stores the key-value pair into the dictionary [ngx.shared.DICT](#ngxshareddict) if the key *does* exist. @@ -6792,7 +7375,7 @@ ngx.shared.DICT.delete **syntax:** *ngx.shared.DICT:delete(key)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Unconditionally removes the key-value pair from the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). @@ -6809,7 +7392,7 @@ ngx.shared.DICT.incr **syntax:** *newval, err, forcible? = ngx.shared.DICT:incr(key, value, init?, init_ttl?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** **optional requirement:** `resty.core.shdict` or `resty.core` @@ -6864,7 +7447,7 @@ ngx.shared.DICT.lpush **syntax:** *length, err = ngx.shared.DICT:lpush(key, value)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Inserts the specified (numerical or string) `value` at the head of the list named `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). Returns the number of elements in the list after the push operation. @@ -6883,7 +7466,7 @@ ngx.shared.DICT.rpush **syntax:** *length, err = ngx.shared.DICT:rpush(key, value)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Similar to the [lpush](#ngxshareddictlpush) method, but inserts the specified (numerical or string) `value` at the tail of the list named `key`. @@ -6898,7 +7481,7 @@ ngx.shared.DICT.lpop **syntax:** *val, err = ngx.shared.DICT:lpop(key)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Removes and returns the first element of the list named `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). @@ -6915,7 +7498,7 @@ ngx.shared.DICT.rpop **syntax:** *val, err = ngx.shared.DICT:rpop(key)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Removes and returns the last element of the list named `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). @@ -6932,7 +7515,7 @@ ngx.shared.DICT.llen **syntax:** *len, err = ngx.shared.DICT:llen(key)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Returns the number of elements in the list named `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). @@ -6949,7 +7532,7 @@ ngx.shared.DICT.ttl **syntax:** *ttl, err = ngx.shared.DICT:ttl(key)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** **requires:** `resty.core.shdict` or `resty.core` @@ -6987,7 +7570,7 @@ ngx.shared.DICT.expire **syntax:** *success, err = ngx.shared.DICT:expire(key, exptime)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** **requires:** `resty.core.shdict` or `resty.core` @@ -7027,7 +7610,7 @@ ngx.shared.DICT.flush_all **syntax:** *ngx.shared.DICT:flush_all()* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Flushes out all the items in the dictionary. This method does not actually free up all the memory blocks in the dictionary but just marks all the existing items as expired. @@ -7042,7 +7625,7 @@ ngx.shared.DICT.flush_expired **syntax:** *flushed = ngx.shared.DICT:flush_expired(max_count?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Flushes out the expired items in the dictionary, up to the maximal number specified by the optional `max_count` argument. When the `max_count` argument is given `0` or not given at all, then it means unlimited. Returns the number of items that have actually been flushed. @@ -7059,7 +7642,7 @@ ngx.shared.DICT.get_keys **syntax:** *keys = ngx.shared.DICT:get_keys(max_count?)* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Fetch a list of the keys from the dictionary, up to ``. @@ -7076,7 +7659,7 @@ ngx.shared.DICT.capacity **syntax:** *capacity_bytes = ngx.shared.DICT:capacity()* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** **requires:** `resty.core.shdict` or `resty.core` @@ -7108,7 +7691,7 @@ ngx.shared.DICT.free_space **syntax:** *free_page_bytes = ngx.shared.DICT:free_space()* -**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** **requires:** `resty.core.shdict` or `resty.core` @@ -7151,10 +7734,11 @@ ngx.socket.udp **syntax:** *udpsock = ngx.socket.udp()* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Creates and returns a UDP or datagram-oriented unix domain socket object (also known as one type of the "cosocket" objects). The following methods are supported on this object: +* [bind](#udpsockbind) * [setpeername](#udpsocksetpeername) * [send](#udpsocksend) * [receive](#udpsockreceive) @@ -7169,6 +7753,36 @@ See also [ngx.socket.tcp](#ngxsockettcp). [Back to TOC](#nginx-api-for-lua) +udpsock:bind +------------ +**syntax:** *ok, err = udpsock:bind(address)* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*,ssl_session_fetch_by_lua*,ssl_client_hello_by_lua** + +Just like the standard [proxy_bind](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_bind) directive, this api makes the outgoing connection to a upstream server originate from the specified local IP address. + +Only IP addresses can be specified as the `address` argument. + +Here is an example for connecting to a TCP server from the specified local IP address: + +```nginx + + location /test { + content_by_lua_block { + local sock = ngx.socket.udp() + -- assume "192.168.1.10" is the local ip address + local ok, err = sock:bind("192.168.1.10") + if not ok then + ngx.say("failed to bind: ", err) + return + end + sock:close() + } + } +``` + +[Back to TOC](#nginx-api-for-lua) + udpsock:setpeername ------------------- @@ -7176,7 +7790,7 @@ udpsock:setpeername **syntax:** *ok, err = udpsock:setpeername("unix:/path/to/unix-domain.socket")* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Attempts to connect a UDP socket object to a remote server or to a datagram unix domain socket file. Because the datagram protocol is actually connection-less, this method does not really establish a "connection", but only just set the name of the remote peer for subsequent read/write operations. @@ -7239,7 +7853,7 @@ udpsock:send **syntax:** *ok, err = udpsock:send(data)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Sends data on the current UDP or datagram unix domain socket object. @@ -7256,7 +7870,7 @@ udpsock:receive **syntax:** *data, err = udpsock:receive(size?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Receives data from the UDP or datagram unix domain socket object with an optional receive buffer size argument, `size`. @@ -7292,7 +7906,7 @@ udpsock:close **syntax:** *ok, err = udpsock:close()* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Closes the current UDP or datagram unix domain socket. It returns the `1` in case of success and returns `nil` with a string describing the error otherwise. @@ -7307,7 +7921,7 @@ udpsock:settimeout **syntax:** *udpsock:settimeout(time)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Set the timeout value in milliseconds for subsequent socket operations (like [receive](#udpsockreceive)). @@ -7332,11 +7946,13 @@ ngx.socket.tcp **syntax:** *tcpsock = ngx.socket.tcp()* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Creates and returns a TCP or stream-oriented unix domain socket object (also known as one type of the "cosocket" objects). The following methods are supported on this object: +* [bind](#tcpsockbind) * [connect](#tcpsockconnect) +* [setclientcert](#tcpsocksetclientcert) * [sslhandshake](#tcpsocksslhandshake) * [send](#tcpsocksend) * [receive](#tcpsockreceive) @@ -7374,6 +7990,43 @@ See also [ngx.socket.udp](#ngxsocketudp). [Back to TOC](#nginx-api-for-lua) +tcpsock:bind +------------ +**syntax:** *ok, err = tcpsock:bind(address, port?)* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*,ssl_session_fetch_by_lua*,ssl_client_hello_by_lua** + +Just like the standard [proxy_bind](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_bind) directive, this api makes the outgoing connection to a upstream server originate from the specified local IP address. + +IP addresses can be specified as the `address` argument. +The optional `port` argument is usually used in the transparent proxy. + +Here is an example for connecting to a TCP server from the specified local IP address: + +```nginx + + location /test { + content_by_lua_block { + local sock = ngx.socket.tcp() + -- assume "192.168.1.10" is the local ip address + local ok, err = sock:bind("192.168.1.10") + if not ok then + ngx.say("failed to bind") + return + end + local ok, err = sock:connect("192.168.1.67", 80) + if not ok then + ngx.say("failed to connect server: ", err) + return + end + ngx.say("successfully connected!") + sock:close() + } + } +``` + +[Back to TOC](#nginx-api-for-lua) + tcpsock:connect --------------- @@ -7381,7 +8034,7 @@ tcpsock:connect **syntax:** *ok, err = tcpsock:connect("unix:/path/to/unix-domain.socket", options_table?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Attempts to connect a TCP socket object to a remote server or to a stream unix domain socket file without blocking. @@ -7476,14 +8129,14 @@ An optional Lua table can be specified as the last argument to this method to sp * `backlog` if specified, this module will limit the total number of opened connections for this pool. No more connections than `pool_size` can be opened - for this pool at any time. If the connection pool is full, subsequent - connect operations will be queued into a queue equal to this option's - value (the "backlog" queue). + for this pool at any time. If `pool_size` number of connections are in use, + subsequent connect operations will be queued into a queue equal to this + option's value (the "backlog" queue). If the number of queued connect operations is equal to `backlog`, subsequent connect operations will fail and return `nil` plus the error string `"too many waiting connect operations"`. - The queued connect operations will be resumed once the number of connections - in the pool is less than `pool_size`. + The queued connect operations will be resumed once the number of active + connections becomes less than `pool_size`. The queued connect operation will abort once they have been queued for more than `connect_timeout`, controlled by [settimeouts](#tcpsocksettimeouts), and will return `nil` plus @@ -7496,12 +8149,52 @@ This method was first introduced in the `v0.5.0rc1` release. [Back to TOC](#nginx-api-for-lua) + +tcpsock:getfd +-------------------- + +**syntax:** *fd, err = tcpsock:getfd()* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** + +Get the file descriptor of the current tcp socket. + +This method was first introduced in the `v0.10.29` release. + +[Back to TOC](#nginx-api-for-lua) + + +tcpsock:setclientcert +--------------------- + +**syntax:** *ok, err = tcpsock:setclientcert(cert, pkey)* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** + +Set client certificate chain and corresponding private key to the TCP socket object. +The certificate chain and private key provided will be used later by the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +* `cert` specify a client certificate chain cdata object that will be used while handshaking with +remote server. These objects can be created using [ngx.ssl.parse\_pem\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert) or [ngx.ssl.parse\_der\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_der_cert) +function provided by lua-resty-core. Note that specifying the `cert` option requires +corresponding `pkey` be provided too. See below. +* `pkey` specify a private key corresponds to the `cert` option above. +These objects can be created using [ngx.ssl.parse\_pem\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key) or [ngx.ssl.parse\_der\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_der_priv_key) +function provided by lua-resty-core. + +If both of `cert` and `pkey` are `nil`, this method will clear any existing client certificate and private key +that was previously set on the cosocket object. + +This method was first introduced in the `v0.10.22` release. + +[Back to TOC](#nginx-api-for-lua) + tcpsock:sslhandshake -------------------- **syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Does SSL/TLS handshake on the currently established connection. @@ -7547,7 +8240,7 @@ tcpsock:send **syntax:** *bytes, err = tcpsock:send(data)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Sends data without blocking on the current TCP or Unix Domain Socket connection. @@ -7580,7 +8273,7 @@ tcpsock:receive **syntax:** *data, err, partial = tcpsock:receive(pattern?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Receives data from the connected socket according to the reading pattern or size. @@ -7623,7 +8316,7 @@ tcpsock:receiveany **syntax:** *data, err = tcpsock:receiveany(max)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Returns any data received by the connected socket, at most `max` bytes. @@ -7658,7 +8351,7 @@ tcpsock:receiveuntil **syntax:** *iterator = tcpsock:receiveuntil(pattern, options?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** This method returns an iterator Lua function that can be called to read the data stream until it sees the specified pattern or an error occurs. @@ -7758,7 +8451,7 @@ tcpsock:close **syntax:** *ok, err = tcpsock:close()* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Closes the current TCP or stream unix domain socket. It returns the `1` in case of success and returns `nil` with a string describing the error otherwise. @@ -7775,7 +8468,7 @@ tcpsock:settimeout **syntax:** *tcpsock:settimeout(time)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Set the timeout value in milliseconds for subsequent socket operations ([connect](#tcpsockconnect), [receive](#tcpsockreceive), and iterators returned from [receiveuntil](#tcpsockreceiveuntil)). @@ -7792,7 +8485,7 @@ tcpsock:settimeouts **syntax:** *tcpsock:settimeouts(connect_timeout, send_timeout, read_timeout)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Respectively sets the connect, send, and read timeout thresholds (in milliseconds) for subsequent socket operations ([connect](#tcpsockconnect), [send](#tcpsocksend), [receive](#tcpsockreceive), and iterators returned from [receiveuntil](#tcpsockreceiveuntil)). @@ -7812,9 +8505,9 @@ tcpsock:setoption **syntax:** *ok, err = tcpsock:setoption(option, value?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** -This function is added for [LuaSocket](http://w3.impa.br/~diego/software/luasocket/tcp.html) API compatibility and does nothing for now. Its functionality is implemented `v0.10.18`. +This function is added for [LuaSocket](http://w3.impa.br/~diego/software/luasocket/tcp.html) API compatibility, its functionality is implemented `v0.10.18`. This feature was first introduced in the `v0.5.0rc1` release. @@ -7915,7 +8608,7 @@ tcpsock:setkeepalive **syntax:** *ok, err = tcpsock:setkeepalive(timeout?, size?)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Puts the current socket's connection immediately into the cosocket built-in connection pool and keep it alive until other [connect](#tcpsockconnect) method calls request it or the associated maximal idle timeout is expired. @@ -7962,7 +8655,7 @@ tcpsock:getreusedtimes **syntax:** *count, err = tcpsock:getreusedtimes()* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** This method returns the (successfully) reused times for the current connection. In case of error, it returns `nil` and a string describing the error. @@ -8004,7 +8697,7 @@ ngx.get_phase **syntax:** *str = ngx.get_phase()* -**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Retrieves the current running phase name. Possible return values are @@ -8018,6 +8711,8 @@ Retrieves the current running phase name. Possible return values are for the context of [ssl_session_fetch_by_lua*](#ssl_session_fetch_by_lua_block). * `ssl_session_store` for the context of [ssl_session_store_by_lua*](#ssl_session_store_by_lua_block). +* `ssl_client_hello` + for the context of [ssl_client_hello_by_lua*](#ssl_client_hello_by_lua_block). * `set` for the context of [set_by_lua*](#set_by_lua). * `rewrite` @@ -8048,7 +8743,7 @@ ngx.thread.spawn **syntax:** *co = ngx.thread.spawn(func, arg1, arg2, ...)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Spawns a new user "light thread" with the Lua function `func` as well as those optional arguments `arg1`, `arg2`, and etc. Returns a Lua thread (or Lua coroutine) object represents this "light thread". @@ -8068,9 +8763,9 @@ By default, the corresponding Nginx handler (e.g., [rewrite_by_lua](#rewrite_by_ When the user "light thread" terminates with a Lua error, however, it will not abort other running "light threads" like the "entry thread" does. -Due to the limitation in the Nginx subrequest model, it is not allowed to abort a running Nginx subrequest in general. So it is also prohibited to abort a running "light thread" that is pending on one ore more Nginx subrequests. You must call [ngx.thread.wait](#ngxthreadwait) to wait for those "light thread" to terminate before quitting the "world". A notable exception here is that you can abort pending subrequests by calling [ngx.exit](#ngxexit) with and only with the status code `ngx.ERROR` (-1), `408`, `444`, or `499`. +Due to the limitation in the Nginx subrequest model, it is not allowed to abort a running Nginx subrequest in general. So it is also prohibited to abort a running "light thread" that is pending on one or more Nginx subrequests. You must call [ngx.thread.wait](#ngxthreadwait) to wait for those "light thread" to terminate before quitting the "world". A notable exception here is that you can abort pending subrequests by calling [ngx.exit](#ngxexit) with and only with the status code `ngx.ERROR` (-1), `408`, `444`, or `499`. -The "light threads" are not scheduled in a pre-emptive way. In other words, no time-slicing is performed automatically. A "light thread" will keep running exclusively on the CPU until +The "light threads" are not scheduled in a preemptive way. In other words, no time-slicing is performed automatically. A "light thread" will keep running exclusively on the CPU until 1. a (nonblocking) I/O operation cannot be completed in a single run, 1. it calls [coroutine.yield](#coroutineyield) to actively give up execution, or @@ -8187,7 +8882,7 @@ ngx.thread.wait **syntax:** *ok, res1, res2, ... = ngx.thread.wait(thread1, thread2, ...)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Waits on one or more child "light threads" and returns the results of the first "light thread" that terminates (either successfully or with an error). @@ -8291,7 +8986,7 @@ ngx.thread.kill **syntax:** *ok, err = ngx.thread.kill(thread)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** Kills a running "light thread" created by [ngx.thread.spawn](#ngxthreadspawn). Returns a true value when successful or `nil` and a string describing the error otherwise. @@ -8347,7 +9042,7 @@ ngx.timer.at **syntax:** *hdl, err = ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Creates an Nginx timer with a user callback function as well as optional user arguments. @@ -8363,7 +9058,7 @@ be any Lua function, which will be invoked later in a background called automatically by the Nginx core with the arguments `premature`, `user_arg1`, `user_arg2`, and etc, where the `premature` argument takes a boolean value indicating whether it is a premature timer -expiration or not, and `user_arg1`, `user_arg2`, and etc, are +expiration or not(for the `0` delay timer it is always `false`), and `user_arg1`, `user_arg2`, and etc, are those (extra) user arguments specified when calling `ngx.timer.at` as the remaining arguments. @@ -8472,7 +9167,14 @@ user "light threads" ([ngx.thread.*](#ngxthreadspawn)), [ngx.exit](#ngxexit), [n (like [ngx.say](#ngxsay), [ngx.print](#ngxprint), and [ngx.flush](#ngxflush)) are explicitly disabled in this context. -You can pass most of the standard Lua values (nils, booleans, numbers, strings, tables, closures, file handles, and etc) into the timer callback, either explicitly as user arguments or implicitly as upvalues for the callback closure. There are several exceptions, however: you *cannot* pass any thread objects returned by [coroutine.create](#coroutinecreate) and [ngx.thread.spawn](#ngxthreadspawn) or any cosocket objects returned by [ngx.socket.tcp](#ngxsockettcp), [ngx.socket.udp](#ngxsocketudp), and [ngx.req.socket](#ngxreqsocket) because these objects' lifetime is bound to the request context creating them while the timer callback is detached from the creating request's context (by design) and runs in its own (fake) request context. If you try to share the thread or cosocket objects across the boundary of the creating request, then you will get the "no co ctx found" error (for threads) or "bad request" (for cosockets). It is fine, however, to create all these objects inside your timer callback. +You must notice that each timer will be based on a fake request (this fake request is also based on a fake connection). Because Nginx's memory release is based on the connection closure, if you run a lot of APIs that apply for memory resources in a timer, such as [tcpsock:connect](#tcpsockconnect), will cause the accumulation of memory resources. So it is recommended to create a new timer after running several times to release memory resources. + +You can pass most of the standard Lua values (nils, booleans, numbers, strings, tables, closures, file handles, etc.) into the timer callback, either explicitly as user arguments or implicitly as upvalues for the callback closure. There are several exceptions, however: you *cannot* pass any thread objects returned by [coroutine.create](#coroutinecreate) and [ngx.thread.spawn](#ngxthreadspawn) or any cosocket objects returned by [ngx.socket.tcp](#ngxsockettcp), [ngx.socket.udp](#ngxsocketudp), and [ngx.req.socket](#ngxreqsocket) because these objects' lifetime is bound to the request context creating them while the timer callback is detached from the creating request's context (by design) and runs in its own (fake) request context. If you try to share the thread or cosocket objects across the boundary of the creating request, then you will get the "no co ctx found" error (for threads) or "bad request" (for cosockets). It is fine, however, to create all these objects inside your timer callback. + +Please note that the timer Lua handler has its own copy of the `ngx.ctx` magic +table. It won't share the same `ngx.ctx` with the Lua handler creating the timer. +If you need to pass data from the timer creator to the timer handler, please +use the extra parameters of `ngx.timer.at()`. This API was first introduced in the `v0.8.0` release. @@ -8483,7 +9185,7 @@ ngx.timer.every **syntax:** *hdl, err = ngx.timer.every(delay, callback, user_arg1, user_arg2, ...)* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Similar to the [ngx.timer.at](#ngxtimerat) API function, but @@ -8506,7 +9208,7 @@ ngx.timer.running_count **syntax:** *count = ngx.timer.running_count()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns the number of timers currently running. @@ -8519,7 +9221,7 @@ ngx.timer.pending_count **syntax:** *count = ngx.timer.pending_count()* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** Returns the number of pending timers. @@ -8632,6 +9334,19 @@ This API was first introduced in the `0.9.5` release. [Back to TOC](#nginx-api-for-lua) +ngx.worker.pids +-------------- + +**syntax:** *pids = ngx.worker.pids()* + +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, exit_worker_by_lua** + +This function returns a Lua table for all Nginx worker process IDs (PIDs). Nginx uses channel to send the current worker PID to another worker in the worker process start or restart. So this API can get all current worker PIDs. Windows does not have this API. + +This API was first introduced in the `0.10.23` release. + +[Back to TOC](#nginx-api-for-lua) + ngx.worker.count ---------------- @@ -8757,7 +9472,7 @@ ndk.set_var.DIRECTIVE **syntax:** *res = ndk.set_var.DIRECTIVE_NAME* -**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** +**context:** *init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua** This mechanism allows calling other Nginx C modules' directives that are implemented by [Nginx Devel Kit](https://github.com/simplresty/ngx_devel_kit) (NDK)'s set_var submodule's `ndk_set_var_value`. @@ -8799,7 +9514,7 @@ coroutine.create **syntax:** *co = coroutine.create(f)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Creates a user Lua coroutines with a Lua function, and returns a coroutine object. @@ -8816,9 +9531,9 @@ coroutine.resume **syntax:** *ok, ... = coroutine.resume(co, ...)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** -Resumes the executation of a user Lua coroutine object previously yielded or just created. +Resumes the execution of a user Lua coroutine object previously yielded or just created. Similar to the standard Lua [coroutine.resume](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.resume) API, but works in the context of the Lua coroutines created by ngx_lua. @@ -8833,7 +9548,7 @@ coroutine.yield **syntax:** *... = coroutine.yield(...)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Yields the execution of the current user Lua coroutine. @@ -8850,7 +9565,7 @@ coroutine.wrap **syntax:** *co = coroutine.wrap(f)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Similar to the standard Lua [coroutine.wrap](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.wrap) API, but works in the context of the Lua coroutines created by ngx_lua. @@ -8865,7 +9580,7 @@ coroutine.running **syntax:** *co = coroutine.running()* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Identical to the standard Lua [coroutine.running](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.running) API. @@ -8880,7 +9595,7 @@ coroutine.status **syntax:** *status = coroutine.status(co)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** Identical to the standard Lua [coroutine.status](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.status) API. @@ -8890,6 +9605,135 @@ This API was first enabled in the `v0.6.0` release. [Back to TOC](#nginx-api-for-lua) +ngx.run_worker_thread +--------------------- + +**syntax:** *ok, res1, res2, ... = ngx.run_worker_thread(threadpool, module_name, func_name, arg1, arg2, ...)* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** + +**This API is still experimental and may change in the future without notice.** + +**This API is available only for Linux.** + +Wrap the [nginx worker thread](http://nginx.org/en/docs/dev/development_guide.html#threads) to execute lua function. The caller coroutine would yield until the function returns. + +Only the following ngx_lua APIs could be used in `function_name` function of the `module` module: + +* `ngx.encode_base64` +* `ngx.decode_base64` + +* `ngx.hmac_sha1` +* `ngx.encode_args` +* `ngx.decode_args` +* `ngx.quote_sql_str` + +* `ngx.crc32_short` +* `ngx.crc32_long` +* `ngx.hmac_sha1` +* `ngx.md5_bin` +* `ngx.md5` + +* `ngx.config.subsystem` +* `ngx.config.debug` +* `ngx.config.prefix` +* `ngx.config.nginx_version` +* `ngx.config.nginx_configure` +* `ngx.config.ngx_lua_version` + +* `ngx.shared.DICT` + +The first argument `threadpool` specifies the Nginx thread pool name defined by [thread_pool](https://nginx.org/en/docs/ngx_core_module.html#thread_pool). + +The second argument `module_name` specifies the lua module name to execute in the worker thread, which would return a lua table. The module must be inside the package path, e.g. + +```nginx + + lua_package_path '/opt/openresty/?.lua;;'; +``` + +The third argument `func_name` specifies the function field in the module table as the second argument. + +The type of `args` must be one of type below: + +* boolean +* number +* string +* nil +* table (the table may be recursive, and contains members of types above.) + +The `ok` is in boolean type, which indicate the C land error (failed to get thread from thread pool, pcall the module function failed, etc.). If `ok` is `false`, the `res1` is the error string. + +The return values (res1, ...) are returned by invocation of the module function. Normally, the `res1` should be in boolean type, so that the caller could inspect the error. + +This API is useful when you need to execute the below types of tasks: + +* CPU bound task, e.g. do md5 calculation +* File I/O task +* Call `os.execute()` or blocking C API via `ffi` +* Call external Lua library not based on cosocket or nginx + +Example1: do md5 calculation. + +```nginx + + location /calc_md5 { + default_type 'text/plain'; + + content_by_lua_block { + local ok, md5_or_err = ngx.run_worker_thread("testpool", "md5", "md5") + ngx.say(ok, " : ", md5_or_err) + } + } +``` + +`md5.lua` + +```lua +local function md5() + return ngx.md5("hello") +end + +return { md5=md5, } +``` + +Example2: write logs into the log file. + +```nginx + + location /write_log_file { + default_type 'text/plain'; + + content_by_lua_block { + local ok, err = ngx.run_worker_thread("testpool", "write_log_file", "log", ngx.var.arg_str) + if not ok then + ngx.say(ok, " : ", err) + return + end + ngx.say(ok) + } + } +``` + +`write_log_file.lua` + +```lua + + local function log(str) + local file, err = io.open("/tmp/tmp.log", "a") + if not file then + return false, err + end + file:write(str) + file:flush() + file:close() + return true + end + return {log=log} +``` + +[Back to TOC](#nginx-api-for-lua) + Obsolete Sections ================= diff --git a/config b/config index 4b32d38318..7b1b061362 100644 --- a/config +++ b/config @@ -261,6 +261,7 @@ HTTP_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_http_lua_util.c \ $ngx_addon_dir/src/ngx_http_lua_cache.c \ $ngx_addon_dir/src/ngx_http_lua_contentby.c \ + $ngx_addon_dir/src/ngx_http_lua_server_rewriteby.c \ $ngx_addon_dir/src/ngx_http_lua_rewriteby.c \ $ngx_addon_dir/src/ngx_http_lua_accessby.c \ $ngx_addon_dir/src/ngx_http_lua_setby.c \ @@ -286,16 +287,20 @@ HTTP_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_http_lua_timer.c \ $ngx_addon_dir/src/ngx_http_lua_config.c \ $ngx_addon_dir/src/ngx_http_lua_worker.c \ + $ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_certby.c \ + $ngx_addon_dir/src/ngx_http_lua_ssl_export_keying_material.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_ocsp.c \ $ngx_addon_dir/src/ngx_http_lua_lex.c \ $ngx_addon_dir/src/ngx_http_lua_balancer.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl.c \ + $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.c \ $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.c \ $ngx_addon_dir/src/ngx_http_lua_input_filters.c \ $ngx_addon_dir/src/ngx_http_lua_pipe.c \ + $ngx_addon_dir/src/ngx_http_lua_worker_thread.c \ " HTTP_LUA_DEPS=" \ @@ -323,6 +328,7 @@ HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_http_lua_util.h \ $ngx_addon_dir/src/ngx_http_lua_cache.h \ $ngx_addon_dir/src/ngx_http_lua_contentby.h \ + $ngx_addon_dir/src/ngx_http_lua_server_rewriteby.c \ $ngx_addon_dir/src/ngx_http_lua_rewriteby.h \ $ngx_addon_dir/src/ngx_http_lua_accessby.h \ $ngx_addon_dir/src/ngx_http_lua_setby.h \ @@ -346,15 +352,19 @@ HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_http_lua_uthread.h \ $ngx_addon_dir/src/ngx_http_lua_timer.h \ $ngx_addon_dir/src/ngx_http_lua_config.h \ + $ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl_certby.h \ $ngx_addon_dir/src/ngx_http_lua_lex.h \ $ngx_addon_dir/src/ngx_http_lua_balancer.h \ + $ngx_addon_dir/src/ngx_http_lua_ssl_export_keying_material.h \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl.h \ + $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.h \ $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.h \ $ngx_addon_dir/src/ngx_http_lua_input_filters.h \ $ngx_addon_dir/src/ngx_http_lua_pipe.h \ + $ngx_addon_dir/src/ngx_http_lua_worker_thread.h \ " # ---------------------------------------- @@ -454,7 +464,8 @@ ngx_feature_libs= ngx_feature_name="NGX_HTTP_LUA_HAVE_SIGNALFD" ngx_feature_run=no ngx_feature_incs="#include " -ngx_feature_test="sigset_t set; signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC);" +ngx_feature_test="sigset_t set = { 0 }; + signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC);" SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS" diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 4ff13f5541..09af86f771 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -8,6 +8,8 @@ then you are essentially using OpenResty. ''This module is not distributed with the Nginx source.'' See [[#Installation|the installation instructions]]. +This is a core component of OpenResty. If you are using this module, then you are essentially using OpenResty :) + = Status = Production ready. @@ -15,8 +17,8 @@ Production ready. = Version = This document describes ngx_lua -[https://github.com/openresty/lua-nginx-module/tags v0.10.19], which was released -on 3 Nov, 2020. +[https://github.com/openresty/lua-nginx-module/tags v0.10.25], which was released +on 19 June 2023. = Videos = @@ -32,6 +34,14 @@ on 3 Nov, 2020. [![OpenResty's resty Command-Line Utility Demo](https://img.youtube.com/vi/L1c7aw4mSOo/0.jpg)](https://youtu.be/L1c7aw4mSOo) +* YouTube video "[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)" + + [![Measure Execution Time of Lua Code Correctly in OpenResty](https://img.youtube.com/vi/VkRYW_qLoME/0.jpg)](https://youtu.be/VkRYW_qLoME) + +* YouTube video "[Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://youtu.be/EP7c0BM2yNo)" + + [![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo) + You are welcome to subscribe to our [official YouTube channel, OpenResty](https://www.youtube.com/channel/UCXVmwF-UCScv2ftsGoMqxhw). = Synopsis = @@ -275,6 +285,8 @@ Alternatively, ngx_lua can be manually compiled into Nginx: # Download the latest version of the ngx_devel_kit (NDK) module [https://github.com/simplresty/ngx_devel_kit/tags HERE] # Download the latest version of ngx_lua [https://github.com/openresty/lua-nginx-module/tags HERE] # Download the latest supported version of Nginx [https://nginx.org/ HERE] (See [[#Nginx Compatibility|Nginx Compatibility]]) +# Download the latest version of the lua-resty-core [HERE](https://github.com/openresty/lua-resty-core) +# Download the latest version of the lua-resty-lrucache [HERE](https://github.com/openresty/lua-resty-lrucache) Build the source with this module: @@ -301,10 +313,22 @@ Build the source with this module: # current nginx build. # You can get usually those options using command nginx -V - # you can change the parallism number 2 below to fit the number of spare CPU cores in your + # you can change the parallelism number 2 below to fit the number of spare CPU cores in your # machine. make -j2 make install + + # Note that this version of lug-nginx-module not allow to set `lua_load_resty_core off;` any more. + # So, you have to install `lua-resty-core` and `lua-resty-lrucache` manually as below. + + cd lua-resty-core + make install PREFIX=/opt/nginx + cd lua-resty-lrucache + make install PREFIX=/opt/nginx + + # add necessary `lua_package_path` directive to `nginx.conf`, in the http context + + lua_package_path "/opt/nginx/lib/lua/?.lua;;"; == Building as a dynamic module == @@ -357,6 +381,10 @@ Please submit bug reports, wishlists, or patches by = LuaJIT bytecode support = +Watch YouTube video "[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)" + +[![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo) + As from the v0.5.0rc32 release, all *_by_lua_file configure directives (such as [[#content_by_lua_file|content_by_lua_file]]) support loading LuaJIT 2.0/2.1 raw bytecode files directly: @@ -743,6 +771,8 @@ As noted earlier, PCRE sequences presented within *_by_lua_block {} # evaluates to "1234" +'''NOTE''' You are recommended to use `by_lua_file` when the Lua code is very long. + == Mixing with SSI Not Supported == Mixing SSI with ngx_lua in the same Nginx request is not supported at all. Just use ngx_lua exclusively. Everything you can do with SSI can be done atop ngx_lua anyway and it can be more efficient when using ngx_lua. @@ -778,12 +808,10 @@ phases. * cosocket: review and merge aviramc's [https://github.com/openresty/lua-nginx-module/pull/290 patch] for adding the bsdrecv method. * cosocket: add configure options for different strategies of handling the cosocket connection exceeding in the pools. * review and apply vadim-pavlov's patch for [[#ngx.location.capture|ngx.location.capture]]'s extra_headers option -* use ngx_hash_t to optimize the built-in header look-up process for [[#ngx.req.set_header|ngx.req.set_header]], [[#ngx.header.HEADER|ngx.header.HEADER]], and etc. -* add directives to run Lua codes when Nginx stops. +* use ngx_hash_t to optimize the built-in header look-up process for [[#ngx.req.set_header|ngx.req.set_header]], and etc. * add ignore_resp_headers, ignore_resp_body, and ignore_resp options to [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] methods, to allow micro performance tuning on the user side. * add automatic Lua code time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks. * add stat mode similar to [https://httpd.apache.org/docs/trunk/mod/mod_lua.html mod_lua]. -* cosocket: add client SSL certificate support. = Changes = @@ -1140,9 +1168,29 @@ As from the v0.5.0rc29 release, the special notation $prefix< '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#init_by_lua_block|init_by_lua_block]] directive instead. -Runs the Lua code specified by the argument on the global Lua VM level when the Nginx master process (if any) is loading the Nginx config file. +Similar to the [[#init_by_lua_block|init_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + + + init_by_lua ' + print("I need no extra escaping here, for example: \r\nblah") + ' + + +This directive was first introduced in the v0.5.5 release. + +== init_by_lua_block == + +'''syntax:''' ''init_by_lua_block { lua-script }'' + +'''context:''' ''http'' + +'''phase:''' ''loading-config'' + -When Nginx receives the HUP signal and starts reloading the config file, the Lua VM will also be re-created and init_by_lua will run again on the new Lua VM. In case that the [[#lua_code_cache|lua_code_cache]] directive is turned off (default on), the init_by_lua handler will run upon every request because in this special mode a standalone Lua VM is always created for each request. +When Nginx receives the HUP signal and starts reloading the config file, the Lua VM will also be re-created and init_by_lua_block will run again on the new Lua VM. In case that the [[#lua_code_cache|lua_code_cache]] directive is turned off (default on), the init_by_lua_block handler will run upon every request because in this special mode a standalone Lua VM is always created for each request. Usually you can pre-load Lua modules at server start-up by means of this hook and take advantage of modern operating systems' copy-on-write (COW) optimization. Here is an example for pre-loading Lua modules: @@ -1181,11 +1229,11 @@ You can also initialize the [[#lua_shared_dict|lua_shared_dict]] shm storage at } -But note that, the [[#lua_shared_dict|lua_shared_dict]]'s shm storage will not be cleared through a config reload (via the HUP signal, for example). So if you do ''not'' want to re-initialize the shm storage in your init_by_lua code in this case, then you just need to set a custom flag in the shm storage and always check the flag in your init_by_lua code. +But note that, the [[#lua_shared_dict|lua_shared_dict]]'s shm storage will not be cleared through a config reload (via the HUP signal, for example). So if you do ''not'' want to re-initialize the shm storage in your init_by_lua_block code in this case, then you just need to set a custom flag in the shm storage and always check the flag in your init_by_lua_block code. Because the Lua code in this context runs before Nginx forks its worker processes (if any), data or code loaded here will enjoy the [https://en.wikipedia.org/wiki/Copy-on-write Copy-on-write (COW)] feature provided by many operating systems among all the worker processes, thus saving a lot of memory. -Do *not* initialize your own Lua global variables in this context because use of Lua global variables have performance penalties and can lead to global namespace pollution (see the [[#Lua_Variable_Scope|Lua Variable Scope]] section for more details). The recommended way is to use proper [https://www.lua.org/manual/5.1/manual.html#5.3 Lua module] files (but do not use the standard Lua function [https://www.lua.org/manual/5.1/manual.html#pdf-module module()] to define Lua modules because it pollutes the global namespace as well) and call [https://www.lua.org/manual/5.1/manual.html#pdf-require require()] to load your own module files in init_by_lua or other contexts ([https://www.lua.org/manual/5.1/manual.html#pdf-require require()] does cache the loaded Lua modules in the global package.loaded table in the Lua registry so your modules will only loaded once for the whole Lua VM instance). +Do *not* initialize your own Lua global variables in this context because use of Lua global variables have performance penalties and can lead to global namespace pollution (see the [[#Lua_Variable_Scope|Lua Variable Scope]] section for more details). The recommended way is to use proper [https://www.lua.org/manual/5.1/manual.html#5.3 Lua module] files (but do not use the standard Lua function [https://www.lua.org/manual/5.1/manual.html#pdf-module module()] to define Lua modules because it pollutes the global namespace as well) and call [https://www.lua.org/manual/5.1/manual.html#pdf-require require()] to load your own module files in init_by_lua_block or other contexts ([https://www.lua.org/manual/5.1/manual.html#pdf-require require()] does cache the loaded Lua modules in the global package.loaded table in the Lua registry so your modules will only loaded once for the whole Lua VM instance). Only a small set of the [[#Nginx API for Lua|Nginx API for Lua]] is supported in this context: @@ -1198,36 +1246,13 @@ Basically you can safely use Lua libraries that do blocking I/O in this very con You should be very careful about potential security vulnerabilities in your Lua code registered in this context because the Nginx master process is often run under the root account. -This directive was first introduced in the v0.5.5 release. +This directive was first introduced in the v0.9.17 release. See also the following blog posts for more details on OpenResty and Nginx's shared memory zones: * [How OpenResty and Nginx Shared Memory Zones Consume RAM](https://blog.openresty.com/en/how-nginx-shm-consume-ram/?src=gh_ngxlua) * [Memory Fragmentation in OpenResty and Nginx's Shared Memory Zones](https://blog.openresty.com/en/nginx-shm-frag/?src=gh_ngxlua) -== init_by_lua_block == - -'''syntax:''' ''init_by_lua_block { lua-script }'' - -'''context:''' ''http'' - -'''phase:''' ''loading-config'' - -Similar to the [[#init_by_lua|init_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - - - init_by_lua_block { - print("I need no extra escaping here, for example: \r\nblah") - } - - -This directive was first introduced in the v0.9.17 release. - == init_by_lua_file == '''syntax:''' ''init_by_lua_file '' @@ -1236,7 +1261,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''loading-config'' -Equivalent to [[#init_by_lua|init_by_lua]], except that the file specified by contains the Lua code or [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#init_by_lua_block|init_by_lua_block]], except that the file specified by contains the Lua code or [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. @@ -1252,12 +1277,35 @@ This directive was first introduced in the v0.5.5 release. '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#init_worker_by_lua_block|init_worker_by_lua_block]] directive instead. -Runs the specified Lua code upon every Nginx worker process's startup when the master process is enabled. When the master process is disabled, this hook will just run after [[#init_by_lua|init_by_lua*]]. +Similar to the [[#init_worker_by_lua_block|init_worker_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). -This hook is often used to create per-worker reoccurring timers (via the [[#ngx.timer.at|ngx.timer.at]] Lua API), either for backend health-check or other timed routine work. Below is an example, +For instance, init_worker_by_lua ' + print("I need no extra escaping here, for example: \r\nblah") + '; + + +This directive was first introduced in the v0.9.5 release. + +This hook no longer runs in the cache manager and cache loader processes since the v0.10.12 release. + +== init_worker_by_lua_block == + +'''syntax:''' ''init_worker_by_lua_block { lua-script }'' + +'''context:''' ''http'' + +'''phase:''' ''starting-worker'' + +Runs the specified Lua code upon every Nginx worker process's startup when the master process is enabled. When the master process is disabled, this hook will just run after [[#init_by_lua_block|init_by_lua*]]. + +This hook is often used to create per-worker reoccurring timers (via the [[#ngx.timer.at|ngx.timer.at]] Lua API), either for backend health-check or other timed routine work. Below is an example, + + + init_worker_by_lua_block { local delay = 3 -- in seconds local new_timer = ngx.timer.at local log = ngx.log @@ -1284,31 +1332,6 @@ This hook is often used to create per-worker reoccurring timers (via the [[#ngx. end -- other job in init_worker_by_lua - '; - - -This directive was first introduced in the v0.9.5 release. - -This hook no longer runs in the cache manager and cache loader processes since the v0.10.12 release. - -== init_worker_by_lua_block == - -'''syntax:''' ''init_worker_by_lua_block { lua-script }'' - -'''context:''' ''http'' - -'''phase:''' ''starting-worker'' - -Similar to the [[#init_worker_by_lua|init_worker_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - - - init_worker_by_lua_block { - print("I need no extra escaping here, for example: \r\nblah") } @@ -1324,7 +1347,7 @@ This hook no longer runs in the cache manager and cache loader processes since t '''phase:''' ''starting-worker'' -Similar to [[#init_worker_by_lua|init_worker_by_lua]], but accepts the file path to a Lua source file or Lua bytecode file. +Similar to [[#init_worker_by_lua_block|init_worker_by_lua_block]], but accepts the file path to a Lua source file or Lua bytecode file. This directive was first introduced in the v0.9.5 release. @@ -1340,7 +1363,7 @@ This hook no longer runs in the cache manager and cache loader processes since t Runs the specified Lua code upon every Nginx worker process's exit when the master process is enabled. When the master process is disabled, this hook will run before the Nginx process exits. -This hook is often used to release resources allocated by each worker (e.g. resources allocated by [[#init_worker_by_lua|init_worker_by_lua*]]), or to prevent workers from exiting abnormally. +This hook is often used to release resources allocated by each worker (e.g. resources allocated by [[#init_worker_by_lua_block|init_worker_by_lua*]]), or to prevent workers from exiting abnormally. For example, @@ -1350,6 +1373,8 @@ For example, } +It's not allowed to create a timer (even a 0-delay timer) here since it runs after all timers have been processed. + This directive was first introduced in the v0.10.18 release. == exit_worker_by_lua_file == @@ -1374,14 +1399,37 @@ This directive was first introduced in the v0.10.18 release. '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#set_by_lua_block|set_by_lua_block]] directive instead. -Executes code specified in with optional input arguments $arg1 $arg2 ..., and returns string output to $res. -The code in can make [[#Nginx API for Lua|API calls]] and can retrieve input arguments from the ngx.arg table (index starts from 1 and increases sequentially). +Similar to the [[#set_by_lua_block|set_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping), and +# this directive support extra arguments after the Lua script. + +For example, + + + set_by_lua $res ' return 32 + math.cos(32) '; + # $res now has the value "32.834223360507" or alike. + + +As from the v0.5.0rc29 release, Nginx variable interpolation is disabled in the argument of this directive and therefore, the dollar sign character ($) can be used directly. + +This directive requires the [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit] module. + +== set_by_lua_block == + +'''syntax:''' ''set_by_lua_block $res { lua-script }'' + +'''context:''' ''server, server if, location, location if'' + +'''phase:''' ''rewrite'' + +Executes code specified inside a pair of curly braces ({}), and returns string output to $res. +The code inside a pair of curly braces ({}) can make [[#Nginx API for Lua|API calls]] and can retrieve input arguments from the ngx.arg table (index starts from 1 and increases sequentially). This directive is designed to execute short, fast running code blocks as the Nginx event loop is blocked during code execution. Time consuming code sequences should therefore be avoided. This directive is implemented by injecting custom commands into the standard [[HttpRewriteModule]]'s command list. Because [[HttpRewriteModule]] does not support nonblocking I/O in its commands, Lua APIs requiring yielding the current Lua "light thread" cannot work in this directive. -At least the following API functions are currently disabled within the context of set_by_lua: +At least the following API functions are currently disabled within the context of set_by_lua_block: * Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]]) * Control API functions (e.g., [[#ngx.exit|ngx.exit]]) @@ -1396,13 +1444,13 @@ a time. However, a workaround is possible using the [[#ngx.var.VARIABLE|ngx.var. location /foo { set $diff ''; # we have to predefine the $diff variable here - set_by_lua $sum ' + set_by_lua_block $sum { local a = 32 local b = 56 ngx.var.diff = a - b -- write to $diff directly return a + b -- return the $sum value normally - '; + } echo "sum = $sum, diff = $diff"; } @@ -1412,38 +1460,14 @@ This directive can be freely mixed with all directives of the [[HttpRewriteModul set $foo 32; - set_by_lua $bar 'return tonumber(ngx.var.foo) + 1'; + set_by_lua_block $bar { return tonumber(ngx.var.foo) + 1 } set $baz "bar: $bar"; # $baz == "bar: 33" -As from the v0.5.0rc29 release, Nginx variable interpolation is disabled in the argument of this directive and therefore, the dollar sign character ($) can be used directly. +No special escaping is required in the Lua code block. This directive requires the [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit] module. -== set_by_lua_block == - -'''syntax:''' ''set_by_lua_block $res { lua-script }'' - -'''context:''' ''server, server if, location, location if'' - -'''phase:''' ''rewrite'' - -Similar to the [[#set_by_lua|set_by_lua]] directive except that - -# this directive inlines the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping), and -# this directive does not support extra arguments after the Lua script as in [[#set_by_lua|set_by_lua]]. - -For example, - - - set_by_lua_block $res { return 32 + math.cos(32) } - # $res now has the value "32.834223360507" or alike. - - -No special escaping is required in the Lua code block. - This directive was first introduced in the v0.9.17 release. == set_by_lua_file == @@ -1454,7 +1478,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''rewrite'' -Equivalent to [[#set_by_lua|set_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#set_by_lua_block|set_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. Nginx variable interpolation is supported in the argument string of this directive. But special care must be taken for injection attacks. @@ -1477,10 +1501,16 @@ This directive requires the [https://github.com/simplresty/ngx_devel_kit ngx_dev '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#content_by_lua_block|content_by_lua_block]] directive instead. -Acts as a "content handler" and executes Lua code string specified in for every request. -The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). +Similar to the [[#content_by_lua_block|content_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). -Do not use this directive and other content handler directives in the same location. For example, this directive and the [[HttpProxyModule#proxy_pass|proxy_pass]] directive should not be used in the same location. +For instance, + + + content_by_lua ' + ngx.say("I need no extra escaping here, for example: \r\nblah") + '; + == content_by_lua_block == @@ -1490,11 +1520,6 @@ Do not use this directive and other content handler directives in the same locat '''phase:''' ''content'' -Similar to the [[#content_by_lua|content_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). - For instance, @@ -1503,6 +1528,11 @@ For instance, } +Acts as a "content handler" and executes Lua code string specified in { lua-script } for every request. +The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). + +Do not use this directive and other content handler directives in the same location. For example, this directive and the [[HttpProxyModule#proxy_pass|proxy_pass]] directive should not be used in the same location. + This directive was first introduced in the v0.9.17 release. == content_by_lua_file == @@ -1513,7 +1543,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''content'' -Equivalent to [[#content_by_lua|content_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#content_by_lua_block|content_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. Nginx variables can be used in the string to provide flexibility. This however carries some risks and is not ordinarily recommended. @@ -1537,6 +1567,88 @@ Nginx variables are supported in the file path for dynamic dispatch, for example But be very careful about malicious user inputs and always carefully validate or filter out the user-supplied path components. +== server_rewrite_by_lua_block == + +'''syntax:''' ''server_rewrite_by_lua_block { lua-script }'' + +'''context:''' ''http, server'' + +'''phase:''' ''server rewrite'' + +Acts as a server rewrite phase handler and executes Lua code string specified in { lua-script } for every request. +The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). + + +server { + ... + + server_rewrite_by_lua_block { + ngx.ctx.a = "server_rewrite_by_lua_block in http" + } + + location /lua { + content_by_lua_block { + ngx.say(ngx.ctx.a) + ngx.log(ngx.INFO, ngx.ctx.a) + } + } +} + + +Just as any other rewrite phase handlers, [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]] also runs in subrequests. + + +server { + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "is_subrequest:", ngx.is_subrequest) + } + + location /lua { + content_by_lua_block { + local res = ngx.location.capture("/sub") + ngx.print(res.body) + } + } + + location /sub { + content_by_lua_block { + ngx.say("OK") + } + } +} + + +Note that when calling ngx.exit(ngx.OK) within a [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]] handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]] handler, call [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. + + + + server_rewrite_by_lua_block { + ngx.exit(503) + } + + location /bar { + ... + # never exec + } + + + +== server_rewrite_by_lua_file == + +'''syntax:''' ''server_rewrite_by_lua_file '' + +'''context:''' ''http, server'' + +'''phase:''' ''server rewrite'' + +Equivalent to [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.10.22 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. + +Nginx variables can be used in the string to provide flexibility. This however carries some risks and is not ordinarily recommended. + +When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. + +When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] off in nginx.conf to avoid reloading Nginx. + == rewrite_by_lua == '''syntax:''' ''rewrite_by_lua '' @@ -1547,7 +1659,26 @@ But be very careful about malicious user inputs and always carefully validate or '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#rewrite_by_lua_block|rewrite_by_lua_block]] directive instead. -Acts as a rewrite phase handler and executes Lua code string specified in for every request. +Similar to the [[#rewrite_by_lua_block|rewrite_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + + + rewrite_by_lua ' + do_something("hello, world!\nhiya\n") + '; + + +== rewrite_by_lua_block == + +'''syntax:''' ''rewrite_by_lua_block { lua-script }'' + +'''context:''' ''http, server, location, location if'' + +'''phase:''' ''rewrite tail'' + +Acts as a rewrite phase handler and executes Lua code string specified in { lua-script } for every request. The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Note that this handler always runs ''after'' the standard [[HttpRewriteModule]]. So the following will work as expected: @@ -1556,12 +1687,14 @@ Note that this handler always runs ''after'' the standard [[HttpRewriteModule]]. location /foo { set $a 12; # create and initialize $a set $b ""; # create and initialize $b - rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; + rewrite_by_lua_block { + ngx.var.b = tonumber(ngx.var.a) + 1 + } echo "res = $b"; } -because set $a 12 and set $b "" run ''before'' [[#rewrite_by_lua|rewrite_by_lua]]. +because set $a 12 and set $b "" run ''before'' [[#rewrite_by_lua_block|rewrite_by_lua_block]]. On the other hand, the following will not work as expected: @@ -1569,7 +1702,9 @@ On the other hand, the following will not work as expected: ? location /foo { ? set $a 12; # create and initialize $a ? set $b ''; # create and initialize $b - ? rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; + ? rewrite_by_lua_block { + ? ngx.var.b = tonumber(ngx.var.a) + 1 + ? } ? if ($b = '13') { ? rewrite ^ /bar redirect; ? break; @@ -1579,7 +1714,7 @@ On the other hand, the following will not work as expected: ? } -because if runs ''before'' [[#rewrite_by_lua|rewrite_by_lua]] even if it is placed after [[#rewrite_by_lua|rewrite_by_lua]] in the config. +because if runs ''before'' [[#rewrite_by_lua_block|rewrite_by_lua_block]] even if it is placed after [[#rewrite_by_lua_block|rewrite_by_lua_block]] in the config. The right way of doing this is as follows: @@ -1587,18 +1722,18 @@ The right way of doing this is as follows: location /foo { set $a 12; # create and initialize $a set $b ''; # create and initialize $b - rewrite_by_lua ' + rewrite_by_lua_block { ngx.var.b = tonumber(ngx.var.a) + 1 if tonumber(ngx.var.b) == 13 then return ngx.redirect("/bar") end - '; + } echo "res = $b"; } -Note that the [http://www.grid.net.ru/nginx/eval.en.html ngx_eval] module can be approximated by using [[#rewrite_by_lua|rewrite_by_lua]]. For example, +Note that the [http://www.grid.net.ru/nginx/eval.en.html ngx_eval] module can be approximated by using [[#rewrite_by_lua_block|rewrite_by_lua_block]]. For example, location / { @@ -1623,57 +1758,38 @@ can be implemented in ngx_lua as: } location / { - rewrite_by_lua ' + rewrite_by_lua_block { local res = ngx.location.capture("/check-spam") if res.body == "spam" then return ngx.redirect("/terms-of-use.html") end - '; + } fastcgi_pass ...; } -Just as any other rewrite phase handlers, [[#rewrite_by_lua|rewrite_by_lua]] also runs in subrequests. +Just as any other rewrite phase handlers, [[#rewrite_by_lua_block|rewrite_by_lua_block]] also runs in subrequests. -Note that when calling ngx.exit(ngx.OK) within a [[#rewrite_by_lua|rewrite_by_lua]] handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#rewrite_by_lua|rewrite_by_lua]] handler, call [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. +Note that when calling ngx.exit(ngx.OK) within a [[#rewrite_by_lua_block|rewrite_by_lua_block]] handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#rewrite_by_lua_block|rewrite_by_lua_block]] handler, call [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. -If the [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive is used to change the URI and initiate location re-lookups (internal redirections), then any [[#rewrite_by_lua|rewrite_by_lua]] or [[#rewrite_by_lua_file|rewrite_by_lua_file]] code sequences within the current location will not be executed. For example, +If the [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive is used to change the URI and initiate location re-lookups (internal redirections), then any [[#rewrite_by_lua_block|rewrite_by_lua_block]] or [[#rewrite_by_lua_file_block|rewrite_by_lua_file_block]] code sequences within the current location will not be executed. For example, location /foo { rewrite ^ /bar; - rewrite_by_lua 'ngx.exit(503)'; + rewrite_by_lua_block { + ngx.exit(503) + } } location /bar { ... } -Here the Lua code ngx.exit(503) will never run. This will be the case if rewrite ^ /bar last is used as this will similarly initiate an internal redirection. If the break modifier is used instead, there will be no internal redirection and the rewrite_by_lua code will be executed. - -The rewrite_by_lua code will always run at the end of the rewrite request-processing phase unless [[#rewrite_by_lua_no_postpone|rewrite_by_lua_no_postpone]] is turned on. - -== rewrite_by_lua_block == - -'''syntax:''' ''rewrite_by_lua_block { lua-script }'' - -'''context:''' ''http, server, location, location if'' - -'''phase:''' ''rewrite tail'' - -Similar to the [[#rewrite_by_lua|rewrite_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, +Here the Lua code ngx.exit(503) will never run. This will be the case if rewrite ^ /bar last is used as this will similarly initiate an internal redirection. If the break modifier is used instead, there will be no internal redirection and the rewrite_by_lua_block code will be executed. - - rewrite_by_lua_block { - do_something("hello, world!\nhiya\n") - } - +The rewrite_by_lua_block code will always run at the end of the rewrite request-processing phase unless [[#rewrite_by_lua_no_postpone|rewrite_by_lua_no_postpone]] is turned on. This directive was first introduced in the v0.9.17 release. @@ -1685,7 +1801,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''rewrite tail'' -Equivalent to [[#rewrite_by_lua|rewrite_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#rewrite_by_lua_block|rewrite_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. Nginx variables can be used in the string to provide flexibility. This however carries some risks and is not ordinarily recommended. @@ -1707,7 +1823,26 @@ Nginx variables are supported in the file path for dynamic dispatch just as in [ '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#access_by_lua_block|access_by_lua_block]] directive instead. -Acts as an access phase handler and executes Lua code string specified in for every request. +Similar to the [[#access_by_lua_block|access_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + + + access_by_lua ' + do_something("hello, world!\nhiya\n") + '; + + +== access_by_lua_block == + +'''syntax:''' ''access_by_lua_block { lua-script }'' + +'''context:''' ''http, server, location, location if'' + +'''phase:''' ''access tail'' + +Acts as an access phase handler and executes Lua code string specified in { for every request. The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Note that this handler always runs ''after'' the standard [[HttpAccessModule]]. So the following will work as expected: @@ -1719,18 +1854,18 @@ Note that this handler always runs ''after'' the standard [[HttpAccessModule]]. allow 10.1.1.0/16; deny all; - access_by_lua ' + access_by_lua_block { local res = ngx.location.capture("/mysql", { ... }) ... - '; + } # proxy_pass/fastcgi_pass/... } -That is, if a client IP address is in the blacklist, it will be denied before the MySQL query for more complex authentication is executed by [[#access_by_lua|access_by_lua]]. +That is, if a client IP address is in the blacklist, it will be denied before the MySQL query for more complex authentication is executed by [[#access_by_lua_block|access_by_lua_block]]. -Note that the [http://mdounin.ru/hg/ngx_http_auth_request_module/ ngx_auth_request] module can be approximated by using [[#access_by_lua|access_by_lua]]: +Note that the [http://mdounin.ru/hg/ngx_http_auth_request_module/ ngx_auth_request] module can be approximated by using [[#access_by_lua_block|access_by_lua_block]]: location / { @@ -1744,7 +1879,7 @@ can be implemented in ngx_lua as: location / { - access_by_lua ' + access_by_lua_block { local res = ngx.location.capture("/auth") if res.status == ngx.HTTP_OK then @@ -1756,41 +1891,20 @@ can be implemented in ngx_lua as: end ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) - '; + } # proxy_pass/fastcgi_pass/postgres_pass/... } -As with other access phase handlers, [[#access_by_lua|access_by_lua]] will ''not'' run in subrequests. +As with other access phase handlers, [[#access_by_lua_block|access_by_lua_block]] will ''not'' run in subrequests. -Note that when calling ngx.exit(ngx.OK) within a [[#access_by_lua|access_by_lua]] handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#access_by_lua|access_by_lua]] handler, call [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. +Note that when calling ngx.exit(ngx.OK) within a [[#access_by_lua_block|access_by_lua_block]] handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#access_by_lua_block|access_by_lua_block]] handler, call [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. Starting from the v0.9.20 release, you can use the [[#access_by_lua_no_postpone|access_by_lua_no_postpone]] directive to control when to run this handler inside the "access" request-processing phase of Nginx. -== access_by_lua_block == - -'''syntax:''' ''access_by_lua_block { lua-script }'' - -'''context:''' ''http, server, location, location if'' - -'''phase:''' ''access tail'' - -Similar to the [[#access_by_lua|access_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - - - access_by_lua_block { - do_something("hello, world!\nhiya\n") - } - - This directive was first introduced in the v0.9.17 release. == access_by_lua_file == @@ -1801,7 +1915,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''access tail'' -Equivalent to [[#access_by_lua|access_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#access_by_lua_block|access_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. Nginx variables can be used in the string to provide flexibility. This however carries some risks and is not ordinarily recommended. @@ -1823,22 +1937,15 @@ Nginx variables are supported in the file path for dynamic dispatch just as in [ '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#header_filter_by_lua_block|header_filter_by_lua_block]] directive instead. -Uses Lua code specified in to define an output header filter. - -Note that the following API functions are currently disabled within this context: - -* Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]]) -* Control API functions (e.g., [[#ngx.redirect|ngx.redirect]] and [[#ngx.exec|ngx.exec]]) -* Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]]) -* Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]). +Similar to the [[#header_filter_by_lua_block|header_filter_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). -Here is an example of overriding a response header (or adding one if absent) in our Lua header filter: +For instance, - location / { - proxy_pass http://mybackend; - header_filter_by_lua 'ngx.header.Foo = "blah"'; - } + header_filter_by_lua ' + ngx.header["content-length"] = nil + '; This directive was first introduced in the v0.2.1rc20 release. @@ -1851,16 +1958,23 @@ This directive was first introduced in the v0.2.1rc20 release. '''phase:''' ''output-header-filter'' -Similar to the [[#header_filter_by_lua|header_filter_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). +Uses Lua code specified in { lua-script } to define an output header filter. -For instance, +Note that the following API functions are currently disabled within this context: + +* Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]]) +* Control API functions (e.g., [[#ngx.redirect|ngx.redirect]] and [[#ngx.exec|ngx.exec]]) +* Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]]) +* Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]). + +Here is an example of overriding a response header (or adding one if absent) in our Lua header filter: - header_filter_by_lua_block { - ngx.header["content-length"] = nil + location / { + proxy_pass http://mybackend; + header_filter_by_lua_block { + ngx.header.Foo = "blah" + } } @@ -1874,7 +1988,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''output-header-filter'' -Equivalent to [[#header_filter_by_lua|header_filter_by_lua]], except that the file specified by contains the Lua code, or as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#header_filter_by_lua_block|header_filter_by_lua_block]], except that the file specified by contains the Lua code, or as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. @@ -1890,7 +2004,28 @@ This directive was first introduced in the v0.2.1rc20 release. '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#body_filter_by_lua_block|body_filter_by_lua_block]] directive instead. -Uses Lua code specified in to define an output body filter. +Similar to the [[#body_filter_by_lua_block|body_filter_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + + + body_filter_by_lua ' + local data, eof = ngx.arg[1], ngx.arg[2] + '; + + +This directive was first introduced in the v0.5.0rc32 release. + +== body_filter_by_lua_block == + +'''syntax:''' ''body_filter_by_lua_block { lua-script-str }'' + +'''context:''' ''http, server, location, location if'' + +'''phase:''' ''output-body-filter'' + +Uses Lua code specified in { lua-script } to define an output body filter. The input data chunk is passed via [[#ngx.arg|ngx.arg]][1] (as a Lua string value) and the "eof" flag indicating the end of the response body data stream is passed via [[#ngx.arg|ngx.arg]][2] (as a Lua boolean value). @@ -1909,7 +2044,9 @@ The Lua code can pass its own modified version of the input data chunk to the do location / { proxy_pass http://mybackend; - body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])'; + body_filter_by_lua_block { + ngx.arg[1] = string.upper(ngx.arg[1]) + } } @@ -1922,7 +2059,7 @@ Likewise, new "eof" flag can also be specified by setting a boolean value to [[# echo hello world; echo hiya globe; - body_filter_by_lua ' + body_filter_by_lua_block { local chunk = ngx.arg[1] if string.match(chunk, "hello") then ngx.arg[2] = true -- new eof @@ -1931,7 +2068,7 @@ Likewise, new "eof" flag can also be specified by setting a boolean value to [[# -- just throw away any remaining chunk data ngx.arg[1] = nil - '; + } } @@ -1949,8 +2086,12 @@ When the Lua code may change the length of the response body, then it is require location /foo { # fastcgi_pass/proxy_pass/... - header_filter_by_lua_block { ngx.header.content_length = nil } - body_filter_by_lua 'ngx.arg[1] = string.len(ngx.arg[1]) .. "\\n"'; + header_filter_by_lua_block { + ngx.header.content_length = nil + } + body_filter_by_lua_block { + ngx.arg[1] = string.len(ngx.arg[1]) .. "\n" + } } @@ -1963,29 +2104,6 @@ Note that the following API functions are currently disabled within this context Nginx output filters may be called multiple times for a single request because response body may be delivered in chunks. Thus, the Lua code specified by in this directive may also run multiple times in the lifetime of a single HTTP request. -This directive was first introduced in the v0.5.0rc32 release. - -== body_filter_by_lua_block == - -'''syntax:''' ''body_filter_by_lua_block { lua-script-str }'' - -'''context:''' ''http, server, location, location if'' - -'''phase:''' ''output-body-filter'' - -Similar to the [[#body_filter_by_lua|body_filter_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - - - body_filter_by_lua_block { - local data, eof = ngx.arg[1], ngx.arg[2] - } - - This directive was first introduced in the v0.9.17 release. == body_filter_by_lua_file == @@ -1996,7 +2114,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''output-body-filter'' -Equivalent to [[#body_filter_by_lua|body_filter_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#body_filter_by_lua_block|body_filter_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. @@ -2012,7 +2130,28 @@ This directive was first introduced in the v0.5.0rc32 release. '''NOTE''' Use of this directive is ''discouraged'' following the v0.9.17 release. Use the [[#log_by_lua_block|log_by_lua_block]] directive instead. -Runs the Lua source code inlined as the at the log request processing phase. This does not replace the current access logs, but runs before. +Similar to the [[#log_by_lua_block|log_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires +special character escaping). + +For instance, + + + log_by_lua ' + print("I need no extra escaping here, for example: \r\nblah") + '; + + +This directive was first introduced in the v0.5.0rc31 release. + +== log_by_lua_block == + +'''syntax:''' ''log_by_lua_block { lua-script }'' + +'''context:''' ''http, server, location, location if'' + +'''phase:''' ''log'' + +Runs the Lua source code inlined as the { lua-script } at the log request processing phase. This does not replace the current access logs, but runs before. Note that the following API functions are currently disabled within this context: @@ -2030,7 +2169,7 @@ Here is an example of gathering average data for [[HttpUpstreamModule#$upstream_ location / { proxy_pass http://mybackend; - log_by_lua ' + log_by_lua_block { local log_dict = ngx.shared.log_dict local upstream_time = tonumber(ngx.var.upstream_response_time) @@ -2043,7 +2182,7 @@ Here is an example of gathering average data for [[HttpUpstreamModule#$upstream_ log_dict:add("upstream_time-nb", 0) log_dict:incr("upstream_time-nb", 1) end - '; + } } location = /status { @@ -2063,29 +2202,6 @@ Here is an example of gathering average data for [[HttpUpstreamModule#$upstream_ } -This directive was first introduced in the v0.5.0rc31 release. - -== log_by_lua_block == - -'''syntax:''' ''log_by_lua_block { lua-script }'' - -'''context:''' ''http, server, location, location if'' - -'''phase:''' ''log'' - -Similar to the [[#log_by_lua|log_by_lua]] directive except that this directive inlines -the Lua source directly -inside a pair of curly braces ({}) instead of in an Nginx string literal (which requires -special character escaping). - -For instance, - - - log_by_lua_block { - print("I need no extra escaping here, for example: \r\nblah") - } - - This directive was first introduced in the v0.9.17 release. == log_by_lua_file == @@ -2096,7 +2212,7 @@ This directive was first introduced in the v0.9.17 release. '''phase:''' ''log'' -Equivalent to [[#log_by_lua|log_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +Equivalent to [[#log_by_lua_block|log_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. @@ -2158,19 +2274,39 @@ via the [[#ngx.ctx|ngx.ctx]] table. This directive was first introduced in the v0.10.0 release. -== balancer_by_lua_file == +== balancer_by_lua_file == + +'''syntax:''' ''balancer_by_lua_file '' + +'''context:''' ''upstream'' + +'''phase:''' ''content'' + +Equivalent to [[#balancer_by_lua_block|balancer_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. + +When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. + +This directive was first introduced in the v0.10.0 release. + +== balancer_keepalive == -'''syntax:''' ''balancer_by_lua_file '' +'''syntax:''' ''balancer_keepalive '' '''context:''' ''upstream'' -'''phase:''' ''content'' +'''phase:''' ''loading-config'' -Equivalent to [[#balancer_by_lua_block|balancer_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. +The total-connections parameter sets the maximum number of idle +keepalive connections to upstream servers that are preserved in the cache of +each worker process. When this number is exceeded, the least recently used +connections are closed. -When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. +It should be particularly noted that the keepalive directive does not limit the +total number of connections to upstream servers that an nginx worker process +can open. The connections parameter should be set to a number small enough to +let upstream servers process new incoming connections as well. -This directive was first introduced in the v0.10.0 release. +This directive was first introduced in the v0.10.21 release. == lua_need_request_body == @@ -2197,6 +2333,118 @@ It is recommended however, to use the [[#ngx.req.read_body|ngx.req.read_body]] a This also applies to [[#access_by_lua|access_by_lua*]]. +== ssl_client_hello_by_lua_block == + +'''syntax:''' ''ssl_client_hello_by_lua_block { lua-script }'' + +'''context:''' ''http, server'' + +'''phase:''' ''right-after-client-hello-message-was-processed'' + +This directive runs user Lua code when Nginx is about to post-process the SSL client hello message for the downstream +SSL (https) connections. + +It is particularly useful for dynamically setting the SSL protocols according to the SNI. + +It is also useful to do some custom operations according to the per-connection information in the client hello message. + +For example, one can parse custom client hello extension and do the corresponding handling in pure Lua. + +This Lua handler will always run whether the SSL session is resumed (via SSL session IDs or TLS session tickets) or not. +While the ssl_certificate_by_lua* Lua handler will only runs when initiating a full SSL handshake. + +The [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md ngx.ssl.clienthello] Lua modules +provided by the [https://github.com/openresty/lua-resty-core/#readme lua-resty-core] +library are particularly useful in this context. + +Note that this handler runs in extremely early stage of SSL handshake, before the SSL client hello extensions are parsed. +So you can not use some Lua API like ssl.server_name() which is dependent on the later stage's processing. + +Also note that only the directive in default server is valid for several virtual servers with the same IP address and port. + +Below is a trivial example using the +[https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md ngx.ssl.clienthello] module +at the same time: + + +server { + listen 443 ssl; + server_name test.com; + ssl_certificate /path/to/cert.crt; + ssl_certificate_key /path/to/key.key; + ssl_client_hello_by_lua_block { + local ssl_clt = require "ngx.ssl.clienthello" + local host, err = ssl_clt.get_client_hello_server_name() + if host == "test.com" then + ssl_clt.set_protocols({"TLSv1", "TLSv1.1"}) + elseif host == "test2.com" then + ssl_clt.set_protocols({"TLSv1.2", "TLSv1.3"}) + elseif not host then + ngx.log(ngx.ERR, "failed to get the SNI name: ", err) + ngx.exit(ngx.ERROR) + else + ngx.log(ngx.ERR, "unknown SNI name: ", host) + ngx.exit(ngx.ERROR) + end + } + ... +} +server { + listen 443 ssl; + server_name test2.com; + ssl_certificate /path/to/cert.crt; + ssl_certificate_key /path/to/key.key; + ... +} + + +See more information in the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md ngx.ssl.clienthello] +Lua modules' official documentation. + +Uncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the +[[#ngx.exit|ngx.exit]] call with an error code like ngx.ERROR. + +This Lua code execution context *does* support yielding, so Lua APIs that may yield +(like cosockets, sleeping, and "light threads") +are enabled in this context + +Note, you need to configure the [https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate ssl_certificate] +and [https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key ssl_certificate_key] +to avoid the following error while starting NGINX: + + + nginx: [emerg] no ssl configured for the server + + +This directive requires OpenSSL 1.1.1 or greater. + +If you are using the [official pre-built +packages](https://openresty.org/en/linux-packages.html) for +[OpenResty](https://openresty.org/) 1.21.4.1 or later, then everything should +work out of the box. + +If you are not using the Nginx core shipped with +[OpenResty](https://openresty.org) 1.21.4.1 or later, you will need to apply +patches to the standard Nginx core: + +https://openresty.org/en/nginx-ssl-patches.html + +This directive was first introduced in the v0.10.21 release. + +== ssl_client_hello_by_lua_file == + +'''syntax:''' ''ssl_client_hello_by_lua_file '' + +'''context:''' ''http, server'' + +'''phase:''' ''right-after-client-hello-message-was-processed'' + +Equivalent to [[#ssl_client_hello_by_lua_block|ssl_client_hello_by_lua_block]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed. + +When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. + +This directive was first introduced in the v0.10.21 release. + == ssl_certificate_by_lua_block == '''syntax:''' ''ssl_certificate_by_lua_block { lua-script }'' @@ -2278,14 +2526,7 @@ packages](https://openresty.org/en/linux-packages.html) for [OpenResty](https://openresty.org/) 1.9.7.2 or later, then everything should work out of the box. -If you are not using one of the [OpenSSL -packages](https://openresty.org/en/linux-packages.html) provided by -[OpenResty](https://openresty.org), you will need to apply patches to OpenSSL -in order to use this directive: - -https://openresty.org/en/openssl-patches.html - -Similarly, if you are not using the Nginx core shipped with +If you are not using the Nginx core shipped with [OpenResty](https://openresty.org) 1.9.7.2 or later, you will need to apply patches to the standard Nginx core: @@ -2624,11 +2865,43 @@ The support for the TLSv1.3 parameter requires version v0.10. This directive was first introduced in the v0.9.11 release. +== lua_ssl_certificate == + +'''syntax:''' ''lua_ssl_certificate '' + +'''default:''' ''none'' + +'''context:''' ''http, server, location'' + +Specifies the file path to the SSL/TLS certificate in PEM format used for the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method. + +This directive allows you to specify the SSL/TLS certificate that will be presented to server during the SSL/TLS handshake process. + +This directive was first introduced in the v0.10.26 release. + +See also [[#lua_ssl_certificate_key|lua_ssl_certificate_key]] and [[#lua_ssl_verify_depth|lua_ssl_verify_depth]]. + +== lua_ssl_certificate_key == + +'''syntax:''' ''lua_ssl_certificate_key '' + +'''default:''' ''none'' + +'''context:''' ''http, server, location'' + +Specifies the file path to the private key associated with the SSL/TLS certificate used in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method. + +This directive allows you to specify the private key file corresponding to the SSL/TLS certificate specified by lua_ssl_certificate. The private key should be in PEM format and must match the certificate. + +This directive was first introduced in the v0.10.26 release. + +See also [[#lua_ssl_certificate|lua_ssl_certificate]] and [[#lua_ssl_verify_depth|lua_ssl_verify_depth]]. + == lua_ssl_trusted_certificate == '''syntax:''' ''lua_ssl_trusted_certificate '' -'''default:''' ''no'' +'''default:''' ''none'' '''context:''' ''http, server, location'' @@ -2650,7 +2923,57 @@ Sets the verification depth in the server certificates chain. This directive was first introduced in the v0.9.11 release. -See also [[#lua_ssl_trusted_certificate|lua_ssl_trusted_certificate]]. +See also [[#lua_ssl_certificate|lua_ssl_certificate]], [[#lua_ssl_certificate_key|lua_ssl_certificate_key]] and [[#lua_ssl_trusted_certificate|lua_ssl_trusted_certificate]]. + +== lua_ssl_key_log == + +'''syntax:''' ''lua_ssl_key_log '' + +'''default:''' ''none'' + +'''context:''' ''http, server, location'' + +Enables logging of client connection SSL keys in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method and specifies the path to the key log file. Keys are logged in the SSLKEYLOGFILE format compatible with Wireshark. + +== lua_ssl_conf_command == + +'''syntax:''' ''lua_ssl_conf_command '' + +'''default:''' ''no'' + +'''context:''' ''http, server, location'' + +Sets arbitrary OpenSSL configuration [https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html commands]. + +The directive is supported when using OpenSSL 1.0.2 or higher and nginx 1.19.4 or higher. According to the specify command, higher OpenSSL version may be needed. + +Several lua_ssl_conf_command directives can be specified on the same level: + + +lua_ssl_conf_command Options PrioritizeChaCha; +lua_ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256; + + +Configuration commands are applied after OpenResty own configuration for SSL, so they can be used to override anything set by OpenResty. + +Note though that configuring OpenSSL directly with lua_ssl_conf_command might result in a behaviour OpenResty does not expect, and should be done with care. + +This directive was first introduced in the v0.10.21 release. + +== lua_upstream_skip_openssl_default_verify == + +'''syntax:''' ''lua_upstream_skip_openssl_default_verify on|off'' + +'''default:''' ''lua_upstream_skip_openssl_default_verify off'' + +'''context:''' ''location, location-if'' + +When using proxy_ssl_verify_by_lua directive, `lua_upstream_skip_openssl_default_verify` controls whether to skip default openssl's verify function, that means using pure Lua code to verify upstream server certificate. + +This directive is turned off by default. + +[Back to TOC](#directives) + == lua_http10_buffering == @@ -2702,7 +3025,7 @@ This directive was first introduced in the v0.9.20 release. '''context:''' ''http, server, location, location-if'' -Controls whether to transform underscores (_) in the response header names specified in the [[#ngx.header.HEADER|ngx.header.HEADER]] API to hypens (-). +Controls whether to transform underscores (_) in the response header names specified in the [[#ngx.header.HEADER|ngx.header.HEADER]] API to hyphens (-). This directive was first introduced in the v0.5.0rc32 release. @@ -2783,6 +3106,22 @@ This allows Lua I/O primitives to not be interrupted by Nginx's handling of vari This directive was first introduced in the v0.10.14 release. +== lua_worker_thread_vm_pool_size == + +'''syntax:''' ''lua_worker_thread_vm_pool_size '' + +'''default:''' ''lua_worker_thread_vm_pool_size 100'' + +'''context:''' ''http'' + +Specifies the size limit of the Lua VM pool (default 100) that will be used in the [ngx.run_worker_thread](#ngxrun_worker_thread) API. + +Also, it is not allowed to create Lua VMs that exceeds the pool size limit. + +The Lua VM in the VM pool is used to execute Lua code in separate thread. + +The pool is global at Nginx worker level. And it is used to reuse Lua VMs between requests. + = Nginx API for Lua = @@ -2908,7 +3247,7 @@ This API requires a relatively expensive metamethod call and it is recommended t == Core constants == -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, *log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, *log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' ngx.OK (0) @@ -2930,7 +3269,7 @@ The ngx.DECLINED constant was first introduced in the v0.5.0r == HTTP method constants == -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' ngx.HTTP_GET @@ -2954,7 +3293,7 @@ These constants are usually used in [[#ngx.location.capture|ngx.location.capture == HTTP status constants == -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release) @@ -2986,7 +3325,8 @@ These constants are usually used in [[#ngx.location.capture|ngx.location.capture value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release) value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release) value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) - value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) + value = ngx.HTTP_NOT_IMPLEMENTED (501) + value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) (kept for compatibility) value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release) value = ngx.HTTP_SERVICE_UNAVAILABLE (503) value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release) @@ -2996,7 +3336,7 @@ These constants are usually used in [[#ngx.location.capture|ngx.location.capture == Nginx log level constants == -'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' ngx.STDERR @@ -3016,7 +3356,7 @@ These constants are usually used by the [[#ngx.log|ngx.log]] method. '''syntax:''' ''print(...)'' -'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Writes argument values into the Nginx error.log file with the ngx.NOTICE log level. @@ -3093,7 +3433,7 @@ Then GET /main will give the output Here, modification of the ngx.ctx.blah entry in the subrequest does not affect the one in the parent request. This is because they have two separate versions of ngx.ctx.blah. -Internal redirection will destroy the original request ngx.ctx data (if any) and the new request will have an empty ngx.ctx table. For instance, +Internal redirects (triggered by nginx configuration directives like `error_page`, `try_files`, `index` and etc) will destroy the original request ngx.ctx data (if any) and the new request will have an empty ngx.ctx table. For instance, location /new { @@ -3119,10 +3459,10 @@ Then GET /orig will give rather than the original "hello" value. Because HTTP request is created after SSL handshake, the ngx.ctx created -in [[#ssl_certificate_by_lua|ssl_certificate_by_lua*]], [[#ssl_session_store_by_lua|ssl_session_store_by_lua*]] and [[#ssl_session_fetch_by_lua|ssl_session_fetch_by_lua*]] +in [[#ssl_certificate_by_lua|ssl_certificate_by_lua*]], [[#ssl_session_store_by_lua|ssl_session_store_by_lua*]], [[#ssl_session_fetch_by_lua|ssl_session_fetch_by_lua*]] and [[#ssl_client_hello_by_lua|ssl_client_hello_by_lua*]] is not available in the following phases like [[#rewrite_by_lua|rewrite_by_lua*]]. -Since dev, the ngx.ctx created during a SSL handshake +Since v0.10.18, the ngx.ctx created during a SSL handshake will be inherited by the requests which share the same TCP connection established by the handshake. Note that overwrite values in ngx.ctx in the http request phases (like `rewrite_by_lua*`) will only take affect in the current http request. @@ -3489,7 +3829,9 @@ Lua tables can be used for both requests and responses when the number of subreq table.insert(reqs, { "/memcached" }) -- issue all the requests at once and wait until they all return - local resps = { ngx.location.capture_multi(reqs) } + local resps = { + ngx.location.capture_multi(reqs) + } -- loop over the responses table for i, resp in ipairs(resps) do @@ -3915,11 +4257,12 @@ See also [[#ngx.req.set_uri|ngx.req.set_uri]]. == ngx.req.get_uri_args == -'''syntax:''' ''args, err = ngx.req.get_uri_args(max_args?)'' +'''syntax:''' ''args, err = ngx.req.get_uri_args(max_args?, tab?)'' '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*'' -Returns a Lua table holding all the current request URL query arguments. +Returns a Lua table holding all the current request URL query arguments. An optional tab argument +can be used to reuse the table returned by this method. location = /test { @@ -4194,6 +4537,8 @@ For security considerations, this method will automatically escape " ", """, "(" By default, all the subrequests subsequently initiated by [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] will inherit the new header. +It is not a Lua's equivalent of nginx `proxy_set_header` directive (same is true about [ngx.req.clear_header](#ngxreqclear_header)). `proxy_set_header` only affects the upstream request while `ngx.req.set_header` change the incoming request. Record the http headers in the access log file will show the difference. But you still can use it as an alternative of nginx `proxy_set_header` directive as long as you know the difference. + Here is an example of setting the Content-Type header: @@ -4285,12 +4630,14 @@ See also [[#ngx.req.read_body|ngx.req.read_body]]. == ngx.req.get_body_data == -'''syntax:''' ''data = ngx.req.get_body_data()'' +'''syntax:''' ''data = ngx.req.get_body_data(max_bytes?)'' '''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, log_by_lua*'' Retrieves in-memory request body data. It returns a Lua string rather than a Lua table holding all the parsed query arguments. Use the [[#ngx.req.get_post_args|ngx.req.get_post_args]] function instead if a Lua table is required. +The optional max_bytes function argument can be used when you don't need the entire body. + This function returns nil if # the request body has not been read, @@ -4333,7 +4680,7 @@ See also [[#ngx.req.get_body_data|ngx.req.get_body_data]]. '''syntax:''' ''ngx.req.set_body_data(data)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua*'' Set the current request's request body using the in-memory data specified by the data argument. @@ -4341,6 +4688,8 @@ If the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.rea Whether the previous request body has been read into memory or buffered into a disk file, it will be freed or the disk file will be cleaned up immediately, respectively. +Note that this function is also work for balancer phase but it needs to call [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md#recreate_request balancer.recreate_request] to make the change take effect after set the request body data or headers. + This function was first introduced in the v0.3.1rc18 release. See also [[#ngx.req.set_body_file|ngx.req.set_body_file]]. @@ -4349,7 +4698,7 @@ See also [[#ngx.req.set_body_file|ngx.req.set_body_file]]. '''syntax:''' ''ngx.req.set_body_file(file_name, auto_clean?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua*'' Set the current request's request body using the in-file data specified by the file_name argument. @@ -4361,6 +4710,8 @@ Please ensure that the file specified by the file_name argument exi Whether the previous request body has been read into memory or buffered into a disk file, it will be freed or the disk file will be cleaned up immediately, respectively. +Note that this function is also work for balancer phase but it needs to call [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md#recreate_request balancer.recreate_request] to make the change take effect after set the request body data or headers. + This function was first introduced in the v0.3.1rc18 release. See also [[#ngx.req.set_body_data|ngx.req.set_body_data]]. @@ -4371,7 +4722,7 @@ See also [[#ngx.req.set_body_data|ngx.req.set_body_data]]. '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*'' -Creates a new blank request body for the current request and inializes the buffer for later request body data writing via the [[#ngx.req.append_body|ngx.req.append_body]] and [[#ngx.req.finish_body|ngx.req.finish_body]] APIs. +Creates a new blank request body for the current request and initializes the buffer for later request body data writing via the [[#ngx.req.append_body|ngx.req.append_body]] and [[#ngx.req.finish_body|ngx.req.finish_body]] APIs. If the buffer_size argument is specified, then its value will be used for the size of the memory buffer for body writing with [[#ngx.req.append_body|ngx.req.append_body]]. If the argument is omitted, then the value specified by the standard [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]] directive will be used instead. @@ -4437,6 +4788,7 @@ Returns a read-only cosocket object that wraps the downstream connection. Only [ In case of error, nil will be returned as well as a string describing the error. +'''Note:''' This method will block while waiting for client request body to be fully received. Block time depends on the [http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_timeout client_body_timeout] directive and maximum body size specified by the [http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size client_max_body_size] directive. If read timeout occurs or client body size exceeds the defined limit, this function will not return and 408 Request Time-out or 413 Request Entity Too Large response will be returned to the client instead. The socket object returned by this method is usually used to read the current request's body in a streaming fashion. Do not turn on the [[#lua_need_request_body|lua_need_request_body]] directive, and do not mix this call with [[#ngx.req.read_body|ngx.req.read_body]] and [[#ngx.req.discard_body|ngx.req.discard_body]]. If any request body data has been pre-read into the Nginx core request header buffer, the resulting cosocket object will take care of this to avoid potential data loss resulting from such pre-reading. @@ -4664,7 +5016,7 @@ Just as [[#ngx.print|ngx.print]] but also emit a trailing newline. '''syntax:''' ''ngx.log(log_level, ...)'' -'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Log arguments concatenated to error.log with the given logging level. @@ -4696,7 +5048,7 @@ Since v0.8.3 this function returns 1 on success, or re '''syntax:''' ''ngx.exit(status)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' When status >= 200 (i.e., ngx.HTTP_OK and above), it will interrupt the execution of the current request and return status code to Nginx. @@ -4777,7 +5129,7 @@ Since v0.8.3 this function returns 1 on success, or re '''syntax:''' ''ngx.sleep(seconds)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Sleeps for the specified seconds without blocking. One can specify time resolution up to 0.001 seconds (i.e., one millisecond). @@ -4791,7 +5143,7 @@ This method was introduced in the 0.5.0rc30 release. '''syntax:''' ''newstr = ngx.escape_uri(str, type?)'' -'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Since `v0.10.16`, this function accepts an optional type argument. It accepts the following values (defaults to `2`): @@ -4807,7 +5159,7 @@ alphabetic characters, digits, -, ., _, '''syntax:''' ''newstr = ngx.unescape_uri(str)'' -'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Unescape str as an escaped URI component. @@ -4823,11 +5175,27 @@ gives the output b r56 7 +Invalid escaping sequences are handled in a conventional way: `%`s are left unchanged. Also, characters that should not appear in escaped string are simply left unchanged. + +For example, + + + ngx.say(ngx.unescape_uri("try %search%%20%again%")) + + +gives the output + + + try %search% %again% + + +(Note that `%20` following `%` got unescaped, even it can be considered a part of invalid sequence.) + == ngx.encode_args == '''syntax:''' ''str = ngx.encode_args(table)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_client_hello_by_lua*'' Encode the Lua table to a query args string according to the URI encoded rules. @@ -4879,7 +5247,7 @@ This method was first introduced in the v0.3.1rc27 release. '''syntax:''' ''table, err = ngx.decode_args(str, max_args?)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Decodes a URI encoded query-string into a Lua table. This is the inverse function of [[#ngx.encode_args|ngx.encode_args]]. @@ -4899,7 +5267,7 @@ This method was introduced in the v0.5.0rc29. '''syntax:''' ''newstr = ngx.encode_base64(str, no_padding?)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Encodes str to a base64 digest. @@ -4909,15 +5277,29 @@ Since the 0.9.16 release, an optional boolean-typed no_paddin '''syntax:''' ''newstr = ngx.decode_base64(str)'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' + +Decodes the str argument as a base64 digest to the raw form. +The str should be standard 'base64' encoding for RFC 3548 or RFC 4648, and will returns nil if is not well formed or any characters not in the base encoding alphabet. + +== ngx.decode_base64mime == +'''syntax:''' ''newstr = ngx.decode_base64mime(str)'' + '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' -Decodes the str argument as a base64 digest to the raw form. Returns nil if str is not well formed. +'''requires:''' resty.core.base64 or resty.core + +Decodes the str argument as a base64 digest to the raw form. +The str follows base64 transfer encoding for MIME (RFC 2045), and will discard characters outside the base encoding alphabet. +Returns nil if str is not well formed. + + '''Note:''' This method requires the resty.core.base64 or resty.core modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library. == ngx.crc32_short == '''syntax:''' ''intval = ngx.crc32_short(str)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Calculates the CRC-32 (Cyclic Redundancy Code) digest for the str argument. @@ -4931,7 +5313,7 @@ This API was first introduced in the v0.3.1rc8 release. '''syntax:''' ''intval = ngx.crc32_long(str)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Calculates the CRC-32 (Cyclic Redundancy Code) digest for the str argument. @@ -4945,7 +5327,7 @@ This API was first introduced in the v0.3.1rc8 release. '''syntax:''' ''digest = ngx.hmac_sha1(secret_key, str)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Computes the [https://en.wikipedia.org/wiki/HMAC HMAC-SHA1] digest of the argument str and turns the result using the secret key . @@ -4974,7 +5356,7 @@ This function was first introduced in the v0.3.1rc29 release. '''syntax:''' ''digest = ngx.md5(str)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Returns the hexadecimal representation of the MD5 digest of the str argument. @@ -4982,7 +5364,9 @@ For example, location = /md5 { - content_by_lua_block { ngx.say(ngx.md5("hello")) } + content_by_lua_block { + ngx.say(ngx.md5("hello")) + } } @@ -4998,7 +5382,7 @@ See [[#ngx.md5_bin|ngx.md5_bin]] if the raw binary MD5 digest is required. '''syntax:''' ''digest = ngx.md5_bin(str)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Returns the binary form of the MD5 digest of the str argument. @@ -5008,7 +5392,7 @@ See [[#ngx.md5|ngx.md5]] if the hexadecimal form of the MD5 digest is required. '''syntax:''' ''digest = ngx.sha1_bin(str)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Returns the binary form of the SHA-1 digest of the str argument. @@ -5020,7 +5404,7 @@ This function was first introduced in the v0.5.0rc6. '''syntax:''' ''quoted_value = ngx.quote_sql_str(raw_value)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Returns a quoted SQL string literal according to the MySQL quoting rules. @@ -5028,7 +5412,7 @@ Returns a quoted SQL string literal according to the MySQL quoting rules. '''syntax:''' ''str = ngx.today()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns current date (in the format yyyy-mm-dd) from the Nginx cached time (no syscall involved unlike Lua's date library). @@ -5038,7 +5422,7 @@ This is the local time. '''syntax:''' ''secs = ngx.time()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns the elapsed seconds from the epoch for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library). @@ -5048,7 +5432,7 @@ Updates of the Nginx time cache can be forced by calling [[#ngx.update_time|ngx. '''syntax:''' ''secs = ngx.now()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns a floating-point number for the elapsed time in seconds (including milliseconds as the decimal part) from the epoch for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library). @@ -5060,7 +5444,7 @@ This API was first introduced in v0.3.1rc32. '''syntax:''' ''ngx.update_time()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Forcibly updates the Nginx current time cache. This call involves a syscall and thus has some overhead, so do not abuse it. @@ -5070,7 +5454,7 @@ This API was first introduced in v0.3.1rc32. '''syntax:''' ''str = ngx.localtime()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns the current time stamp (in the format yyyy-mm-dd hh:mm:ss) of the Nginx cached time (no syscall involved unlike Lua's [https://www.lua.org/manual/5.1/manual.html#pdf-os.date os.date] function). @@ -5080,7 +5464,7 @@ This is the local time. '''syntax:''' ''str = ngx.utctime()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns the current time stamp (in the format yyyy-mm-dd hh:mm:ss) of the Nginx cached time (no syscall involved unlike Lua's [https://www.lua.org/manual/5.1/manual.html#pdf-os.date os.date] function). @@ -5090,7 +5474,7 @@ This is the UTC time. '''syntax:''' ''str = ngx.cookie_time(sec)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns a formatted string can be used as the cookie expiration time. The parameter sec is the time stamp in seconds (like those returned from [[#ngx.time|ngx.time]]). @@ -5103,7 +5487,7 @@ Returns a formatted string can be used as the cookie expiration time. The parame '''syntax:''' ''str = ngx.http_time(sec)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns a formated string can be used as the http header time (for example, being used in Last-Modified header). The parameter sec is the time stamp in seconds (like those returned from [[#ngx.time|ngx.time]]). @@ -5116,7 +5500,7 @@ Returns a formated string can be used as the http header time (for example, bein '''syntax:''' ''sec = ngx.parse_http_time(str)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Parse the http time string (as returned by [[#ngx.http_time|ngx.http_time]]) into seconds. Returns the seconds or nil if the input string is in bad forms. @@ -5139,7 +5523,7 @@ Returns true if the current request is an Nginx subrequest, or subject string using the Perl compatible regular expression regex with the optional options. @@ -5287,7 +5671,7 @@ This feature was introduced in the v0.2.1rc11 release. '''syntax:''' ''from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Similar to [[#ngx.re.match|ngx.re.match]] but only returns the beginning index (from) and end index (to) of the matched substring. The returned indexes are 1-based and can be fed directly into the [https://www.lua.org/manual/5.1/manual.html#pdf-string.sub string.sub] API function to obtain the matched substring. @@ -5337,7 +5721,7 @@ This API function was first introduced in the v0.9.2 release. '''syntax:''' ''iterator, err = ngx.re.gmatch(subject, regex, options?)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Similar to [[#ngx.re.match|ngx.re.match]], but returns a Lua iterator instead, so as to let the user programmer iterate all the matches over the string argument with the PCRE regex. @@ -5411,7 +5795,7 @@ This feature was first introduced in the v0.2.1rc12 release. '''syntax:''' ''newstr, n, err = ngx.re.sub(subject, regex, replace, options?)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Substitutes the first match of the Perl compatible regular expression regex on the subject argument string with the string or function argument replace. The optional options argument has exactly the same meaning as in [[#ngx.re.match|ngx.re.match]]. @@ -5472,7 +5856,7 @@ This feature was first introduced in the v0.2.1rc13 release. '''syntax:''' ''newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Just like [[#ngx.re.sub|ngx.re.sub]], but does global substitution. @@ -5508,7 +5892,7 @@ This feature was first introduced in the v0.2.1rc15 release. '''syntax:''' ''dict = ngx.shared[name_var]'' -'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Fetching the shm-based Lua dictionary object for the shared memory zone named DICT defined by the [[#lua_shared_dict|lua_shared_dict]] directive. @@ -5588,7 +5972,7 @@ This feature was first introduced in the v0.3.1rc22 release. '''syntax:''' ''value, flags = ngx.shared.DICT:get(key)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Retrieving the value in the dictionary [[#ngx.shared.DICT|ngx.shared.DICT]] for the key key. If the key does not exist or has expired, then nil will be returned. @@ -5622,7 +6006,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''value, flags, stale = ngx.shared.DICT:get_stale(key)'' -'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Similar to the [[#ngx.shared.DICT.get|get]] method but returns the value even if the key has already expired. @@ -5638,7 +6022,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Unconditionally sets a key-value pair into the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. Returns three values: @@ -5686,7 +6070,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''ok, err = ngx.shared.DICT:safe_set(key, value, exptime?, flags?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Similar to the [[#ngx.shared.DICT.set|set]] method, but never overrides the (least recently used) unexpired items in the store when running out of storage in the shared memory zone. In this case, it will immediately return nil and the string "no memory". @@ -5698,7 +6082,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''success, err, forcible = ngx.shared.DICT:add(key, value, exptime?, flags?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Just like the [[#ngx.shared.DICT.set|set]] method, but only stores the key-value pair into the dictionary [[#ngx.shared.DICT|ngx.shared.DICT]] if the key does ''not'' exist. @@ -5712,7 +6096,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''ok, err = ngx.shared.DICT:safe_add(key, value, exptime?, flags?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Similar to the [[#ngx.shared.DICT.add|add]] method, but never overrides the (least recently used) unexpired items in the store when running out of storage in the shared memory zone. In this case, it will immediately return nil and the string "no memory". @@ -5724,7 +6108,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Just like the [[#ngx.shared.DICT.set|set]] method, but only stores the key-value pair into the dictionary [[#ngx.shared.DICT|ngx.shared.DICT]] if the key ''does'' exist. @@ -5738,7 +6122,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''ngx.shared.DICT:delete(key)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Unconditionally removes the key-value pair from the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. @@ -5752,7 +6136,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''newval, err, forcible? = ngx.shared.DICT:incr(key, value, init?, init_ttl?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' '''optional requirement:''' resty.core.shdict or resty.core @@ -5803,7 +6187,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''length, err = ngx.shared.DICT:lpush(key, value)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Inserts the specified (numerical or string) value at the head of the list named key in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. Returns the number of elements in the list after the push operation. @@ -5819,7 +6203,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''length, err = ngx.shared.DICT:rpush(key, value)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Similar to the [[#ngx.shared.DICT.lpush|lpush]] method, but inserts the specified (numerical or string) value at the tail of the list named key. @@ -5831,7 +6215,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''val, err = ngx.shared.DICT:lpop(key)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Removes and returns the first element of the list named key in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. @@ -5845,7 +6229,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''val, err = ngx.shared.DICT:rpop(key)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Removes and returns the last element of the list named key in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. @@ -5859,7 +6243,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''len, err = ngx.shared.DICT:llen(key)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Returns the number of elements in the list named key in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. @@ -5873,7 +6257,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''ttl, err = ngx.shared.DICT:ttl(key)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' '''requires:''' resty.core.shdict or resty.core @@ -5907,7 +6291,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''success, err = ngx.shared.DICT:expire(key, exptime)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' '''requires:''' resty.core.shdict or resty.core @@ -5943,7 +6327,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''ngx.shared.DICT:flush_all()'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Flushes out all the items in the dictionary. This method does not actually free up all the memory blocks in the dictionary but just marks all the existing items as expired. @@ -5955,7 +6339,7 @@ See also [[#ngx.shared.DICT.flush_expired|ngx.shared.DICT.flush_expired]] and [[ '''syntax:''' ''flushed = ngx.shared.DICT:flush_expired(max_count?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Flushes out the expired items in the dictionary, up to the maximal number specified by the optional max_count argument. When the max_count argument is given 0 or not given at all, then it means unlimited. Returns the number of items that have actually been flushed. @@ -5969,7 +6353,7 @@ See also [[#ngx.shared.DICT.flush_all|ngx.shared.DICT.flush_all]] and [[#ngx.sha '''syntax:''' ''keys = ngx.shared.DICT:get_keys(max_count?)'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Fetch a list of the keys from the dictionary, up to . @@ -5983,7 +6367,7 @@ This feature was first introduced in the v0.7.3 release. '''syntax:''' ''capacity_bytes = ngx.shared.DICT:capacity()'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' '''requires:''' resty.core.shdict or resty.core @@ -6011,7 +6395,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''free_page_bytes = ngx.shared.DICT:free_space()'' -'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' '''requires:''' resty.core.shdict or resty.core @@ -6050,7 +6434,7 @@ See also [[#ngx.shared.DICT|ngx.shared.DICT]]. '''syntax:''' ''udpsock = ngx.socket.udp()'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Creates and returns a UDP or datagram-oriented unix domain socket object (also known as one type of the "cosocket" objects). The following methods are supported on this object: @@ -6072,7 +6456,7 @@ See also [[#ngx.socket.tcp|ngx.socket.tcp]]. '''syntax:''' ''ok, err = udpsock:setpeername("unix:/path/to/unix-domain.socket")'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Attempts to connect a UDP socket object to a remote server or to a datagram unix domain socket file. Because the datagram protocol is actually connection-less, this method does not really establish a "connection", but only just set the name of the remote peer for subsequent read/write operations. @@ -6129,7 +6513,7 @@ This method was first introduced in the v0.5.7 release. '''syntax:''' ''ok, err = udpsock:send(data)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Sends data on the current UDP or datagram unix domain socket object. @@ -6143,7 +6527,7 @@ This feature was first introduced in the v0.5.7 release. '''syntax:''' ''data, err = udpsock:receive(size?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Receives data from the UDP or datagram unix domain socket object with an optional receive buffer size argument, size. @@ -6175,7 +6559,7 @@ This feature was first introduced in the v0.5.7 release. '''syntax:''' ''ok, err = udpsock:close()'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Closes the current UDP or datagram unix domain socket. It returns the 1 in case of success and returns nil with a string describing the error otherwise. @@ -6187,7 +6571,7 @@ This feature was first introduced in the v0.5.7 release. '''syntax:''' ''udpsock:settimeout(time)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Set the timeout value in milliseconds for subsequent socket operations (like [[#udpsock:receive|receive]]). @@ -6206,11 +6590,13 @@ This API function was first added to the v0.10.1 release. '''syntax:''' ''tcpsock = ngx.socket.tcp()'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Creates and returns a TCP or stream-oriented unix domain socket object (also known as one type of the "cosocket" objects). The following methods are supported on this object: +* [[#tcpsock:bind|bind]] * [[#tcpsock:connect|connect]] +* [[#tcpsock:setclientcert|setclientcert]] * [[#tcpsock:sslhandshake|sslhandshake]] * [[#tcpsock:send|send]] * [[#tcpsock:receive|receive]] @@ -6246,13 +6632,45 @@ This feature was first introduced in the v0.5.0rc1 release. See also [[#ngx.socket.udp|ngx.socket.udp]]. +== tcpsock:bind == +'''syntax:''' ''ok, err = tcpsock:bind(address)'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*'' + +Just like the standard [[HttpProxyModule#proxy_bind|proxy_bind]] directive, this api makes the outgoing connection to a upstream server originate from the specified local IP address. + +Only IP addresses can be specified as the address argument. + +Here is an example for connecting to a TCP server from the specified local IP address: + + + location /test { + content_by_lua_block { + local sock = ngx.socket.tcp() + -- assume "192.168.1.10" is the local ip address + local ok, err = sock:bind("192.168.1.10") + if not ok then + ngx.say("failed to bind") + return + end + local ok, err = sock:connect("192.168.1.67", 80) + if not ok then + ngx.say("failed to connect server: ", err) + return + end + ngx.say("successfully connected!") + sock:close() + } + } + + == tcpsock:connect == '''syntax:''' ''ok, err = tcpsock:connect(host, port, options_table?)'' '''syntax:''' ''ok, err = tcpsock:connect("unix:/path/to/unix-domain.socket", options_table?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Attempts to connect a TCP socket object to a remote server or to a stream unix domain socket file without blocking. @@ -6361,11 +6779,33 @@ The support for the options table argument was first introduced in the v0. This method was first introduced in the v0.5.0rc1 release. +== tcpsock:setclientcert == + +'''syntax:''' ''ok, err = tcpsock:setclientcert(cert, pkey)'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' + +Set client certificate chain and corresponding private key to the TCP socket object. +The certificate chain and private key provided will be used later by the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +* cert specify a client certificate chain cdata object that will be used while handshaking with +remote server. These objects can be created using [ngx.ssl.parse\_pem\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert) +function provided by lua-resty-core. Note that specifying the cert option requires +corresponding pkey be provided too. See below. +* pkey specify a private key corresponds to the cert option above. +These objects can be created using [ngx.ssl.parse\_pem\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key) +function provided by lua-resty-core. + +If both of cert and pkey are nil, this method will clear any existing client certificate and private key +that was previously set on the cosocket object. + +This method was first introduced in the `v0.10.22` release. + == tcpsock:sslhandshake == '''syntax:''' ''session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Does SSL/TLS handshake on the currently established connection. @@ -6408,7 +6848,7 @@ This method was first introduced in the v0.9.11 release. '''syntax:''' ''bytes, err = tcpsock:send(data)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Sends data without blocking on the current TCP or Unix Domain Socket connection. @@ -6437,7 +6877,7 @@ This feature was first introduced in the v0.5.0rc1 release. '''syntax:''' ''data, err, partial = tcpsock:receive(pattern?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Receives data from the connected socket according to the reading pattern or size. @@ -6476,7 +6916,7 @@ This feature was first introduced in the v0.5.0rc1 release. '''syntax:''' ''data, err = tcpsock:receiveany(max)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Returns any data received by the connected socket, at most max bytes. @@ -6507,7 +6947,7 @@ This feature was first introduced in the v0.10.14 release. '''syntax:''' ''iterator = tcpsock:receiveuntil(pattern, options?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' This method returns an iterator Lua function that can be called to read the data stream until it sees the specified pattern or an error occurs. @@ -6600,7 +7040,7 @@ This method was first introduced in the v0.5.0rc1 release. '''syntax:''' ''ok, err = tcpsock:close()'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Closes the current TCP or stream unix domain socket. It returns the 1 in case of success and returns nil with a string describing the error otherwise. @@ -6614,7 +7054,7 @@ This feature was first introduced in the v0.5.0rc1 release. '''syntax:''' ''tcpsock:settimeout(time)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Set the timeout value in milliseconds for subsequent socket operations ([[#tcpsock:connect|connect]], [[#tcpsock:receive|receive]], and iterators returned from [[#tcpsock:receiveuntil|receiveuntil]]). @@ -6628,7 +7068,7 @@ This feature was first introduced in the v0.5.0rc1 release. '''syntax:''' ''tcpsock:settimeouts(connect_timeout, send_timeout, read_timeout)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Respectively sets the connect, send, and read timeout thresholds (in milliseconds) for subsequent socket operations ([[#tcpsock:connect|connect]], [[#tcpsock:send|send]], [[#tcpsock:receive|receive]], and iterators returned from [[#tcpsock:receiveuntil|receiveuntil]]). @@ -6645,7 +7085,7 @@ This feature was first introduced in the v0.10.7 release. '''syntax:''' ''ok, err = tcpsock:setoption(option, value?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' This function is added for [http://w3.impa.br/~diego/software/luasocket/tcp.html LuaSocket] API compatibility and does nothing for now. Its functionality is implemented v0.10.18. @@ -6739,7 +7179,7 @@ These options described above are supported in v0.10.18, and more o '''syntax:''' ''ok, err = tcpsock:setkeepalive(timeout?, size?)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Puts the current socket's connection immediately into the cosocket built-in connection pool and keep it alive until other [[#tcpsock:connect|connect]] method calls request it or the associated maximal idle timeout is expired. @@ -6783,7 +7223,7 @@ This feature was first introduced in the v0.5.0rc1 release. '''syntax:''' ''count, err = tcpsock:getreusedtimes()'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' This method returns the (successfully) reused times for the current connection. In case of error, it returns nil and a string describing the error. @@ -6818,7 +7258,7 @@ This feature was first introduced in the v0.5.0rc1 release. '''syntax:''' ''str = ngx.get_phase()'' -'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Retrieves the current running phase name. Possible return values are @@ -6832,6 +7272,8 @@ Retrieves the current running phase name. Possible return values are : for the context of [[#ssl_session_fetch_by_lua_block|ssl_session_fetch_by_lua*]]. * ssl_session_store : for the context of [[#ssl_session_store_by_lua_block|ssl_session_store_by_lua*]]. +* ssl_client_hello +: for the context of [[#ssl_client_hello_by_lua_block|ssl_client_hello_by_lua*]]. * set : for the context of [[#set_by_lua|set_by_lua*]]. * rewrite @@ -6859,7 +7301,7 @@ This API was first introduced in the v0.5.10 release. '''syntax:''' ''co = ngx.thread.spawn(func, arg1, arg2, ...)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Spawns a new user "light thread" with the Lua function func as well as those optional arguments arg1, arg2, and etc. Returns a Lua thread (or Lua coroutine) object represents this "light thread". @@ -6993,7 +7435,7 @@ This API was first enabled in the v0.7.0 release. '''syntax:''' ''ok, res1, res2, ... = ngx.thread.wait(thread1, thread2, ...)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Waits on one or more child "light threads" and returns the results of the first "light thread" that terminates (either successfully or with an error). @@ -7092,7 +7534,7 @@ This API was first enabled in the v0.7.0 release. '''syntax:''' ''ok, err = ngx.thread.kill(thread)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua*'' Kills a running "light thread" created by [[#ngx.thread.spawn|ngx.thread.spawn]]. Returns a true value when successful or nil and a string describing the error otherwise. @@ -7141,7 +7583,7 @@ See also [[#lua_check_client_abort|lua_check_client_abort]]. '''syntax:''' ''hdl, err = ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Creates an Nginx timer with a user callback function as well as optional user arguments. @@ -7264,15 +7706,22 @@ user "light threads" ([[#ngx.thread.spawn|ngx.thread.*]]), [[#ngx.exit|ngx.exit] (like [[#ngx.say|ngx.say]], [[#ngx.print|ngx.print]], and [[#ngx.flush|ngx.flush]]) are explicitly disabled in this context. +You must notice that each timer will be based on a fake request (this fake request is also based on a fake connection). Because Nginx's memory release is based on the connection closure, if you run a lot of APIs that apply for memory resources in a timer, such as [[#tcpsock:connect|tcpsock:connect]], will cause the accumulation of memory resources. So it is recommended to create a new timer after running several times to release memory resources. + You can pass most of the standard Lua values (nils, booleans, numbers, strings, tables, closures, file handles, and etc) into the timer callback, either explicitly as user arguments or implicitly as upvalues for the callback closure. There are several exceptions, however: you ''cannot'' pass any thread objects returned by [[#coroutine.create|coroutine.create]] and [[#ngx.thread.spawn|ngx.thread.spawn]] or any cosocket objects returned by [[#ngx.socket.tcp|ngx.socket.tcp]], [[#ngx.socket.udp|ngx.socket.udp]], and [[#ngx.req.socket|ngx.req.socket]] because these objects' lifetime is bound to the request context creating them while the timer callback is detached from the creating request's context (by design) and runs in its own (fake) request context. If you try to share the thread or cosocket objects across the boundary of the creating request, then you will get the "no co ctx found" error (for threads) or "bad request" (for cosockets). It is fine, however, to create all these objects inside your timer callback. +Please note that the timer Lua handler has its own copy of the `ngx.ctx` magic +table. It won't share the same `ngx.ctx` with the Lua handler creating the timer. +If you need to pass data from the timer creator to the timer handler, please +use the extra parameters of `ngx.timer.at()`. + This API was first introduced in the v0.8.0 release. == ngx.timer.every == '''syntax:''' ''hdl, err = ngx.timer.every(delay, callback, user_arg1, user_arg2, ...)'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Similar to the [[#ngx.timer.at|ngx.timer.at]] API function, but @@ -7292,7 +7741,7 @@ This API was first introduced in the v0.10.9 release. '''syntax:''' ''count = ngx.timer.running_count()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns the number of timers currently running. @@ -7302,7 +7751,7 @@ This directive was first introduced in the v0.9.20 release. '''syntax:''' ''count = ngx.timer.pending_count()'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' Returns the number of pending timers. @@ -7389,6 +7838,13 @@ This function returns a Lua number for the process ID (PID) of the current Nginx This API was first introduced in the 0.9.5 release. +== ngx.worker.pids == + +'''syntax:''' ''pid = ngx.worker.pids()'' + +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, exit_worker_by_lua*'' + +This function returns a Lua table for all Nginx worker process ID (PID). Nginx uses channel to send the current worker PID to another worker in the worker process start or restart. So this API can get all current worker PID. == ngx.worker.count == '''syntax:''' ''count = ngx.worker.count()'' @@ -7495,7 +7951,7 @@ This feature requires at least ngx_lua v0.10.0. '''syntax:''' ''res = ndk.set_var.DIRECTIVE_NAME'' -'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' +'''context:''' ''init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*'' This mechanism allows calling other Nginx C modules' directives that are implemented by [https://github.com/simplresty/ngx_devel_kit Nginx Devel Kit] (NDK)'s set_var submodule's ndk_set_var_value. @@ -7533,7 +7989,7 @@ This feature requires the [https://github.com/simplresty/ngx_devel_kit ngx_devel '''syntax:''' ''co = coroutine.create(f)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Creates a user Lua coroutines with a Lua function, and returns a coroutine object. @@ -7547,9 +8003,9 @@ This API was first introduced in the v0.6.0 release. '''syntax:''' ''ok, ... = coroutine.resume(co, ...)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' -Resumes the executation of a user Lua coroutine object previously yielded or just created. +Resumes the execution of a user Lua coroutine object previously yielded or just created. Similar to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.resume coroutine.resume] API, but works in the context of the Lua coroutines created by ngx_lua. @@ -7561,7 +8017,7 @@ This API was first introduced in the v0.6.0 release. '''syntax:''' ''... = coroutine.yield(...)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Yields the execution of the current user Lua coroutine. @@ -7575,7 +8031,7 @@ This API was first introduced in the v0.6.0 release. '''syntax:''' ''co = coroutine.wrap(f)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Similar to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.wrap coroutine.wrap] API, but works in the context of the Lua coroutines created by ngx_lua. @@ -7587,7 +8043,7 @@ This API was first introduced in the v0.6.0 release. '''syntax:''' ''co = coroutine.running()'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Identical to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.running coroutine.running] API. @@ -7599,7 +8055,7 @@ This API was first enabled in the v0.6.0 release. '''syntax:''' ''status = coroutine.status(co)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, init_by_lua*, ngx.timer.*, header_filter_by_lua*, body_filter_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' Identical to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.status coroutine.status] API. @@ -7607,6 +8063,133 @@ This API was first usable in the context of [[#init_by_lua|init_by_lua*]] since This API was first enabled in the v0.6.0 release. +== ngx.run_worker_thread == + +'''syntax:''' ''ok, res1, res2, ... = ngx.run_worker_thread(threadpool, module_name, func_name, arg1, arg2, ...)'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' + +'''This API is still experimental and may change in the future without notice.''' + +'''This API is available only for Linux.''' + +Wrap the [http://nginx.org/en/docs/dev/development_guide.html#threads nginx worker thread] to execute lua function. The caller coroutine would yield until the function returns. + +Only the following ngx_lua APIs could be used in `function_name` function of the `module` module: + +* `ngx.encode_base64` +* `ngx.decode_base64` + +* `ngx.hmac_sha1` +* `ngx.encode_args` +* `ngx.decode_args` +* `ngx.quote_sql_str` + +* `ngx.re.match` +* `ngx.re.find` +* `ngx.re.gmatch` +* `ngx.re.sub` +* `ngx.re.gsub` + +* `ngx.crc32_short` +* `ngx.crc32_long` +* `ngx.hmac_sha1` +* `ngx.md5_bin` +* `ngx.md5` + +* `ngx.config.subsystem` +* `ngx.config.debug` +* `ngx.config.prefix` +* `ngx.config.nginx_version` +* `ngx.config.nginx_configure` +* `ngx.config.ngx_lua_version` + + +The first argument `threadpool` specifies the Nginx thread pool name defined by [thread_pool](https://nginx.org/en/docs/ngx_core_module.html#thread_pool). + +The second argument module_name specifies the lua module name to execute in the worker thread, which would return a lua table. The module must be inside the package path, e.g. + + +lua_package_path '/opt/openresty/?.lua;;'; + + +The third argument func_name specifies the function field in the module table as the second argument. + +The type of args must be one of type below: + +* boolean +* number +* string +* nil +* table (the table may be recursive, and contains members of types above.) + +The ok is in boolean type, which indicate the C land error (failed to get thread from thread pool, pcall the module function failed, .etc). If ok is false, the res1 is the error string. + +The return values (res1, ...) are returned by invocation of the module function. Normally, the res1 should be in boolean type, so that the caller could inspect the error. + +This API is useful when you need to execute the below types of tasks: + +* CPU bound task, e.g. do md5 calculation +* File I/O task +* Call os.execute() or blocking C API via ffi +* Call external Lua library not based on cosocket or nginx + +Example1: do md5 calculation. + + +location /calc_md5 { + default_type 'text/plain'; + + content_by_lua_block { + local ok, md5_or_err = ngx.run_worker_thread("testpool", "md5", "md5") + ngx.say(ok, " : ", md5_or_err) + } +} + + + +md5.lua + + +local function md5() + return ngx.md5("hello") +end +return {md5=md5} + + +Example2: write logs into the log file. + + +location /write_log_file { + default_type 'text/plain'; + + content_by_lua_block { + local ok, err = ngx.run_worker_thread("testpool", "write_log_file", "log", ngx.var.arg_str) + if not ok then + ngx.say(ok, " : ", err) + return + end + ngx.say(ok) + } +} + + +write_log_file.lua + + +local function log(str) + local file, err = io.open("/tmp/tmp.log", "a") + if not file then + return false, err + end + file:write(str) + file:flush() + file:close() + return true +end +return {log=log} + + = Obsolete Sections = This section is just holding obsolete documentation sections that have been either renamed or removed so that existing links over the web are still valid. diff --git a/doc/images/lua_nginx_modules_directives.drawio.png b/doc/images/lua_nginx_modules_directives.drawio.png new file mode 100644 index 0000000000..41112147f2 Binary files /dev/null and b/doc/images/lua_nginx_modules_directives.drawio.png differ diff --git a/src/api/ngx_http_lua_api.h b/src/api/ngx_http_lua_api.h index 8a86ef0593..51a9e7b714 100644 --- a/src/api/ngx_http_lua_api.h +++ b/src/api/ngx_http_lua_api.h @@ -19,7 +19,11 @@ /* Public API for other Nginx modules */ -#define ngx_http_lua_version 10019 +#define ngx_http_lua_version 10029 +#define NGX_HTTP_LUA_EXPORT_CO_CTX_CLEANUP 1 + + +typedef struct ngx_http_lua_co_ctx_s ngx_http_lua_co_ctx_t; typedef struct { @@ -56,6 +60,24 @@ ngx_shm_zone_t *ngx_http_lua_find_zone(u_char *name_data, size_t name_len); ngx_shm_zone_t *ngx_http_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag); +ngx_http_lua_co_ctx_t *ngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r); + +void ngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r, + ngx_http_lua_co_ctx_t *coctx); + +lua_State *ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx); + + +void *ngx_http_lua_get_co_ctx_data(ngx_http_lua_co_ctx_t *coctx); +void ngx_http_lua_set_co_ctx_cleanup(ngx_http_lua_co_ctx_t *coctx, + ngx_http_cleanup_pt cleanup, void *data); +void ngx_http_lua_cleanup_co_ctx_pending_operation( + ngx_http_lua_co_ctx_t *coctx); + +void ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets); + +int ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r); + #endif /* _NGX_HTTP_LUA_API_H_INCLUDED_ */ diff --git a/src/ngx_http_lua_accessby.c b/src/ngx_http_lua_accessby.c index 58c251443e..39bdcda2bf 100644 --- a/src/ngx_http_lua_accessby.c +++ b/src/ngx_http_lua_accessby.c @@ -95,6 +95,7 @@ ngx_http_lua_access_handler(ngx_http_request_t *r) dd("entered? %d", (int) ctx->entered_access_phase); + if (ctx->entered_access_phase) { dd("calling wev handler"); rc = ctx->resume_handler(r); @@ -105,7 +106,9 @@ ngx_http_lua_access_handler(ngx_http_request_t *r) } if (rc == NGX_OK) { - if (r->header_sent) { + if (r->header_sent + || (r->headers_out.status != 0 && ctx->out != NULL)) + { dd("header already sent"); /* response header was already generated in access_by_lua*, @@ -170,6 +173,10 @@ ngx_http_lua_access_handler_inline(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->access_src_ref = LUA_REFNIL; + } + /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, llcf->access_src.value.data, @@ -211,6 +218,10 @@ ngx_http_lua_access_handler_file(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->access_src_ref = LUA_REFNIL; + } + /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, &llcf->access_src_ref, @@ -240,7 +251,7 @@ ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r) ngx_event_t *rev; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; @@ -291,9 +302,9 @@ ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r) /* }}} */ - /* {{{ register request cleanup hooks */ + /* {{{ register nginx pool cleanup hooks */ if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -369,7 +380,8 @@ ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r) #if 1 if (rc == NGX_OK) { - if (r->header_sent) { + if (r->header_sent || (r->headers_out.status != 0 && ctx->out != NULL)) + { dd("header already sent"); /* response header was already generated in access_by_lua*, diff --git a/src/ngx_http_lua_api.c b/src/ngx_http_lua_api.c index b69d627dce..2478f0da80 100644 --- a/src/ngx_http_lua_api.c +++ b/src/ngx_http_lua_api.c @@ -213,4 +213,155 @@ ngx_http_lua_shared_memory_init(ngx_shm_zone_t *shm_zone, void *data) return NGX_OK; } + +ngx_http_lua_co_ctx_t * +ngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r) +{ + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + return ctx == NULL ? NULL : ctx->cur_co_ctx; +} + + +void +ngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r, ngx_http_lua_co_ctx_t *coctx) +{ + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + coctx->data = r; + + ctx->cur_co_ctx = coctx; +} + + +lua_State * +ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx) +{ + return coctx->co; +} + + +void * +ngx_http_lua_get_co_ctx_data(ngx_http_lua_co_ctx_t *coctx) +{ + return coctx ? coctx->data : NULL; +} + + +void +ngx_http_lua_set_co_ctx_cleanup(ngx_http_lua_co_ctx_t *coctx, + ngx_http_cleanup_pt cleanup, void *data) +{ + coctx->cleanup = cleanup; + coctx->data = data; +} + + +void +ngx_http_lua_cleanup_co_ctx_pending_operation(ngx_http_lua_co_ctx_t *coctx) +{ + ngx_http_lua_cleanup_pending_operation(coctx); +} + + +static ngx_int_t +ngx_http_lua_co_ctx_resume(ngx_http_request_t *r) +{ + lua_State *vm; + ngx_connection_t *c; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_http_lua_wev_handler; + + c = r->connection; + vm = ngx_http_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_http_lua_run_thread(vm, r, ctx, ctx->cur_co_ctx->nrets); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +void +ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_log_ctx_t *log_ctx; + + r = coctx->data; + c = r->connection; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + return; + } + + if (c->fd != (ngx_socket_t) -1) { /* not a fake connection */ + log_ctx = c->log->data; + log_ctx->current_request = r; + } + + coctx->nrets = nrets; + coctx->cleanup = NULL; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua coctx resume handler: \"%V?%V\"", &r->uri, &r->args); + + ctx->cur_co_ctx = coctx; + + if (ctx->entered_content_phase) { + (void) ngx_http_lua_co_ctx_resume(r); + + } else { + ctx->resume_handler = ngx_http_lua_co_ctx_resume; + ngx_http_core_run_phases(r); + } + + ngx_http_run_posted_requests(c); +} + + +int +ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r) +{ + ngx_http_lua_loc_conf_t *llcf; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + return llcf->http10_buffering; +} + + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_balancer.c b/src/ngx_http_lua_balancer.c index e4ac57a301..aa26a4a07a 100644 --- a/src/ngx_http_lua_balancer.c +++ b/src/ngx_http_lua_balancer.c @@ -15,10 +15,35 @@ #include "ngx_http_lua_util.h" #include "ngx_http_lua_directive.h" +#define NGX_BALANCER_DEF_HOST_LEN 32 +typedef struct { + ngx_queue_t queue; + ngx_queue_t hnode; + ngx_uint_t hash; + ngx_http_lua_srv_conf_t *lscf; + ngx_connection_t *connection; + socklen_t socklen; + ngx_sockaddr_t sockaddr; + ngx_sockaddr_t local_sockaddr; + ngx_str_t host; + /* try to avoid allocating memory from the connection pool */ + u_char host_data[NGX_BALANCER_DEF_HOST_LEN]; +} ngx_http_lua_balancer_ka_item_t; /*balancer keepalive item*/ + struct ngx_http_lua_balancer_peer_data_s { - /* the round robin data must be first */ - ngx_http_upstream_rr_peer_data_t rrp; + ngx_uint_t keepalive_requests; + ngx_msec_t keepalive_timeout; + + void *data; + + ngx_event_get_peer_pt original_get_peer; + ngx_event_free_peer_pt original_free_peer; + +#if (NGX_HTTP_SSL) + ngx_event_set_peer_session_pt original_set_session; + ngx_event_save_peer_session_pt original_save_session; +#endif ngx_http_lua_srv_conf_t *conf; ngx_http_request_t *request; @@ -28,15 +53,18 @@ struct ngx_http_lua_balancer_peer_data_s { struct sockaddr *sockaddr; socklen_t socklen; + ngx_addr_t *local; - ngx_str_t *host; - in_port_t port; + ngx_str_t host; + ngx_str_t *addr_text; int last_peer_state; #if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS) - unsigned cloned_upstream_conf; /* :1 */ + unsigned cloned_upstream_conf:1; #endif + + unsigned keepalive:1; }; @@ -45,6 +73,9 @@ static ngx_int_t ngx_http_lua_balancer_set_session(ngx_peer_connection_t *pc, void *data); static void ngx_http_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data); +static ngx_int_t +ngx_http_lua_upstream_get_ssl_name(ngx_http_request_t *r, + ngx_http_upstream_t *u); #endif static ngx_int_t ngx_http_lua_balancer_init(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us); @@ -56,6 +87,18 @@ static ngx_int_t ngx_http_lua_balancer_by_chunk(lua_State *L, ngx_http_request_t *r); static void ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data, ngx_uint_t state); +static void ngx_http_lua_balancer_notify_peer(ngx_peer_connection_t *pc, + void *data, ngx_uint_t type); +static void ngx_http_lua_balancer_close(ngx_connection_t *c); +static void ngx_http_lua_balancer_dummy_handler(ngx_event_t *ev); +static void ngx_http_lua_balancer_close_handler(ngx_event_t *ev); +static ngx_connection_t *ngx_http_lua_balancer_get_cached_item( + ngx_http_lua_srv_conf_t *lscf, ngx_peer_connection_t *pc, ngx_str_t *name); +static ngx_uint_t ngx_http_lua_balancer_calc_hash(ngx_str_t *name, + struct sockaddr *sockaddr, socklen_t socklen, ngx_addr_t *local); + + +static struct sockaddr *ngx_http_lua_balancer_default_server_sockaddr; ngx_int_t @@ -90,7 +133,7 @@ ngx_http_lua_balancer_handler_inline(ngx_http_request_t *r, lscf->balancer.src.len, &lscf->balancer.src_ref, lscf->balancer.src_key, - "=balancer_by_lua"); + (const char *) lscf->balancer.chunkname); if (rc != NGX_OK) { return rc; } @@ -125,12 +168,16 @@ char * ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + size_t chunkname_len; + u_char *chunkname; u_char *cache_key = NULL; u_char *name; ngx_str_t *value; ngx_http_lua_srv_conf_t *lscf = conf; + ngx_url_t url; ngx_http_upstream_srv_conf_t *uscf; + ngx_http_upstream_server_t *us; dd("enter"); @@ -172,17 +219,56 @@ ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_ERROR; } + chunkname = ngx_http_lua_gen_chunk_name(cf, "balancer_by_lua", + sizeof("balancer_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + /* Don't eval nginx variables for inline lua code */ lscf->balancer.src = value[1]; + lscf->balancer.chunkname = chunkname; } lscf->balancer.src_key = cache_key; + /* balancer setup */ + uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); + if (uscf->servers->nelts == 0) { + us = ngx_array_push(uscf->servers); + if (us == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); + ngx_memzero(&url, sizeof(ngx_url_t)); + + ngx_str_set(&url.url, "0.0.0.1"); + url.default_port = 80; + + if (ngx_parse_url(cf->pool, &url) != NGX_OK) { + return NGX_CONF_ERROR; + } + + us->name = url.url; + us->addrs = url.addrs; + us->naddrs = url.naddrs; + + ngx_http_lua_balancer_default_server_sockaddr = us->addrs[0].sockaddr; + } + if (uscf->peer.init_upstream) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "load balancing method redefined"); + + lscf->balancer.original_init_upstream = uscf->peer.init_upstream; + + } else { + lscf->balancer.original_init_upstream = + ngx_http_upstream_init_round_robin; } uscf->peer.init_upstream = ngx_http_lua_balancer_init; @@ -198,16 +284,58 @@ ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, static ngx_int_t -ngx_http_lua_balancer_init(ngx_conf_t *cf, - ngx_http_upstream_srv_conf_t *us) +ngx_http_lua_balancer_init(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) { - if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) { + ngx_uint_t i; + ngx_uint_t bucket_cnt; + ngx_queue_t *buckets; + ngx_http_lua_srv_conf_t *lscf; + ngx_http_lua_balancer_ka_item_t *cached; + + lscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module); + + ngx_conf_init_uint_value(lscf->balancer.max_cached, 32); + + if (lscf->balancer.original_init_upstream(cf, us) != NGX_OK) { return NGX_ERROR; } - /* this callback is called upon individual requests */ + lscf->balancer.original_init_peer = us->peer.init; + us->peer.init = ngx_http_lua_balancer_init_peer; + /* allocate cache items and add to free queue */ + + cached = ngx_pcalloc(cf->pool, + sizeof(ngx_http_lua_balancer_ka_item_t) + * lscf->balancer.max_cached); + if (cached == NULL) { + return NGX_ERROR; + } + + ngx_queue_init(&lscf->balancer.cache); + ngx_queue_init(&lscf->balancer.free); + + for (i = 0; i < lscf->balancer.max_cached; i++) { + ngx_queue_insert_head(&lscf->balancer.free, &cached[i].queue); + cached[i].lscf = lscf; + } + + bucket_cnt = lscf->balancer.max_cached / 2; + bucket_cnt = bucket_cnt > 0 ? bucket_cnt : 1; + buckets = ngx_pcalloc(cf->pool, sizeof(ngx_queue_t) * bucket_cnt); + + if (buckets == NULL) { + return NGX_ERROR; + } + + for (i = 0; i < bucket_cnt; i++) { + ngx_queue_init(&buckets[i]); + } + + lscf->balancer.buckets = buckets; + lscf->balancer.bucket_cnt = bucket_cnt; + return NGX_OK; } @@ -216,33 +344,39 @@ static ngx_int_t ngx_http_lua_balancer_init_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) { - ngx_http_lua_srv_conf_t *bcf; + ngx_http_lua_srv_conf_t *lscf; ngx_http_lua_balancer_peer_data_t *bp; - bp = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_balancer_peer_data_t)); - if (bp == NULL) { + lscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module); + + if (lscf->balancer.original_init_peer(r, us) != NGX_OK) { return NGX_ERROR; } - r->upstream->peer.data = &bp->rrp; - - if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) { + bp = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_balancer_peer_data_t)); + if (bp == NULL) { return NGX_ERROR; } + bp->conf = lscf; + bp->request = r; + bp->data = r->upstream->peer.data; + bp->original_get_peer = r->upstream->peer.get; + bp->original_free_peer = r->upstream->peer.free; + + r->upstream->peer.data = bp; r->upstream->peer.get = ngx_http_lua_balancer_get_peer; r->upstream->peer.free = ngx_http_lua_balancer_free_peer; + r->upstream->peer.notify = ngx_http_lua_balancer_notify_peer; #if (NGX_HTTP_SSL) + bp->original_set_session = r->upstream->peer.set_session; + bp->original_save_session = r->upstream->peer.save_session; + r->upstream->peer.set_session = ngx_http_lua_balancer_set_session; r->upstream->peer.save_session = ngx_http_lua_balancer_save_session; #endif - bcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module); - - bp->conf = bcf; - bp->request = r; - return NGX_OK; } @@ -250,25 +384,30 @@ ngx_http_lua_balancer_init_peer(ngx_http_request_t *r, static ngx_int_t ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data) { + void *pdata; lua_State *L; ngx_int_t rc; + ngx_connection_t *c; ngx_http_request_t *r; +#if (NGX_HTTP_SSL) + ngx_http_upstream_t *u; +#endif ngx_http_lua_ctx_t *ctx; ngx_http_lua_srv_conf_t *lscf; - ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_balancer_peer_data_t *bp = data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "lua balancer peer, tries: %ui", pc->tries); - - lscf = bp->conf; + "lua balancer: get peer, tries: %ui", pc->tries); r = bp->request; +#if (NGX_HTTP_SSL) + u = r->upstream; +#endif + lscf = bp->conf; ngx_http_lua_assert(lscf->balancer.handler && r); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { @@ -289,18 +428,18 @@ ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data) bp->sockaddr = NULL; bp->socklen = 0; bp->more_tries = 0; + bp->keepalive_requests = 0; + bp->keepalive_timeout = 0; + bp->keepalive = 0; bp->total_tries++; - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - - /* balancer_by_lua does not support yielding and - * there cannot be any conflicts among concurrent requests, - * thus it is safe to store the peer data in the main conf. - */ - lmcf->balancer_peer_data = bp; + pdata = r->upstream->peer.data; + r->upstream->peer.data = bp; rc = lscf->balancer.handler(r, lscf, L); + r->upstream->peer.data = pdata; + if (rc == NGX_ERROR) { return NGX_ERROR; } @@ -322,25 +461,60 @@ ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data) } } + if (bp->local != NULL) { + pc->local = bp->local; + } + if (bp->sockaddr && bp->socklen) { pc->sockaddr = bp->sockaddr; pc->socklen = bp->socklen; + pc->name = bp->addr_text; pc->cached = 0; pc->connection = NULL; - pc->name = bp->host; - - bp->rrp.peers->single = 0; if (bp->more_tries) { r->upstream->peer.tries += bp->more_tries; } - dd("tries: %d", (int) r->upstream->peer.tries); + if (bp->keepalive) { +#if (NGX_HTTP_SSL) + if (bp->host.len == 0 && u->ssl) { + ngx_http_lua_upstream_get_ssl_name(r, u); + bp->host = u->ssl_name; + } +#endif + + c = ngx_http_lua_balancer_get_cached_item(lscf, pc, &bp->host); + + if (c) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua balancer: keepalive reusing connection %p," + " host: %V, name: %V", + c, bp->addr_text, &bp->host); + return NGX_DONE; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua balancer: keepalive no free connection, " + "host: %V, name: %v", bp->addr_text, &bp->host); + } return NGX_OK; } - return ngx_http_upstream_get_round_robin_peer(pc, &bp->rrp); + rc = bp->original_get_peer(pc, bp->data); + if (rc == NGX_ERROR) { + return rc; + } + + if (pc->sockaddr == ngx_http_lua_balancer_default_server_sockaddr) { + ngx_log_error(NGX_LOG_ERR, pc->log, 0, + "lua balancer: no peer set"); + + return NGX_ERROR; + } + + return rc; } @@ -403,10 +577,20 @@ static void ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data, ngx_uint_t state) { - ngx_http_lua_balancer_peer_data_t *bp = data; + ngx_uint_t hash; + ngx_str_t *host; + ngx_queue_t *q; + ngx_connection_t *c; + ngx_http_upstream_t *u; + ngx_http_lua_balancer_ka_item_t *item; + ngx_http_lua_balancer_peer_data_t *bp = data; + ngx_http_lua_srv_conf_t *lscf = bp->conf; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "lua balancer free peer, tries: %ui", pc->tries); + "lua balancer: free peer, tries: %ui", pc->tries); + + u = bp->request->upstream; + c = pc->connection; if (bp->sockaddr && bp->socklen) { bp->last_peer_state = (int) state; @@ -415,12 +599,229 @@ ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data, pc->tries--; } + if (bp->keepalive) { + if (state & NGX_PEER_FAILED + || c == NULL + || c->read->eof + || c->read->error + || c->read->timedout + || c->write->error + || c->write->timedout) + { + goto invalid; + } + + if (bp->keepalive_requests + && c->requests >= bp->keepalive_requests) + { + goto invalid; + } + + if (!u->keepalive) { + goto invalid; + } + + if (!u->request_body_sent) { + goto invalid; + } + + if (ngx_terminate || ngx_exiting) { + goto invalid; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto invalid; + } + + if (ngx_queue_empty(&lscf->balancer.free)) { + q = ngx_queue_last(&lscf->balancer.cache); + + item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t, + queue); + ngx_queue_remove(q); + ngx_queue_remove(&item->hnode); + + ngx_http_lua_balancer_close(item->connection); + + } else { + q = ngx_queue_head(&lscf->balancer.free); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t, + queue); + } + + host = &bp->host; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua balancer: keepalive saving connection %p, " + "host: %V, name: %V", + c, bp->addr_text, host); + + ngx_queue_insert_head(&lscf->balancer.cache, q); + hash = ngx_http_lua_balancer_calc_hash(host, + bp->sockaddr, bp->socklen, + bp->local); + item->hash = hash; + hash %= lscf->balancer.bucket_cnt; + ngx_queue_insert_head(&lscf->balancer.buckets[hash], &item->hnode); + item->connection = c; + pc->connection = NULL; + + c->read->delayed = 0; + ngx_add_timer(c->read, bp->keepalive_timeout); + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + c->write->handler = ngx_http_lua_balancer_dummy_handler; + c->read->handler = ngx_http_lua_balancer_close_handler; + + c->data = item; + c->idle = 1; + c->log = ngx_cycle->log; + c->read->log = ngx_cycle->log; + c->write->log = ngx_cycle->log; + c->pool->log = ngx_cycle->log; + + item->socklen = pc->socklen; + ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); + if (pc->local) { + ngx_memcpy(&item->local_sockaddr, + pc->local->sockaddr, pc->local->socklen); + + } else { + ngx_memzero(&item->local_sockaddr, + sizeof(item->local_sockaddr)); + } + + if (host->data && host->len) { + if (host->len <= sizeof(item->host_data)) { + ngx_memcpy(item->host_data, host->data, host->len); + item->host.data = item->host_data; + item->host.len = host->len; + + } else { + item->host.data = ngx_pstrdup(c->pool, host); + if (item->host.data == NULL) { + ngx_http_lua_balancer_close(c); + + ngx_queue_remove(&item->queue); + ngx_queue_remove(&item->hnode); + ngx_queue_insert_head(&item->lscf->balancer.free, + &item->queue); + return; + } + + item->host.len = host->len; + } + + } else { + ngx_str_null(&item->host); + } + + if (c->read->ready) { + ngx_http_lua_balancer_close_handler(c->read); + } + + return; + +invalid: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua balancer: keepalive not saving connection %p", + c); + } + + return; + } + + bp->original_free_peer(pc, bp->data, state); +} + + +static void +ngx_http_lua_balancer_notify_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t type) +{ +#ifdef NGX_HTTP_UPSTREAM_NOTIFY_CACHED_CONNECTION_ERROR + if (type == NGX_HTTP_UPSTREAM_NOTIFY_CACHED_CONNECTION_ERROR) { + pc->tries--; + } +#endif +} + + +static void +ngx_http_lua_balancer_close(ngx_connection_t *c) +{ +#if (NGX_HTTP_SSL) + if (c->ssl) { + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; + + if (ngx_ssl_shutdown(c) == NGX_AGAIN) { + c->ssl->handler = ngx_http_lua_balancer_close; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua balancer: keepalive shutdown " + "connection %p failed", c); + return; + } + } +#endif + + ngx_destroy_pool(c->pool); + ngx_close_connection(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua balancer: keepalive closing connection %p", c); +} + + +static void +ngx_http_lua_balancer_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua balancer: dummy handler"); +} + + +static void +ngx_http_lua_balancer_close_handler(ngx_event_t *ev) +{ + ngx_http_lua_balancer_ka_item_t *item; + + int n; + char buf[1]; + ngx_connection_t *c; + + c = ev->data; + if (c->close || c->read->timedout) { + goto close; + } + + n = recv(c->fd, buf, 1, MSG_PEEK); + + if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + ev->ready = 0; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto close; + } + return; } - /* fallback */ +close: - ngx_http_upstream_free_round_robin_peer(pc, data, state); + item = c->data; + c->log = ev->log; + + ngx_http_lua_balancer_close(c); + + ngx_queue_remove(&item->queue); + ngx_queue_remove(&item->hnode); + ngx_queue_insert_head(&item->lscf->balancer.free, &item->queue); } @@ -436,7 +837,7 @@ ngx_http_lua_balancer_set_session(ngx_peer_connection_t *pc, void *data) return NGX_OK; } - return ngx_http_upstream_set_round_robin_peer_session(pc, &bp->rrp); + return bp->original_set_session(pc, bp->data); } @@ -450,8 +851,7 @@ ngx_http_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data) return; } - ngx_http_upstream_save_round_robin_peer_session(pc, &bp->rrp); - return; + bp->original_save_session(pc, bp->data); } #endif @@ -459,13 +859,14 @@ ngx_http_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data) int ngx_http_lua_ffi_balancer_set_current_peer(ngx_http_request_t *r, - const u_char *addr, size_t addr_len, int port, char **err) + const u_char *addr, size_t addr_len, int port, + const u_char *host, size_t host_len, + char **err) { ngx_url_t url; ngx_http_lua_ctx_t *ctx; ngx_http_upstream_t *u; - ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_balancer_peer_data_t *bp; if (r == NULL) { @@ -491,18 +892,6 @@ ngx_http_lua_ffi_balancer_set_current_peer(ngx_http_request_t *r, return NGX_ERROR; } - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - - /* we cannot read r->upstream->peer.data here directly because - * it could be overridden by other modules like - * ngx_http_upstream_keepalive_module. - */ - bp = lmcf->balancer_peer_data; - if (bp == NULL) { - *err = "no upstream peer data found"; - return NGX_ERROR; - } - ngx_memzero(&url, sizeof(ngx_url_t)); url.url.data = ngx_palloc(r->pool, addr_len); @@ -526,34 +915,116 @@ ngx_http_lua_ffi_balancer_set_current_peer(ngx_http_request_t *r, return NGX_ERROR; } + bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data; + if (url.addrs && url.addrs[0].sockaddr) { bp->sockaddr = url.addrs[0].sockaddr; bp->socklen = url.addrs[0].socklen; - bp->host = &url.addrs[0].name; + bp->addr_text = &url.addrs[0].name; } else { *err = "no host allowed"; return NGX_ERROR; } + if (host && host_len) { + bp->host.data = ngx_palloc(r->pool, host_len); + if (bp->host.data == NULL) { + *err = "no memory"; + return NGX_ERROR; + } + + ngx_memcpy(bp->host.data, host, host_len); + bp->host.len = host_len; + +#if (NGX_HTTP_SSL) + if (u->ssl) { + u->ssl_name = bp->host; + } +#endif + + } else { + ngx_str_null(&bp->host); + } + return NGX_OK; } int -ngx_http_lua_ffi_balancer_set_timeouts(ngx_http_request_t *r, - long connect_timeout, long send_timeout, long read_timeout, - char **err) +ngx_http_lua_ffi_balancer_bind_to_local_addr(ngx_http_request_t *r, + const u_char *addr, size_t addr_len, + u_char *errbuf, size_t *errbuf_size) { + u_char *p; ngx_http_lua_ctx_t *ctx; ngx_http_upstream_t *u; + ngx_int_t rc; -#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS) - ngx_http_upstream_conf_t *ucf; -#endif - ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_balancer_peer_data_t *bp; + if (r == NULL) { + p = ngx_snprintf(errbuf, *errbuf_size, "no request found"); + *errbuf_size = p - errbuf; + return NGX_ERROR; + } + + u = r->upstream; + + if (u == NULL) { + p = ngx_snprintf(errbuf, *errbuf_size, "no upstream found"); + *errbuf_size = p - errbuf; + return NGX_ERROR; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + p = ngx_snprintf(errbuf, *errbuf_size, "no ctx found"); + *errbuf_size = p - errbuf; + return NGX_ERROR; + } + + if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) { + p = ngx_snprintf(errbuf, *errbuf_size, + "API disabled in the current context"); + *errbuf_size = p - errbuf; + return NGX_ERROR; + } + + bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data; + + if (bp->local == NULL) { + bp->local = ngx_palloc(r->pool, sizeof(ngx_addr_t) + addr_len); + if (bp->local == NULL) { + p = ngx_snprintf(errbuf, *errbuf_size, "no memory"); + *errbuf_size = p - errbuf; + return NGX_ERROR; + } + } + + rc = ngx_parse_addr_port(r->pool, bp->local, (u_char *) addr, addr_len); + if (rc == NGX_ERROR) { + p = ngx_snprintf(errbuf, *errbuf_size, "invalid addr %s", addr); + *errbuf_size = p - errbuf; + return NGX_ERROR; + } + + bp->local->name.len = addr_len; + bp->local->name.data = (u_char *) (bp->local + 1); + ngx_memcpy(bp->local->name.data, addr, addr_len); + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_balancer_enable_keepalive(ngx_http_request_t *r, + unsigned long timeout, unsigned int max_requests, char **err) +{ + ngx_http_upstream_t *u; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_balancer_peer_data_t *bp; + if (r == NULL) { *err = "no request found"; return NGX_ERROR; @@ -577,15 +1048,60 @@ ngx_http_lua_ffi_balancer_set_timeouts(ngx_http_request_t *r, return NGX_ERROR; } - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data; - bp = lmcf->balancer_peer_data; - if (bp == NULL) { - *err = "no upstream peer data found"; + if (!(bp->sockaddr && bp->socklen)) { + *err = "no current peer set"; return NGX_ERROR; } + bp->keepalive_timeout = (ngx_msec_t) timeout; + bp->keepalive_requests = (ngx_uint_t) max_requests; + bp->keepalive = 1; + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_balancer_set_timeouts(ngx_http_request_t *r, + long connect_timeout, long send_timeout, long read_timeout, + char **err) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_upstream_t *u; + #if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS) + ngx_http_upstream_conf_t *ucf; + ngx_http_lua_balancer_peer_data_t *bp; +#endif + + if (r == NULL) { + *err = "no request found"; + return NGX_ERROR; + } + + u = r->upstream; + + if (u == NULL) { + *err = "no upstream found"; + return NGX_ERROR; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + *err = "no ctx found"; + return NGX_ERROR; + } + + if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + +#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS) + bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data; + if (!bp->cloned_upstream_conf) { /* we clone the upstream conf for the current request so that * we do not affect other requests at all. */ @@ -640,12 +1156,10 @@ ngx_http_lua_ffi_balancer_set_more_tries(ngx_http_request_t *r, int count, char **err) { #if (nginx_version >= 1007005) - ngx_uint_t max_tries, total; + ngx_uint_t max_tries, total; #endif - ngx_http_lua_ctx_t *ctx; - ngx_http_upstream_t *u; - - ngx_http_lua_main_conf_t *lmcf; + ngx_http_lua_ctx_t *ctx; + ngx_http_upstream_t *u; ngx_http_lua_balancer_peer_data_t *bp; if (r == NULL) { @@ -671,13 +1185,7 @@ ngx_http_lua_ffi_balancer_set_more_tries(ngx_http_request_t *r, return NGX_ERROR; } - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - - bp = lmcf->balancer_peer_data; - if (bp == NULL) { - *err = "no upstream peer data found"; - return NGX_ERROR; - } + bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data; #if (nginx_version >= 1007005) max_tries = r->upstream->conf->next_upstream_tries; @@ -703,12 +1211,10 @@ int ngx_http_lua_ffi_balancer_get_last_failure(ngx_http_request_t *r, int *status, char **err) { - ngx_http_lua_ctx_t *ctx; - ngx_http_upstream_t *u; - ngx_http_upstream_state_t *state; - + ngx_http_lua_ctx_t *ctx; + ngx_http_upstream_t *u; + ngx_http_upstream_state_t *state; ngx_http_lua_balancer_peer_data_t *bp; - ngx_http_lua_main_conf_t *lmcf; if (r == NULL) { *err = "no request found"; @@ -733,13 +1239,7 @@ ngx_http_lua_ffi_balancer_get_last_failure(ngx_http_request_t *r, return NGX_ERROR; } - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - - bp = lmcf->balancer_peer_data; - if (bp == NULL) { - *err = "no upstream peer data found"; - return NGX_ERROR; - } + bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data; if (r->upstream_states && r->upstream_states->nelts > 1) { state = r->upstream_states->elts; @@ -792,11 +1292,221 @@ ngx_http_lua_ffi_balancer_recreate_request(ngx_http_request_t *r, /* u->request_bufs already contains a valid request buffer * remove it from chain first */ - u->request_bufs = u->request_bufs->next; + u->request_bufs = r->request_body->bufs; } return u->create_request(r); } +int +ngx_http_lua_ffi_balancer_set_upstream_tls(ngx_http_request_t *r, int on, + char **err) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_upstream_t *u; + + if (r == NULL) { + *err = "no request found"; + return NGX_ERROR; + } + + u = r->upstream; + + if (u == NULL) { + *err = "no upstream found"; + return NGX_ERROR; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + *err = "no ctx found"; + return NGX_ERROR; + } + + if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) { + *err = "API disabled in the current context"; + return NGX_ERROR; + } + + if (on == 0) { + u->ssl = 0; + u->schema.len = sizeof("http://") - 1; + + } else { + u->ssl = 1; + u->schema.len = sizeof("https://") - 1; + } + + return NGX_OK; +} + + +char * +ngx_http_lua_balancer_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_int_t n; + ngx_str_t *value; + +#if 0 + ngx_http_upstream_srv_conf_t *uscf; +#endif + ngx_http_lua_srv_conf_t *lscf = conf; + + if (lscf->balancer.max_cached != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + /* read options */ + + value = cf->args->elts; + + n = ngx_atoi(value[1].data, value[1].len); + + if (n == NGX_ERROR || n == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\" in \"%V\" directive", + &value[1], &cmd->name); + return NGX_CONF_ERROR; + } + + lscf->balancer.max_cached = n; + + return NGX_CONF_OK; +} + + +#if (NGX_HTTP_SSL) +static ngx_int_t +ngx_http_lua_upstream_get_ssl_name(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + u_char *p, *last; + ngx_str_t name; + + if (u->conf->ssl_name) { + if (ngx_http_complex_value(r, u->conf->ssl_name, &name) != NGX_OK) { + return NGX_ERROR; + } + + } else { + name = u->ssl_name; + } + + if (name.len == 0) { + goto done; + } + + /* + * ssl name here may contain port, notably if derived from $proxy_host + * or $http_host; we have to strip it. eg: www.example.com:443 + */ + + p = name.data; + last = name.data + name.len; + + if (*p == '[') { + p = ngx_strlchr(p, last, ']'); + + if (p == NULL) { + p = name.data; + } + } + + p = ngx_strlchr(p, last, ':'); + + if (p != NULL) { + name.len = p - name.data; + } + +done: + + u->ssl_name = name; + + return NGX_OK; +} +#endif + + +static ngx_uint_t +ngx_http_lua_balancer_calc_hash(ngx_str_t *name, + struct sockaddr *sockaddr, socklen_t socklen, ngx_addr_t *local) +{ + ngx_uint_t hash; + + hash = ngx_hash_key_lc(name->data, name->len); + hash ^= ngx_hash_key((u_char *) sockaddr, socklen); + if (local != NULL) { + hash ^= ngx_hash_key((u_char *) local->sockaddr, local->socklen); + } + + return hash; +} + + +static ngx_connection_t * +ngx_http_lua_balancer_get_cached_item(ngx_http_lua_srv_conf_t *lscf, + ngx_peer_connection_t *pc, ngx_str_t *name) +{ + ngx_uint_t hash; + ngx_queue_t *q; + ngx_queue_t *head; + ngx_connection_t *c; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_addr_t *local; + ngx_http_lua_balancer_ka_item_t *item; + + sockaddr = pc->sockaddr; + socklen = pc->socklen; + local = pc->local; + + hash = ngx_http_lua_balancer_calc_hash(name, sockaddr, socklen, pc->local); + head = &lscf->balancer.buckets[hash % lscf->balancer.bucket_cnt]; + + c = NULL; + for (q = ngx_queue_head(head); + q != ngx_queue_sentinel(head); + q = ngx_queue_next(q)) + { + item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t, hnode); + if (item->hash != hash) { + continue; + } + + if (name->len == item->host.len + && ngx_memn2cmp((u_char *) &item->sockaddr, + (u_char *) sockaddr, + item->socklen, socklen) == 0 + && ngx_strncasecmp(name->data, + item->host.data, name->len) == 0 + && (local == NULL + || ngx_memn2cmp((u_char *) &item->local_sockaddr, + (u_char *) local->sockaddr, + socklen, local->socklen) == 0)) + { + c = item->connection; + ngx_queue_remove(q); + ngx_queue_remove(&item->queue); + ngx_queue_insert_head(&lscf->balancer.free, &item->queue); + c->idle = 0; + c->sent = 0; + c->log = pc->log; + c->read->log = pc->log; + c->write->log = pc->log; + c->pool->log = pc->log; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + pc->cached = 1; + pc->connection = c; + return c; + } + } + + return NULL; +} + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_balancer.h b/src/ngx_http_lua_balancer.h index bb49dc02ca..34db2fe82f 100644 --- a/src/ngx_http_lua_balancer.h +++ b/src/ngx_http_lua_balancer.h @@ -23,5 +23,7 @@ char *ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, char *ngx_http_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_http_lua_balancer_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); #endif /* _NGX_HTTP_LUA_BALANCER_H_INCLUDED_ */ diff --git a/src/ngx_http_lua_bodyfilterby.c b/src/ngx_http_lua_bodyfilterby.c index 75608695e1..cb5f7eb608 100644 --- a/src/ngx_http_lua_bodyfilterby.c +++ b/src/ngx_http_lua_bodyfilterby.c @@ -155,13 +155,18 @@ ngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->body_filter_src_ref = LUA_REFNIL; + } + /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, llcf->body_filter_src.value.data, llcf->body_filter_src.value.len, &llcf->body_filter_src_ref, llcf->body_filter_src_key, - "=body_filter_by_lua"); + (const char *) + llcf->body_filter_chunkname); if (rc != NGX_OK) { return NGX_ERROR; } @@ -205,6 +210,10 @@ ngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->body_filter_src_ref = LUA_REFNIL; + } + /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, &llcf->body_filter_src_ref, @@ -233,20 +242,17 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_http_lua_ctx_t *ctx; ngx_int_t rc; uint16_t old_context; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ngx_chain_t *out; + ngx_chain_t *cl, *ln; ngx_http_lua_main_conf_t *lmcf; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua body filter for user lua code, uri \"%V\"", &r->uri); - if (in == NULL) { - return ngx_http_next_body_filter(r, in); - } - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (llcf->body_filter_handler == NULL) { + if (llcf->body_filter_handler == NULL || r->header_only) { dd("no body filter handler found"); return ngx_http_next_body_filter(r, in); } @@ -269,11 +275,54 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in) in->buf->file_pos = in->buf->file_last; } - return NGX_OK; + in = NULL; + + /* continue to call ngx_http_next_body_filter to process cached data */ + } + + if (in != NULL + && ngx_chain_add_copy(r->pool, &ctx->filter_in_bufs, in) != NGX_OK) + { + return NGX_ERROR; + } + + if (ctx->filter_busy_bufs != NULL + && (r->connection->buffered + & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED))) + { + /* Socket write buffer was full on last write. + * Try to write the remain data, if still can not write + * do not execute body_filter_by_lua otherwise the `in` chain will be + * replaced by new content from lua and buf of `in` mark as consumed. + * And then ngx_output_chain will call the filter chain again which + * make all the data cached in the memory and long ngx_chain_t link + * cause CPU 100%. + */ + rc = ngx_http_next_body_filter(r, NULL); + + if (rc == NGX_ERROR) { + return rc; + } + + out = NULL; + ngx_chain_update_chains(r->pool, + &ctx->free_bufs, &ctx->filter_busy_bufs, &out, + (ngx_buf_tag_t) &ngx_http_lua_body_filter); + if (rc != NGX_OK + && ctx->filter_busy_bufs != NULL + && (r->connection->buffered + & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED))) + { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "waiting body filter busy buffer to be sent"); + return NGX_AGAIN; + } + + /* continue to process bufs in ctx->filter_in_bufs */ } if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_ERROR; } @@ -286,39 +335,59 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in) old_context = ctx->context; ctx->context = NGX_HTTP_LUA_CONTEXT_BODY_FILTER; - dd("calling body filter handler"); - rc = llcf->body_filter_handler(r, in); + in = ctx->filter_in_bufs; + ctx->filter_in_bufs = NULL; - dd("calling body filter handler returned %d", (int) rc); + if (in != NULL) { + dd("calling body filter handler"); + rc = llcf->body_filter_handler(r, in); - ctx->context = old_context; + dd("calling body filter handler returned %d", (int) rc); - if (rc != NGX_OK) { - return NGX_ERROR; - } + ctx->context = old_context; - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - out = lmcf->body_filter_chain; + if (rc != NGX_OK) { + return NGX_ERROR; + } - if (in == out) { - return ngx_http_next_body_filter(r, in); - } + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + + /* lmcf->body_filter_chain is the new buffer chain if + * body_filter_by_lua set new body content via ngx.arg[1] = new_content + * otherwise it is the original `in` buffer chain. + */ + out = lmcf->body_filter_chain; + + if (in != out) { + /* content of body was replaced in + * ngx_http_lua_body_filter_param_set and the buffers was marked + * as consumed. + */ + for (cl = in; cl != NULL; cl = ln) { + ln = cl->next; + ngx_free_chain(r->pool, cl); + } - if (out == NULL) { - /* do not forward NULL to the next filters because the input is - * not NULL */ - return NGX_OK; + if (out == NULL) { + /* do not forward NULL to the next filters because the input is + * not NULL */ + return NGX_OK; + } + } + + } else { + ctx->context = old_context; + out = NULL; } - /* in != out */ rc = ngx_http_next_body_filter(r, out); if (rc == NGX_ERROR) { return NGX_ERROR; } ngx_chain_update_chains(r->pool, - &ctx->free_bufs, &ctx->busy_bufs, &out, - (ngx_buf_tag_t) &ngx_http_lua_module); + &ctx->free_bufs, &ctx->filter_busy_bufs, &out, + (ngx_buf_tag_t) &ngx_http_lua_body_filter); return rc; } @@ -336,51 +405,48 @@ ngx_http_lua_body_filter_init(void) int -ngx_http_lua_body_filter_param_get(lua_State *L, ngx_http_request_t *r) +ngx_http_lua_ffi_get_body_filter_param_eof(ngx_http_request_t *r) { - u_char *data, *p; - size_t size; ngx_chain_t *cl; - ngx_buf_t *b; - int idx; ngx_chain_t *in; ngx_http_lua_main_conf_t *lmcf; - idx = luaL_checkint(L, 2); + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + in = lmcf->body_filter_chain; - dd("index: %d", idx); + /* asking for the eof argument */ - if (idx != 1 && idx != 2) { - lua_pushnil(L); - return 1; + for (cl = in; cl; cl = cl->next) { + if (cl->buf->last_buf || cl->buf->last_in_chain) { + return 1; + } } - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - in = lmcf->body_filter_chain; + return 0; +} - if (idx == 2) { - /* asking for the eof argument */ - for (cl = in; cl; cl = cl->next) { - if (cl->buf->last_buf || cl->buf->last_in_chain) { - lua_pushboolean(L, 1); - return 1; - } - } +int +ngx_http_lua_ffi_get_body_filter_param_body(ngx_http_request_t *r, + u_char **data_p, size_t *len_p) +{ + size_t size; + ngx_chain_t *cl; + ngx_buf_t *b; + ngx_chain_t *in; - lua_pushboolean(L, 0); - return 1; - } + ngx_http_lua_main_conf_t *lmcf; - /* idx == 1 */ + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + in = lmcf->body_filter_chain; size = 0; if (in == NULL) { /* being a cleared chain on the Lua land */ - lua_pushliteral(L, ""); - return 1; + *len_p = 0; + return NGX_OK; } if (in->next == NULL) { @@ -388,8 +454,9 @@ ngx_http_lua_body_filter_param_get(lua_State *L, ngx_http_request_t *r) dd("seen only single buffer"); b = in->buf; - lua_pushlstring(L, (char *) b->pos, b->last - b->pos); - return 1; + *data_p = b->pos; + *len_p = b->last - b->pos; + return NGX_OK; } dd("seen multiple buffers"); @@ -404,7 +471,26 @@ ngx_http_lua_body_filter_param_get(lua_State *L, ngx_http_request_t *r) } } - data = (u_char *) lua_newuserdata(L, size); + /* the buf is need and is not allocated from Lua land yet, return with + * the actual size */ + *len_p = size; + return NGX_AGAIN; +} + + +int +ngx_http_lua_ffi_copy_body_filter_param_body(ngx_http_request_t *r, + u_char *data) +{ + u_char *p; + ngx_chain_t *cl; + ngx_buf_t *b; + ngx_chain_t *in; + + ngx_http_lua_main_conf_t *lmcf; + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + in = lmcf->body_filter_chain; for (p = data, cl = in; cl; cl = cl->next) { b = cl->buf; @@ -415,8 +501,7 @@ ngx_http_lua_body_filter_param_get(lua_State *L, ngx_http_request_t *r) } } - lua_pushlstring(L, (char *) data, size); - return 1; + return NGX_OK; } @@ -456,9 +541,23 @@ ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, if (last) { ctx->seen_last_in_filter = 1; - /* the "in" chain cannot be NULL and we set the "last_buf" or - * "last_in_chain" flag in the last buf of "in" */ + /* the "in" chain cannot be NULL except that we set arg[1] = "" + * before arg[2] = true + */ + if (in == NULL) { + in = ngx_http_lua_chain_get_free_buf(r->connection->log, + r->pool, + &ctx->free_bufs, 0); + if (in == NULL) { + return luaL_error(L, "no memory"); + } + in->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter; + lmcf->body_filter_chain = in; + } + + /* we set the "last_buf" or "last_in_chain" flag + * in the last buf of "in" */ for (cl = in; cl; cl = cl->next) { if (cl->next == NULL) { if (r == r->main) { @@ -581,6 +680,7 @@ ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, return luaL_error(L, "no memory"); } + cl->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter; if (type == LUA_TTABLE) { cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last); @@ -598,6 +698,8 @@ ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, if (cl == NULL) { return luaL_error(L, "no memory"); } + + cl->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter; } if (last) { diff --git a/src/ngx_http_lua_bodyfilterby.h b/src/ngx_http_lua_bodyfilterby.h index b108202faa..359646f2b5 100644 --- a/src/ngx_http_lua_bodyfilterby.h +++ b/src/ngx_http_lua_bodyfilterby.h @@ -21,7 +21,6 @@ ngx_int_t ngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in); ngx_int_t ngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in); -int ngx_http_lua_body_filter_param_get(lua_State *L, ngx_http_request_t *r); int ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); diff --git a/src/ngx_http_lua_cache.c b/src/ngx_http_lua_cache.c index 30f29fc4a7..534424688d 100644 --- a/src/ngx_http_lua_cache.c +++ b/src/ngx_http_lua_cache.c @@ -120,7 +120,7 @@ ngx_http_lua_cache_load_code(ngx_log_t *log, lua_State *L, dd("Value associated with given key in code cache table is not code " "chunk: stack top=%d, top value type=%s\n", - lua_gettop(L), lua_typename(L, -1)); + lua_gettop(L), luaL_typename(L, -1)); /* remove cache table and value from stack */ lua_pop(L, 2); /* sp-=2 */ @@ -307,7 +307,13 @@ ngx_http_lua_cache_loadfile(ngx_log_t *log, lua_State *L, break; case LUA_ERRFILE: - errcode = NGX_HTTP_NOT_FOUND; + if (errno == ENOENT) { + errcode = NGX_HTTP_NOT_FOUND; + + } else { + errcode = NGX_HTTP_SERVICE_UNAVAILABLE; + } + /* fall through */ default: diff --git a/src/ngx_http_lua_clfactory.c b/src/ngx_http_lua_clfactory.c index e8a8a0e1f7..9eab164661 100644 --- a/src/ngx_http_lua_clfactory.c +++ b/src/ngx_http_lua_clfactory.c @@ -91,7 +91,7 @@ * | Int | At which line this function is defined * | [linedefined] | * --------------------- - * | Int | At while line this function definition ended + * | Int | At which line this function definition ended * | [lastlinedefined] | * --------------------- * | Char | Number of upvalues referenced by this function @@ -126,9 +126,9 @@ * | at [p[i]] | * --------------------- * | Vector | Debug lineinfo vector - * | [lineinfo] | Empty vector here if dubug info is stripped + * | [lineinfo] | Empty vector here if debug info is stripped * --------------------- - * | Int | Number of local variable in this function + * | Int | Number of local variables in this function * | [sizelocvars] | 0 if debug info is stripped * --------------------- * | String | ------------------------------------ @@ -136,7 +136,7 @@ * | .varname] | | * --------------------- | * | Int | instruction counter | - * | [locvars[i]] | where lcoal var i start to be |-> repeat for i in + * | [locvars[i]] | where local var i start to be |-> repeat for i in * | .startpc] | referenced | [0..sizelocvars] * --------------------- | * | Int | instruction counter, where local | diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 0751a1123f..40c330baff 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -31,6 +31,7 @@ typedef struct { size_t size; int ref; u_char *key; + u_char *chunkname; ngx_str_t script; } ngx_http_lua_set_var_data_t; #endif @@ -54,11 +55,17 @@ typedef struct { #if (NGX_PCRE) -#include -# if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21) +# if (NGX_PCRE2) # define LUA_HAVE_PCRE_JIT 1 # else -# define LUA_HAVE_PCRE_JIT 0 + +#include + +# if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21) +# define LUA_HAVE_PCRE_JIT 1 +# else +# define LUA_HAVE_PCRE_JIT 0 +# endif # endif #endif @@ -124,21 +131,27 @@ typedef struct { (NGX_HTTP_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH) -/* must be within 16 bit */ -#define NGX_HTTP_LUA_CONTEXT_SET 0x0001 -#define NGX_HTTP_LUA_CONTEXT_REWRITE 0x0002 -#define NGX_HTTP_LUA_CONTEXT_ACCESS 0x0004 -#define NGX_HTTP_LUA_CONTEXT_CONTENT 0x0008 -#define NGX_HTTP_LUA_CONTEXT_LOG 0x0010 -#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x0020 -#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x0040 -#define NGX_HTTP_LUA_CONTEXT_TIMER 0x0080 -#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER 0x0100 -#define NGX_HTTP_LUA_CONTEXT_BALANCER 0x0200 -#define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x0400 -#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x0800 -#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000 -#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x2000 +/* must be within 32 bits */ +#define NGX_HTTP_LUA_CONTEXT_SET 0x00000001 +#define NGX_HTTP_LUA_CONTEXT_REWRITE 0x00000002 +#define NGX_HTTP_LUA_CONTEXT_ACCESS 0x00000004 +#define NGX_HTTP_LUA_CONTEXT_CONTENT 0x00000008 +#define NGX_HTTP_LUA_CONTEXT_LOG 0x00000010 +#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x00000020 +#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x00000040 +#define NGX_HTTP_LUA_CONTEXT_TIMER 0x00000080 +#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER 0x00000100 +#define NGX_HTTP_LUA_CONTEXT_BALANCER 0x00000200 +#define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x00000400 +#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x00000800 +#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x00001000 +#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x00002000 +#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x00004000 +#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE 0x00008000 + +#ifdef HAVE_PROXY_SSL_PATCH +#define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY 0x00010000 +#endif #define NGX_HTTP_LUA_FFI_NO_REQ_CTX -100 @@ -158,10 +171,12 @@ typedef struct ngx_http_lua_co_ctx_s ngx_http_lua_co_ctx_t; typedef struct ngx_http_lua_sema_mm_s ngx_http_lua_sema_mm_t; -typedef union ngx_http_lua_srv_conf_u ngx_http_lua_srv_conf_t; +typedef struct ngx_http_lua_srv_conf_s ngx_http_lua_srv_conf_t; typedef struct ngx_http_lua_main_conf_s ngx_http_lua_main_conf_t; +typedef struct ngx_http_lua_loc_conf_s ngx_http_lua_loc_conf_t; + typedef struct ngx_http_lua_header_val_s ngx_http_lua_header_val_t; typedef struct ngx_http_lua_posted_thread_s ngx_http_lua_posted_thread_t; @@ -175,6 +190,9 @@ typedef ngx_int_t (*ngx_http_lua_main_conf_handler_pt)(ngx_log_t *log, typedef ngx_int_t (*ngx_http_lua_srv_conf_handler_pt)(ngx_http_request_t *r, ngx_http_lua_srv_conf_t *lscf, lua_State *L); +typedef ngx_int_t (*ngx_http_lua_loc_conf_handler_pt)(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L); + typedef ngx_int_t (*ngx_http_lua_set_header_pt)(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); @@ -212,13 +230,20 @@ struct ngx_http_lua_main_conf_s { ngx_int_t lua_thread_cache_max_entries; + ngx_hash_t builtin_headers_out; + #if (NGX_PCRE) ngx_int_t regex_cache_entries; ngx_int_t regex_cache_max_entries; ngx_int_t regex_match_limit; -# if (LUA_HAVE_PCRE_JIT) +#endif + +#if (LUA_HAVE_PCRE_JIT) +#if (NGX_PCRE2) + pcre2_jit_stack *jit_stack; +#else pcre_jit_stack *jit_stack; -# endif +#endif #endif ngx_array_t *shm_zones; /* of ngx_shm_zone_t* */ @@ -232,19 +257,15 @@ struct ngx_http_lua_main_conf_s { ngx_http_lua_main_conf_handler_pt init_handler; ngx_str_t init_src; + u_char *init_chunkname; ngx_http_lua_main_conf_handler_pt init_worker_handler; ngx_str_t init_worker_src; + u_char *init_worker_chunkname; ngx_http_lua_main_conf_handler_pt exit_worker_handler; ngx_str_t exit_worker_src; - - ngx_http_lua_balancer_peer_data_t *balancer_peer_data; - /* neither yielding nor recursion is possible in - * balancer_by_lua*, so there cannot be any races among - * concurrent requests and it is safe to store the peer - * data pointer in the main conf. - */ + u_char *exit_worker_chunkname; ngx_chain_t *body_filter_chain; /* neither yielding nor recursion is possible in @@ -272,9 +293,11 @@ struct ngx_http_lua_main_conf_s { ngx_http_lua_sema_mm_t *sema_mm; ngx_uint_t malloc_trim_cycle; /* a cycle is defined as the number - of reqeusts */ + of requests */ ngx_uint_t malloc_trim_req_count; + ngx_uint_t directive_line; + #if (nginx_version >= 1011011) /* the following 2 fields are only used by ngx.req.raw_headers() for now */ ngx_buf_t **busy_buf_ptrs; @@ -288,6 +311,8 @@ struct ngx_http_lua_main_conf_s { ngx_queue_t free_lua_threads; /* of ngx_http_lua_thread_ref_t */ ngx_queue_t cached_lua_threads; /* of ngx_http_lua_thread_ref_t */ + ngx_uint_t worker_thread_vm_pool_size; + unsigned requires_header_filter:1; unsigned requires_body_filter:1; unsigned requires_capture_filter:1; @@ -296,46 +321,87 @@ struct ngx_http_lua_main_conf_s { unsigned requires_log:1; unsigned requires_shm:1; unsigned requires_capture_log:1; + unsigned requires_server_rewrite:1; }; -union ngx_http_lua_srv_conf_u { -#if (NGX_HTTP_SSL) +struct ngx_http_lua_srv_conf_s { struct { +#if (NGX_HTTP_SSL) ngx_http_lua_srv_conf_handler_pt ssl_cert_handler; ngx_str_t ssl_cert_src; u_char *ssl_cert_src_key; + u_char *ssl_cert_chunkname; int ssl_cert_src_ref; ngx_http_lua_srv_conf_handler_pt ssl_sess_store_handler; ngx_str_t ssl_sess_store_src; u_char *ssl_sess_store_src_key; + u_char *ssl_sess_store_chunkname; int ssl_sess_store_src_ref; ngx_http_lua_srv_conf_handler_pt ssl_sess_fetch_handler; ngx_str_t ssl_sess_fetch_src; u_char *ssl_sess_fetch_src_key; + u_char *ssl_sess_fetch_chunkname; int ssl_sess_fetch_src_ref; - } srv; + + ngx_http_lua_srv_conf_handler_pt ssl_client_hello_handler; + ngx_str_t ssl_client_hello_src; + u_char *ssl_client_hello_src_key; + u_char *ssl_client_hello_chunkname; + int ssl_client_hello_src_ref; #endif + ngx_http_lua_srv_conf_handler_pt server_rewrite_handler; + ngx_http_complex_value_t server_rewrite_src; + u_char *server_rewrite_src_key; + u_char *server_rewrite_chunkname; + int server_rewrite_src_ref; + } srv; + struct { + ngx_uint_t max_cached; + ngx_queue_t cache; + ngx_queue_t free; + ngx_queue_t *buckets; + ngx_uint_t bucket_cnt; + ngx_http_upstream_init_pt original_init_upstream; + ngx_http_upstream_init_peer_pt original_init_peer; + ngx_http_lua_srv_conf_handler_pt handler; ngx_str_t src; u_char *src_key; + u_char *chunkname; int src_ref; } balancer; }; -typedef struct { +struct ngx_http_lua_loc_conf_s { #if (NGX_HTTP_SSL) ngx_ssl_t *ssl; /* shared by SSL cosockets */ + ngx_array_t *ssl_certificates; + ngx_array_t *ssl_certificate_keys; ngx_uint_t ssl_protocols; ngx_str_t ssl_ciphers; ngx_uint_t ssl_verify_depth; ngx_str_t ssl_trusted_certificate; ngx_str_t ssl_crl; + ngx_str_t ssl_key_log; +#if (nginx_version >= 1019004) + ngx_array_t *ssl_conf_commands; +#endif + +#ifdef HAVE_PROXY_SSL_PATCH + ngx_http_lua_loc_conf_handler_pt proxy_ssl_verify_handler; + ngx_str_t proxy_ssl_verify_src; + u_char *proxy_ssl_verify_src_key; + u_char *proxy_ssl_verify_chunkname; + int proxy_ssl_verify_src_ref; + ngx_flag_t upstream_skip_openssl_default_verify; +#endif + #endif ngx_flag_t force_read_body; /* whether force request body to @@ -354,6 +420,8 @@ typedef struct { ngx_http_output_body_filter_pt body_filter_handler; + + u_char *rewrite_chunkname; ngx_http_complex_value_t rewrite_src; /* rewrite_by_lua inline script/script @@ -390,6 +458,7 @@ typedef struct { inline script/script file path */ + u_char *header_filter_chunkname; u_char *header_filter_src_key; /* cached key for header_filter_src */ int header_filter_src_ref; @@ -397,6 +466,7 @@ typedef struct { ngx_http_complex_value_t body_filter_src; u_char *body_filter_src_key; + u_char *body_filter_chunkname; int body_filter_src_ref; ngx_msec_t keepalive_timeout; @@ -413,7 +483,7 @@ typedef struct { ngx_flag_t log_socket_errors; ngx_flag_t check_client_abort; ngx_flag_t use_default_type; -} ngx_http_lua_loc_conf_t; +}; typedef enum { @@ -458,6 +528,11 @@ struct ngx_http_lua_co_ctx_s { uint8_t *sr_flags; + unsigned nresults_from_worker_thread; /* number of results + * from worker + * thread callback */ + unsigned nrets; /* ngx_http_lua_run_thread nrets arg. */ + unsigned nsubreqs; /* number of subrequests of the * current request */ @@ -543,7 +618,10 @@ typedef struct ngx_http_lua_ctx_s { ngx_chain_t *busy_bufs; ngx_chain_t *free_recv_bufs; - ngx_http_cleanup_pt *cleanup; + ngx_chain_t *filter_in_bufs; /* for the body filter */ + ngx_chain_t *filter_busy_bufs; /* for the body filter */ + + ngx_pool_cleanup_pt *cleanup; ngx_http_cleanup_t *free_cleanup; /* free list of cleanup records */ @@ -569,7 +647,7 @@ typedef struct ngx_http_lua_ctx_s { int uthreads; /* number of active user threads */ - uint16_t context; /* the current running directive context + uint32_t context; /* the current running directive context (or running phase) for the current Lua chunk */ @@ -602,7 +680,7 @@ typedef struct ngx_http_lua_ctx_s { response headers */ unsigned mime_set:1; /* whether the user has set Content-Type response header */ - + unsigned entered_server_rewrite_phase:1; unsigned entered_rewrite_phase:1; unsigned entered_access_phase:1; unsigned entered_content_phase:1; diff --git a/src/ngx_http_lua_consts.c b/src/ngx_http_lua_consts.c index 47ba998576..f2dcf4a490 100644 --- a/src/ngx_http_lua_consts.c +++ b/src/ngx_http_lua_consts.c @@ -176,6 +176,10 @@ ngx_http_lua_inject_http_consts(lua_State *L) lua_pushinteger(L, NGX_HTTP_INTERNAL_SERVER_ERROR); lua_setfield(L, -2, "HTTP_INTERNAL_SERVER_ERROR"); + lua_pushinteger(L, NGX_HTTP_NOT_IMPLEMENTED); + lua_setfield(L, -2, "HTTP_NOT_IMPLEMENTED"); + + /* keep for backward compatibility */ lua_pushinteger(L, NGX_HTTP_NOT_IMPLEMENTED); lua_setfield(L, -2, "HTTP_METHOD_NOT_IMPLEMENTED"); diff --git a/src/ngx_http_lua_contentby.c b/src/ngx_http_lua_contentby.c index 76e6a074da..039f23ffcd 100644 --- a/src/ngx_http_lua_contentby.c +++ b/src/ngx_http_lua_contentby.c @@ -29,7 +29,7 @@ ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r) lua_State *co; ngx_event_t *rev; ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; @@ -83,7 +83,7 @@ ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r) /* {{{ register request cleanup hooks */ if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -267,6 +267,10 @@ ngx_http_lua_content_handler_file(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->content_src_ref = LUA_REFNIL; + } + /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, &llcf->content_src_ref, @@ -297,6 +301,10 @@ ngx_http_lua_content_handler_inline(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->content_src_ref = LUA_REFNIL; + } + /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, llcf->content_src.value.data, diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index 9b51c4af37..8358abcbb6 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -91,6 +91,7 @@ ngx_http_lua_ngx_exec(lua_State *L) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); @@ -232,6 +233,7 @@ ngx_http_lua_ngx_redirect(lua_State *L) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); @@ -278,6 +280,9 @@ ngx_http_lua_ngx_redirect(lua_State *L) h->value.len = len; h->value.data = uri; +#if defined(nginx_version) && nginx_version >= 1023000 + h->next = NULL; +#endif ngx_str_set(&h->key, "Location"); r->headers_out.status = rc; @@ -358,6 +363,14 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, { ngx_http_lua_ctx_t *ctx; + if (status == NGX_AGAIN || status == NGX_DONE) { + *errlen = ngx_snprintf(err, *errlen, + "bad argument to 'ngx.exit': does not accept " + "NGX_AGAIN or NGX_DONE") + - err; + return NGX_ERROR; + } + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err; @@ -365,11 +378,16 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, } if (ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER +#ifdef HAVE_PROXY_SSL_PATCH + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY +#endif + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH, @@ -380,6 +398,10 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, } if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT +#ifdef HAVE_PROXY_SSL_PATCH + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY +#endif + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) { diff --git a/src/ngx_http_lua_ctx.c b/src/ngx_http_lua_ctx.c index dc84013493..d5431be724 100644 --- a/src/ngx_http_lua_ctx.c +++ b/src/ngx_http_lua_ctx.c @@ -88,6 +88,7 @@ ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r, int *in_ssl_phase, } *in_ssl_phase = ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE); *ssl_ctx_ref = LUA_NOREF; @@ -123,6 +124,7 @@ ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref) #if (NGX_HTTP_SSL) if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE)) { diff --git a/src/ngx_http_lua_directive.c b/src/ngx_http_lua_directive.c index 1ec641e079..f42aae9d51 100644 --- a/src/ngx_http_lua_directive.c +++ b/src/ngx_http_lua_directive.c @@ -17,6 +17,7 @@ #include "ngx_http_lua_cache.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_accessby.h" +#include "ngx_http_lua_server_rewriteby.h" #include "ngx_http_lua_rewriteby.h" #include "ngx_http_lua_logby.h" #include "ngx_http_lua_headerfilterby.h" @@ -32,6 +33,12 @@ #include "ngx_http_lua_log.h" +/* the max length is 60, after deducting the fixed four characters "=(:)" + * only 56 left. + */ +#define LJ_CHUNKNAME_MAX_LEN 56 + + typedef struct ngx_http_lua_block_parser_ctx_s ngx_http_lua_block_parser_ctx_t; @@ -43,8 +50,6 @@ typedef struct ngx_http_lua_block_parser_ctx_s static ngx_int_t ngx_http_lua_set_by_lua_init(ngx_http_request_t *r); #endif -static u_char *ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, - size_t tag_len, size_t *chunkname_len); static ngx_int_t ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf, ngx_http_lua_block_parser_ctx_t *ctx); static u_char *ngx_http_lua_strlstrn(u_char *s1, u_char *last, u_char *s2, @@ -280,6 +285,8 @@ ngx_http_lua_set_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, char * ngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + size_t chunkname_len; + u_char *chunkname; u_char *cache_key; ngx_str_t *value; ngx_str_t target; @@ -312,7 +319,15 @@ ngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } + chunkname = ngx_http_lua_gen_chunk_name(cf, "set_by_lua", + sizeof("set_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + filter_data->key = cache_key; + filter_data->chunkname = chunkname; filter_data->ref = LUA_REFNIL; filter_data->script = value[2]; filter_data->size = filter.size; @@ -375,6 +390,7 @@ ngx_http_lua_set_by_lua_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) filter_data->key = cache_key; filter_data->ref = LUA_REFNIL; filter_data->size = filter.size; + filter_data->chunkname = NULL; ngx_str_null(&filter_data->script); @@ -404,7 +420,8 @@ ngx_http_lua_filter_set_by_lua_inline(ngx_http_request_t *r, ngx_str_t *val, filter_data->script.data, filter_data->script.len, &filter_data->ref, - filter_data->key, "=set_by_lua"); + filter_data->key, + (const char *) filter_data->chunkname); if (rc != NGX_OK) { return NGX_ERROR; } @@ -574,6 +591,111 @@ ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +char * +ngx_http_lua_server_rewrite_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + char *rv; + ngx_conf_t save; + save = *cf; + cf->handler = ngx_http_lua_server_rewrite_by_lua; + cf->handler_conf = conf; + + rv = ngx_http_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_http_lua_server_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + size_t chunkname_len; + u_char *cache_key = NULL, *chunkname; + ngx_str_t *value; + ngx_http_lua_main_conf_t *lmcf; + ngx_http_lua_srv_conf_t *lscf = conf; + + ngx_http_compile_complex_value_t ccv; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->srv.server_rewrite_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (value[1].len == 0) { + /* Oops...Invalid location conf */ + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "invalid location config: no runnable Lua code"); + + return NGX_CONF_ERROR; + } + + if (cmd->post == ngx_http_lua_server_rewrite_handler_inline) { + chunkname = + ngx_http_lua_gen_chunk_name(cf, "server_rewrite_by_lua", + sizeof("server_rewrite_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + cache_key = + ngx_http_lua_gen_chunk_cache_key(cf, "server_rewrite_by_lua", + value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + + /* Don't eval nginx variables for inline lua code */ + lscf->srv.server_rewrite_src.value = value[1]; + lscf->srv.server_rewrite_chunkname = chunkname; + + } else { + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &lscf->srv.server_rewrite_src; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (lscf->srv.server_rewrite_src.lengths == NULL) { + /* no variable found */ + cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + } + } + + lscf->srv.server_rewrite_src_key = cache_key; + lscf->srv.server_rewrite_handler = + (ngx_http_lua_srv_conf_handler_pt) cmd->post; + + lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); + + lmcf->requires_server_rewrite = 1; + lmcf->requires_capture_filter = 1; + + return NGX_CONF_OK; +} + + char * ngx_http_lua_access_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) @@ -912,7 +1034,8 @@ char * ngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - u_char *cache_key = NULL; + size_t chunkname_len; + u_char *cache_key = NULL, *chunkname; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; @@ -947,8 +1070,15 @@ ngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_ERROR; } + chunkname = ngx_http_lua_gen_chunk_name(cf, "header_filter_by_lua", + sizeof("header_filter_by_lua") - 1, &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + /* Don't eval nginx variables for inline lua code */ llcf->header_filter_src.value = value[1]; + llcf->header_filter_chunkname = chunkname; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); @@ -1004,7 +1134,8 @@ char * ngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - u_char *cache_key = NULL; + size_t chunkname_len; + u_char *cache_key = NULL, *chunkname; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; @@ -1039,8 +1170,16 @@ ngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_ERROR; } + chunkname = ngx_http_lua_gen_chunk_name(cf, "body_filter_by_lua", + sizeof("body_filter_by_lua") - 1, &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + /* Don't eval nginx variables for inline lua code */ llcf->body_filter_src.value = value[1]; + llcf->body_filter_chunkname = chunkname; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); @@ -1100,6 +1239,8 @@ ngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, u_char *name; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf = conf; + size_t chunkname_len; + u_char *chunkname; dd("enter"); @@ -1135,6 +1276,15 @@ ngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, } else { lmcf->init_src = value[1]; + + chunkname = ngx_http_lua_gen_chunk_name(cf, "init_by_lua", + sizeof("init_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + lmcf->init_chunkname = chunkname; } return NGX_CONF_OK; @@ -1167,6 +1317,8 @@ ngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, u_char *name; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf = conf; + size_t chunkname_len; + u_char *chunkname; dd("enter"); @@ -1195,6 +1347,14 @@ ngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, } else { lmcf->init_worker_src = value[1]; + + chunkname = ngx_http_lua_gen_chunk_name(cf, "init_worker_by_lua", + sizeof("init_worker_by_lua") - 1, &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + lmcf->init_worker_chunkname = chunkname; } return NGX_CONF_OK; @@ -1227,6 +1387,8 @@ ngx_http_lua_exit_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, u_char *name; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf = conf; + size_t chunkname_len; + u_char *chunkname; /* must specify a content handler */ if (cmd->post == NULL) { @@ -1253,6 +1415,15 @@ ngx_http_lua_exit_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, } else { lmcf->exit_worker_src = value[1]; + + chunkname = ngx_http_lua_gen_chunk_name(cf, "exit_worker_by_lua", + sizeof("exit_worker_by_lua")- 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + lmcf->exit_worker_chunkname = chunkname; } return NGX_CONF_OK; @@ -1265,7 +1436,7 @@ ngx_http_lua_set_by_lua_init(ngx_http_request_t *r) { lua_State *L; ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { @@ -1280,7 +1451,7 @@ ngx_http_lua_set_by_lua_init(ngx_http_request_t *r) } if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_ERROR; } @@ -1296,12 +1467,20 @@ ngx_http_lua_set_by_lua_init(ngx_http_request_t *r) #endif -static u_char * +u_char * ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len, size_t *chunkname_len) { u_char *p, *out; size_t len; + ngx_uint_t start_line; + ngx_str_t *conf_prefix; + ngx_str_t *filename; + u_char *filename_end; + const char *pre_str = ""; + ngx_uint_t reserve_len; + + ngx_http_lua_main_conf_t *lmcf; len = sizeof("=(:)") - 1 + tag_len + cf->conf_file->file.name.len + NGX_INT64_LEN + 1; @@ -1311,27 +1490,56 @@ ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len, return NULL; } - if (cf->conf_file->file.name.len) { - p = cf->conf_file->file.name.data + cf->conf_file->file.name.len; - while (--p >= cf->conf_file->file.name.data) { - if (*p == '/' || *p == '\\') { - p++; + lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); + start_line = lmcf->directive_line > 0 + ? lmcf->directive_line : cf->conf_file->line; + p = ngx_snprintf(out, len, "%d", start_line); + reserve_len = tag_len + p - out; + + filename = &cf->conf_file->file.name; + filename_end = filename->data + filename->len; + if (filename->len > 0) { + if (filename->len >= 11) { + p = filename_end - 11; + if ((*p == '/' || *p == '\\') + && ngx_memcmp(p, "/nginx.conf", 11) == 0) + { + p++; /* now p is nginx.conf */ goto found; } } - p++; + conf_prefix = &cf->cycle->conf_prefix; + p = filename->data + conf_prefix->len; + if ((conf_prefix->len < filename->len) + && ngx_memcmp(conf_prefix->data, + filename->data, conf_prefix->len) == 0) + { + /* files in conf_prefix directory, use the relative path */ + if (filename_end - p + reserve_len > LJ_CHUNKNAME_MAX_LEN) { + p = filename_end - LJ_CHUNKNAME_MAX_LEN + reserve_len + 3; + pre_str = "..."; + } - } else { - p = cf->conf_file->file.name.data; + goto found; + } } + p = filename->data; + + if (filename->len + reserve_len <= LJ_CHUNKNAME_MAX_LEN) { + goto found; + } + + p = filename_end - LJ_CHUNKNAME_MAX_LEN + reserve_len + 3; + pre_str = "..."; + found: - 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); + + p = ngx_snprintf(out, len, "=%*s(%s%*s:%d)%Z", + tag_len, tag, pre_str, filename_end - p, + p, start_line); *chunkname_len = p - out - 1; /* exclude the trailing '\0' byte */ @@ -1343,6 +1551,7 @@ ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len, char * ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd) { + ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_block_parser_ctx_t ctx; int level = 1; @@ -1376,6 +1585,9 @@ ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd) ctx.token_len = 0; start_line = cf->conf_file->line; + lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); + lmcf->directive_line = start_line; + dd("init start line: %d", (int) start_line); ctx.start_line = start_line; @@ -1494,6 +1706,8 @@ ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd) done: + lmcf->directive_line = 0; + if (rc == NGX_ERROR) { return NGX_CONF_ERROR; } @@ -1537,7 +1751,7 @@ ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf, for ( ;; ) { if (b->pos >= b->last - || (b->last - b->pos < (b->end - b->start) / 3 + || (b->last - b->pos < (b->end - b->start) / 2 && cf->conf_file->file.offset < file_size)) { @@ -1632,7 +1846,7 @@ ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf, } if (len) { - ngx_memcpy(b->start, b->pos, len); + ngx_memmove(b->start, b->pos, len); } size = (ssize_t) (file_size - cf->conf_file->file.offset); @@ -1709,7 +1923,8 @@ ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf, if (q == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Lua code block missing the closing " - "long bracket \"%*s\"", + "long bracket \"%*s\", " + "the inlined Lua code may be too long", b->pos + ovec[1] - p, p); return NGX_ERROR; } diff --git a/src/ngx_http_lua_directive.h b/src/ngx_http_lua_directive.h index adfba12761..4bec5e3094 100644 --- a/src/ngx_http_lua_directive.h +++ b/src/ngx_http_lua_directive.h @@ -25,6 +25,10 @@ char *ngx_http_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char *ngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_http_lua_server_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_http_lua_server_rewrite_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); char *ngx_http_lua_rewrite_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char *ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, @@ -81,7 +85,8 @@ char *ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd); char *ngx_http_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); - +u_char *ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, + size_t tag_len, size_t *chunkname_len); #endif /* _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_ */ diff --git a/src/ngx_http_lua_exitworkerby.c b/src/ngx_http_lua_exitworkerby.c index cd59dc4bed..e8f65110ae 100644 --- a/src/ngx_http_lua_exitworkerby.c +++ b/src/ngx_http_lua_exitworkerby.c @@ -13,6 +13,10 @@ #include "ngx_http_lua_exitworkerby.h" #include "ngx_http_lua_util.h" +#if (NGX_THREADS) +#include "ngx_http_lua_worker_thread.h" +#endif + void ngx_http_lua_exit_worker(ngx_cycle_t *cycle) @@ -23,6 +27,10 @@ ngx_http_lua_exit_worker(ngx_cycle_t *cycle) ngx_http_lua_ctx_t *ctx; ngx_http_conf_ctx_t *conf_ctx; +#if (NGX_THREADS) + ngx_http_lua_thread_exit_process(); +#endif + lmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lua_module); if (lmcf == NULL || lmcf->exit_worker_handler == NULL @@ -87,9 +95,17 @@ ngx_http_lua_exit_worker_by_inline(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L) { int status; + const char *chunkname; + + if (lmcf->exit_worker_chunkname == NULL) { + chunkname = "=exit_worker_by_lua"; + + } else { + chunkname = (const char *) lmcf->exit_worker_chunkname; + } status = luaL_loadbuffer(L, (char *) lmcf->exit_worker_src.data, - lmcf->exit_worker_src.len, "=exit_worker_by_lua") + lmcf->exit_worker_src.len, chunkname) || ngx_http_lua_do_call(log, L); return ngx_http_lua_report(log, L, status, "exit_worker_by_lua"); diff --git a/src/ngx_http_lua_headerfilterby.c b/src/ngx_http_lua_headerfilterby.c index 4741c72267..d41c055ef5 100644 --- a/src/ngx_http_lua_headerfilterby.c +++ b/src/ngx_http_lua_headerfilterby.c @@ -165,13 +165,18 @@ ngx_http_lua_header_filter_inline(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->header_filter_src_ref = LUA_REFNIL; + } + /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, llcf->header_filter_src.value.data, llcf->header_filter_src.value.len, &llcf->header_filter_src_ref, llcf->header_filter_src_key, - "=header_filter_by_lua"); + (const char *) + llcf->header_filter_chunkname); if (rc != NGX_OK) { return NGX_ERROR; } @@ -209,6 +214,10 @@ ngx_http_lua_header_filter_file(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->header_filter_src_ref = LUA_REFNIL; + } + /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, &llcf->header_filter_src_ref, @@ -230,7 +239,7 @@ ngx_http_lua_header_filter(ngx_http_request_t *r) ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; uint16_t old_context; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -259,7 +268,7 @@ ngx_http_lua_header_filter(ngx_http_request_t *r) } if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_ERROR; } diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index d8d5edc8b2..85836a1289 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -84,6 +84,12 @@ ngx_http_lua_ngx_req_http_version(lua_State *L) break; #endif +#ifdef NGX_HTTP_VERSION_30 + case NGX_HTTP_VERSION_30: + lua_pushnumber(L, 3.0); + break; +#endif + default: lua_pushnil(L); break; @@ -167,6 +173,12 @@ ngx_http_lua_ngx_req_raw_header(lua_State *L) size = 0; b = c->buffer; + if (mr->request_line.len == 0) { + /* return empty string on invalid request */ + lua_pushlstring(L, "", 0); + return 1; + } + if (mr->request_line.data[mr->request_line.len] == CR) { line_break_len = 2; @@ -677,7 +689,7 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) p = (u_char *) luaL_checklstring(L, -1, &len); /* - * we also copy the trailling '\0' char here because nginx + * we also copy the trailing '\0' char here because nginx * header values must be null-terminated * */ @@ -705,7 +717,7 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) } else { /* - * we also copy the trailling '\0' char here because nginx + * we also copy the trailing '\0' char here because nginx * header values must be null-terminated * */ @@ -770,6 +782,11 @@ ngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r, int max, { int count; ngx_list_part_t *part; +#if (NGX_HTTP_V3) + int has_host = 0; + ngx_uint_t i; + ngx_table_elt_t *header; +#endif if (r->connection->fd == (ngx_socket_t) -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; @@ -782,11 +799,54 @@ ngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r, int max, } part = &r->headers_in.headers.part; + +#if (NGX_HTTP_V3) + count = 0; + header = part->elts; + + if (r->http_version == NGX_HTTP_VERSION_30 + && r->headers_in.server.data != NULL) + { + has_host = 1; + count++; + } + + if (has_host == 1) { + for (i = 0; /* void */; i++) { + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].key.len == 4 + && ngx_strncasecmp(header[i].key.data, + (u_char *) "host", 4) == 0) + { + continue; + } + + count++; + } + + } else { + count = part->nelts; + while (part->next != NULL) { + part = part->next; + count += part->nelts; + } + } +#else count = part->nelts; while (part->next != NULL) { part = part->next; count += part->nelts; } +#endif if (max > 0 && count > max) { *truncated = 1; @@ -809,12 +869,29 @@ ngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r, ngx_uint_t i; ngx_list_part_t *part; ngx_table_elt_t *header; +#if (NGX_HTTP_V3) + int has_host = 0; +#endif if (count <= 0) { return NGX_OK; } n = 0; + +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30 + && r->headers_in.server.data != NULL) + { + out[n].key.data = (u_char *) "host"; + out[n].key.len = sizeof("host") - 1; + out[n].value.len = r->headers_in.server.len; + out[n].value.data = r->headers_in.server.data; + has_host = 1; + ++n; + } +#endif + part = &r->headers_in.headers.part; header = part->elts; @@ -830,6 +907,14 @@ ngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r, i = 0; } +#if (NGX_HTTP_V3) + if (has_host == 1 && header[i].key.len == 4 + && ngx_strncasecmp(header[i].key.data, (u_char *) "host", 4) == 0) + { + continue; + } +#endif + if (raw) { out[n].key.data = header[i].key.data; out[n].key.len = (int) header[i].key.len; @@ -1068,6 +1153,7 @@ ngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r, { int found; u_char c, *p; + time_t last_modified; ngx_uint_t i; ngx_table_elt_t *h; ngx_list_part_t *part; @@ -1134,6 +1220,28 @@ ngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r, break; + case 13: + if (ngx_strncasecmp(key_buf, (u_char *) "Last-Modified", 13) == 0) { + last_modified = r->headers_out.last_modified_time; + if (last_modified >= 0) { + p = ngx_palloc(r->pool, + sizeof("Mon, 28 Sep 1970 06:00:00 GMT")); + if (p == NULL) { + *errmsg = "no memory"; + return NGX_ERROR; + } + + values[0].data = p; + values[0].len = ngx_http_time(p, last_modified) - p; + + return 1; + } + + return 0; + } + + break; + default: break; } @@ -1211,4 +1319,18 @@ ngx_http_lua_ngx_raw_header_cleanup(void *data) #endif +#if (NGX_DARWIN) +int +ngx_http_lua_ffi_set_resp_header_macos(ngx_http_lua_set_resp_header_params_t *p) +{ + return ngx_http_lua_ffi_set_resp_header(p->r, (const u_char *) p->key_data, + p->key_len, p->is_nil, + (const u_char *) p->sval, + p->sval_len, + p->mvals, p->mvals_len, + p->override, p->errmsg); +} +#endif + + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_headers_in.c b/src/ngx_http_lua_headers_in.c index 7626d1f949..e756700b06 100644 --- a/src/ngx_http_lua_headers_in.c +++ b/src/ngx_http_lua_headers_in.c @@ -152,9 +152,15 @@ static ngx_http_lua_set_header_t ngx_http_lua_set_handlers[] = { ngx_http_set_builtin_header }, #endif +#if defined(nginx_version) && nginx_version >= 1023000 + { ngx_string("Cookie"), + offsetof(ngx_http_headers_in_t, cookie), + ngx_http_set_builtin_multi_header }, +#else { ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookies), ngx_http_set_builtin_multi_header }, +#endif { ngx_null_string, 0, ngx_http_set_header } }; @@ -274,6 +280,9 @@ ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, h->key = hv->key; h->value = *value; +#if defined(nginx_version) && nginx_version >= 1023000 + h->next = NULL; +#endif h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { @@ -580,6 +589,47 @@ static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { +#if defined(nginx_version) && nginx_version >= 1023000 + ngx_table_elt_t **headers, **ph, *h; + + headers = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset); + + if (!hv->no_override && *headers != NULL) { +#if defined(DDEBUG) && (DDEBUG) + int nelts = 0; + + for (h = *headers; h; h = h->next) { + nelts++; + } + + dd("clear multi-value headers: %d", nelts); +#endif + + *headers = NULL; + } + + if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) { + return NGX_ERROR; + } + + if (value->len == 0) { + return NGX_OK; + } + + dd("new multi-value header: %p", h); + + if (*headers) { + for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ } + *ph = h; + + } else { + *headers = h; + } + + h->next = NULL; + + return NGX_OK; +#else ngx_array_t *headers; ngx_table_elt_t **v, *h; @@ -626,6 +676,7 @@ ngx_http_set_builtin_multi_header(ngx_http_request_t *r, *v = h; return NGX_OK; +#endif } @@ -669,7 +720,13 @@ ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key, return NGX_ERROR; } - hv.hash = ngx_hash_key_lc(key.data, key.len); + if (value.len > 0) { + hv.hash = ngx_hash_key_lc(key.data, key.len); + + } else { + hv.hash = 0; + } + hv.key = key; hv.offset = 0; diff --git a/src/ngx_http_lua_headers_out.c b/src/ngx_http_lua_headers_out.c index 6e1879c50f..c51146a3fc 100644 --- a/src/ngx_http_lua_headers_out.c +++ b/src/ngx_http_lua_headers_out.c @@ -229,6 +229,9 @@ ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, h->key = hv->key; h->value = *value; +#if defined(nginx_version) && nginx_version >= 1023000 + h->next = NULL; +#endif h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { @@ -311,6 +314,69 @@ static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { +#if defined(nginx_version) && nginx_version >= 1023000 + ngx_table_elt_t **headers, *h, *ho, **ph; + + headers = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); + + if (hv->no_override) { + for (h = *headers; h; h = h->next) { + if (!h->hash) { + h->value = *value; + h->hash = hv->hash; + return NGX_OK; + } + } + + goto create; + } + + /* override old values (if any) */ + + if (*headers) { + for (h = (*headers)->next; h; h = h->next) { + h->hash = 0; + h->value.len = 0; + } + + h = *headers; + + h->value = *value; + + if (value->len == 0) { + h->hash = 0; + + } else { + h->hash = hv->hash; + } + + return NGX_OK; + } + +create: + + for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ } + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + ho->value = *value; + + if (value->len == 0) { + ho->hash = 0; + + } else { + ho->hash = hv->hash; + } + + ho->key = hv->key; + ho->next = NULL; + *ph = ho; + + return NGX_OK; +#else ngx_array_t *pa; ngx_table_elt_t *ho, **ph; ngx_uint_t i; @@ -384,6 +450,7 @@ ngx_http_set_builtin_multi_header(ngx_http_request_t *r, *ph = ho; return NGX_OK; +#endif } @@ -486,8 +553,9 @@ ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_str_t key, ngx_str_t value, unsigned override) { ngx_http_lua_header_val_t hv; - ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers; - ngx_uint_t i; + ngx_http_lua_main_conf_t *lmcf; + ngx_http_lua_set_header_t *lsh; + ngx_hash_t *hash; dd("set header value: %.*s", (int) value.len, value.data); @@ -504,42 +572,20 @@ ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, hv.offset = 0; hv.no_override = !override; - hv.handler = NULL; - - for (i = 0; handlers[i].name.len; i++) { - if (hv.key.len != handlers[i].name.len - || ngx_strncasecmp(hv.key.data, handlers[i].name.data, - handlers[i].name.len) != 0) - { - dd("hv key comparison: %s <> %s", handlers[i].name.data, - hv.key.data); - - continue; - } - - dd("Matched handler: %s %s", handlers[i].name.data, hv.key.data); - - hv.offset = handlers[i].offset; - hv.handler = handlers[i].handler; - + hv.handler = ngx_http_set_header; + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + hash = &lmcf->builtin_headers_out; + lsh = ngx_http_lua_hash_find_lc(hash, hv.hash, hv.key.data, hv.key.len); + if (lsh) { + dd("Matched handler: %s %s", lsh->name.data, hv.key.data); + hv.offset = lsh->offset; + hv.handler = lsh->handler; if (hv.handler == ngx_http_set_content_type_header) { ctx->mime_set = 1; } - - break; } - if (handlers[i].name.len == 0 && handlers[i].handler) { - hv.offset = handlers[i].offset; - hv.handler = handlers[i].handler; - } - -#if 1 - if (hv.handler == NULL) { - return NGX_ERROR; - } -#endif - return hv.handler(r, &hv, &value); } @@ -652,4 +698,48 @@ ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r, return 1; } + +ngx_int_t +ngx_http_lua_init_builtin_headers_out(ngx_conf_t *cf, + ngx_http_lua_main_conf_t *lmcf) +{ + ngx_array_t headers; + ngx_hash_key_t *hk; + ngx_hash_init_t hash; + ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers; + ngx_uint_t count; + + count = sizeof(ngx_http_lua_set_handlers) + / sizeof(ngx_http_lua_set_header_t); + + if (ngx_array_init(&headers, cf->temp_pool, count, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + while (handlers->name.data) { + hk = ngx_array_push(&headers); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key = handlers->name; + hk->key_hash = ngx_hash_key_lc(handlers->name.data, handlers->name.len); + hk->value = (void *) handlers; + + handlers++; + } + + hash.hash = &lmcf->builtin_headers_out; + hash.key = ngx_hash_key_lc; + hash.max_size = 512; + hash.bucket_size = ngx_align(64, ngx_cacheline_size); + hash.name = "builtin_headers_out_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + return ngx_hash_init(&hash, headers.elts, headers.nelts); +} + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_headers_out.h b/src/ngx_http_lua_headers_out.h index 6ec1fe391d..eb3c794723 100644 --- a/src/ngx_http_lua_headers_out.h +++ b/src/ngx_http_lua_headers_out.h @@ -12,10 +12,28 @@ #include "ngx_http_lua_common.h" +#if (NGX_DARWIN) +typedef struct { + ngx_http_request_t *r; + const char *key_data; + size_t key_len; + int is_nil; + const char *sval; + size_t sval_len; + void *mvals; + size_t mvals_len; + int override; + char **errmsg; +} ngx_http_lua_set_resp_header_params_t; +#endif + + ngx_int_t ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_str_t key, ngx_str_t value, unsigned override); int ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_str_t *key); +ngx_int_t ngx_http_lua_init_builtin_headers_out(ngx_conf_t *cf, + ngx_http_lua_main_conf_t *lmcf); #endif /* _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_ */ diff --git a/src/ngx_http_lua_initby.c b/src/ngx_http_lua_initby.c index e8941da6ca..1f956e7ebd 100644 --- a/src/ngx_http_lua_initby.c +++ b/src/ngx_http_lua_initby.c @@ -18,9 +18,18 @@ ngx_http_lua_init_by_inline(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L) { int status; + const char *chunkname; + + + if (lmcf->init_chunkname == NULL) { + chunkname = "=init_by_lua"; + + } else { + chunkname = (const char *) lmcf->init_chunkname; + } status = luaL_loadbuffer(L, (char *) lmcf->init_src.data, - lmcf->init_src.len, "=init_by_lua") + lmcf->init_src.len, chunkname) || ngx_http_lua_do_call(log, L); return ngx_http_lua_report(log, L, status, "init_by_lua"); diff --git a/src/ngx_http_lua_initworkerby.c b/src/ngx_http_lua_initworkerby.c index 94de796ade..edb68df08a 100644 --- a/src/ngx_http_lua_initworkerby.c +++ b/src/ngx_http_lua_initworkerby.c @@ -118,14 +118,15 @@ ngx_http_lua_init_worker(ngx_cycle_t *cycle) ngx_queue_init(&fake_cycle->reusable_connections_queue); if (ngx_array_init(&fake_cycle->listening, cycle->pool, - cycle->listening.nelts || 1, + cycle->listening.nelts ? cycle->listening.nelts : 1, sizeof(ngx_listening_t)) != NGX_OK) { goto failed; } - if (ngx_array_init(&fake_cycle->paths, cycle->pool, cycle->paths.nelts || 1, + if (ngx_array_init(&fake_cycle->paths, cycle->pool, + cycle->paths.nelts ? cycle->paths.nelts : 1, sizeof(ngx_path_t *)) != NGX_OK) { @@ -135,7 +136,8 @@ ngx_http_lua_init_worker(ngx_cycle_t *cycle) part = &cycle->open_files.part; ofile = part->elts; - if (ngx_list_init(&fake_cycle->open_files, cycle->pool, part->nelts || 1, + if (ngx_list_init(&fake_cycle->open_files, cycle->pool, + part->nelts ? part->nelts : 1, sizeof(ngx_open_file_t)) != NGX_OK) { @@ -295,6 +297,8 @@ ngx_http_lua_init_worker(ngx_cycle_t *cycle) (void) lmcf->init_worker_handler(cycle->log, lmcf, lmcf->lua); + ngx_http_lua_set_req(lmcf->lua, NULL); + ngx_destroy_pool(c->pool); return NGX_OK; @@ -317,9 +321,17 @@ ngx_http_lua_init_worker_by_inline(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L) { int status; + const char *chunkname; + + if (lmcf->init_worker_chunkname == NULL) { + chunkname = "=init_worker_by_lua"; + + } else { + chunkname = (const char *) lmcf->init_worker_chunkname; + } status = luaL_loadbuffer(L, (char *) lmcf->init_worker_src.data, - lmcf->init_worker_src.len, "=init_worker_by_lua") + lmcf->init_worker_src.len, chunkname) || ngx_http_lua_do_call(log, L); return ngx_http_lua_report(log, L, status, "init_worker_by_lua"); diff --git a/src/ngx_http_lua_lex.c b/src/ngx_http_lua_lex.c index 45cf7540e7..c9064030c2 100644 --- a/src/ngx_http_lua_lex.c +++ b/src/ngx_http_lua_lex.c @@ -3,8 +3,7 @@ * * WARNING: DO NOT EVER EDIT THIS FILE!! * - * This file was automatically generated by the re.pl script of sregex's - * "dfa-multi-re" git branch. + * This file was automatically generated by the OpenResty Regex compiler. */ diff --git a/src/ngx_http_lua_logby.c b/src/ngx_http_lua_logby.c index 408708cfa5..cded8d8d04 100644 --- a/src/ngx_http_lua_logby.c +++ b/src/ngx_http_lua_logby.c @@ -69,6 +69,9 @@ ngx_http_lua_log_handler(ngx_http_request_t *r) #if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM) ngx_uint_t trim_cycle, trim_nreq; ngx_http_lua_main_conf_t *lmcf; +#if (NGX_DEBUG) + ngx_int_t trim_ret; +#endif #endif ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; @@ -88,8 +91,9 @@ ngx_http_lua_log_handler(ngx_http_request_t *r) lmcf->malloc_trim_req_count = 0; #if (NGX_DEBUG) + trim_ret = malloc_trim(1); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "malloc_trim(1) returned %d", malloc_trim(1)); + "malloc_trim(1) returned %d", trim_ret); #else (void) malloc_trim(1); #endif @@ -145,6 +149,10 @@ ngx_http_lua_log_handler_inline(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->log_src_ref = LUA_REFNIL; + } + /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, llcf->log_src.value.data, @@ -184,6 +192,10 @@ ngx_http_lua_log_handler_file(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->log_src_ref = LUA_REFNIL; + } + /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, &llcf->log_src_ref, diff --git a/src/ngx_http_lua_misc.c b/src/ngx_http_lua_misc.c index 2300aa3e4c..8b74167161 100644 --- a/src/ngx_http_lua_misc.c +++ b/src/ngx_http_lua_misc.c @@ -64,8 +64,11 @@ ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r) int -ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status) +ngx_http_lua_ffi_set_resp_status_and_reason(ngx_http_request_t *r, int status, + const char *reason, size_t reason_len) { + u_char *buf; + if (r->connection->fd == (ngx_socket_t) -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } @@ -77,6 +80,14 @@ ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status) return NGX_DECLINED; } + /* per RFC-7230 sec 3.1.2, the status line must be 3 digits, it also makes + * buffer size calculation easier */ + if (status < 100 || status > 999) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid HTTP status code %d", status); + return NGX_DECLINED; + } + r->headers_out.status = status; if (r->err_status) { @@ -91,6 +102,18 @@ ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status) ngx_str_set(&r->headers_out.status_line, "101 Switching Protocols"); + } else if (reason != NULL && reason_len > 0) { + reason_len += 4; /* "ddd " */ + buf = ngx_palloc(r->pool, reason_len); + if (buf == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no memory"); + return NGX_DECLINED; + } + + ngx_snprintf(buf, reason_len, "%d %s", status, reason); + r->headers_out.status_line.len = reason_len; + r->headers_out.status_line.data = buf; + } else { r->headers_out.status_line.len = 0; } @@ -99,6 +122,24 @@ ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status) } +int +ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status) +{ + return ngx_http_lua_ffi_set_resp_status_and_reason(r, status, NULL, 0); +} + + +int +ngx_http_lua_ffi_req_is_internal(ngx_http_request_t *r) +{ + if (r->connection->fd == (ngx_socket_t) -1) { + return NGX_HTTP_LUA_FFI_BAD_CONTEXT; + } + + return r->internal; +} + + int ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r) { diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 7358a95639..16726abbe1 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -14,6 +14,7 @@ #include "ngx_http_lua_directive.h" #include "ngx_http_lua_capturefilter.h" #include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_server_rewriteby.h" #include "ngx_http_lua_rewriteby.h" #include "ngx_http_lua_accessby.h" #include "ngx_http_lua_logby.h" @@ -26,11 +27,20 @@ #include "ngx_http_lua_probe.h" #include "ngx_http_lua_semaphore.h" #include "ngx_http_lua_balancer.h" +#include "ngx_http_lua_ssl_client_helloby.h" #include "ngx_http_lua_ssl_certby.h" #include "ngx_http_lua_ssl_session_storeby.h" #include "ngx_http_lua_ssl_session_fetchby.h" + +#ifdef HAVE_PROXY_SSL_PATCH +#include "ngx_http_lua_proxy_ssl_verifyby.h" +#endif + #include "ngx_http_lua_headers.h" +#include "ngx_http_lua_headers_out.h" +#if !(NGX_WIN32) #include "ngx_http_lua_pipe.h" +#endif static void *ngx_http_lua_create_main_conf(ngx_conf_t *cf); @@ -45,11 +55,25 @@ static char *ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, static ngx_int_t ngx_http_lua_init(ngx_conf_t *cf); static char *ngx_http_lua_lowat_check(ngx_conf_t *cf, void *post, void *data); #if (NGX_HTTP_SSL) +static ngx_int_t ngx_http_lua_merge_ssl(ngx_conf_t *cf, + ngx_http_lua_loc_conf_t *conf, ngx_http_lua_loc_conf_t *prev); static ngx_int_t ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf); +static void key_log_callback(const ngx_ssl_conn_t *ssl_conn, + const char *line); +static void ngx_http_lua_ssl_cleanup_key_log(void *data); +static ngx_int_t ngx_http_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_str_t *file); +#if (nginx_version >= 1019004) +static char *ngx_http_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); +#endif #endif static char *ngx_http_lua_malloc_trim(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +#if (NGX_PCRE2) +extern void ngx_http_lua_regex_cleanup(void *data); +#endif static ngx_conf_post_t ngx_http_lua_lowat_post = @@ -73,6 +97,11 @@ static ngx_conf_bitmask_t ngx_http_lua_ssl_protocols[] = { { ngx_null_string, 0 } }; +#if (nginx_version >= 1019004) +static ngx_conf_post_t ngx_http_lua_ssl_conf_command_post = + { ngx_http_lua_ssl_conf_command_check }; +#endif + #endif @@ -280,6 +309,22 @@ static ngx_command_t ngx_http_lua_cmds[] = { (void *) ngx_http_lua_filter_set_by_lua_file }, #endif + /* server_rewrite_by_lua_block { } */ + { ngx_string("server_rewrite_by_lua_block"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_lua_server_rewrite_by_lua_block, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + (void *) ngx_http_lua_server_rewrite_handler_inline }, + + /* server_rewrite_by_lua_file filename; */ + { ngx_string("server_rewrite_by_lua_file"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_lua_server_rewrite_by_lua, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + (void *) ngx_http_lua_server_rewrite_handler_file }, + /* rewrite_by_lua "" */ { ngx_string("rewrite_by_lua"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF @@ -461,6 +506,13 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, (void *) ngx_http_lua_balancer_handler_file }, + { ngx_string("balancer_keepalive"), + NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, + ngx_http_lua_balancer_keepalive, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_lua_srv_conf_t, balancer.max_cached), + NULL }, + { ngx_string("lua_socket_keepalive_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, @@ -557,6 +609,20 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, ssl_ciphers), NULL }, + { ngx_string("ssl_client_hello_by_lua_block"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_lua_ssl_client_hello_by_lua_block, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + (void *) ngx_http_lua_ssl_client_hello_handler_inline }, + + { ngx_string("ssl_client_hello_by_lua_file"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_lua_ssl_client_hello_by_lua, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + (void *) ngx_http_lua_ssl_client_hello_handler_file }, + { ngx_string("ssl_certificate_by_lua_block"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_http_lua_ssl_cert_by_lua_block, @@ -599,6 +665,30 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, (void *) ngx_http_lua_ssl_sess_fetch_handler_file }, +#ifdef HAVE_PROXY_SSL_PATCH + /* same context as proxy_pass directive */ + { ngx_string("proxy_ssl_verify_by_lua_block"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_lua_proxy_ssl_verify_by_lua_block, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + (void *) ngx_http_lua_proxy_ssl_verify_handler_inline }, + + { ngx_string("proxy_ssl_verify_by_lua_file"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_lua_proxy_ssl_verify_by_lua, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + (void *) ngx_http_lua_proxy_ssl_verify_handler_file }, + + { ngx_string("lua_upstream_skip_openssl_default_verify"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, upstream_skip_openssl_default_verify), + NULL }, +#endif + { ngx_string("lua_ssl_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -606,6 +696,20 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, ssl_verify_depth), NULL }, + { ngx_string("lua_ssl_certificate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_certificates), + NULL }, + + { ngx_string("lua_ssl_certificate_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_certificate_keys), + NULL }, + { ngx_string("lua_ssl_trusted_certificate"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -620,6 +724,21 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, ssl_crl), NULL }, + { ngx_string("lua_ssl_key_log"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_key_log), + NULL }, + +#if (nginx_version >= 1019004) + { ngx_string("lua_ssl_conf_command"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_conf_commands), + &ngx_http_lua_ssl_conf_command_post }, +#endif #endif /* NGX_HTTP_SSL */ { ngx_string("lua_malloc_trim"), @@ -629,6 +748,13 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, NULL }, + { ngx_string("lua_worker_thread_vm_pool_size"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_lua_main_conf_t, worker_thread_vm_pool_size), + NULL }, + ngx_null_command }; @@ -713,6 +839,16 @@ ngx_http_lua_init(ngx_conf_t *cf) cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + if (lmcf->requires_server_rewrite) { + h = ngx_array_push( + &cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_lua_server_rewrite_handler; + } + if (lmcf->requires_rewrite) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); if (h == NULL) { @@ -772,6 +908,17 @@ ngx_http_lua_init(ngx_conf_t *cf) cln->data = lmcf; cln->handler = ngx_http_lua_sema_mm_cleanup; +#if (NGX_PCRE2) + /* add the cleanup of pcre2 regex */ + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->data = lmcf; + cln->handler = ngx_http_lua_regex_cleanup; +#endif + #ifdef HAVE_NGX_LUA_PIPE ngx_http_lua_pipe_init(); #endif @@ -924,6 +1071,13 @@ ngx_http_lua_create_main_conf(ngx_conf_t *cf) * lmcf->shm_zones = NULL; * lmcf->init_handler = NULL; * lmcf->init_src = { 0, NULL }; + * lmcf->init_chunkname = NULL; + * lmcf->init_worker_handler = NULL; + * lmcf->init_worker_src = { 0, NULL }; + * lmcf->init_worker_chunkname = NULL; + * lmcf->exit_worker_handler = NULL; + * lmcf->exit_worker_src = { 0, NULL }; + * lmcf->exit_worker_chunkname = NULL; * lmcf->shm_zones_inited = 0; * lmcf->shdict_zones = NULL; * lmcf->preload_hooks = NULL; @@ -958,6 +1112,8 @@ ngx_http_lua_create_main_conf(ngx_conf_t *cf) return NULL; } + lmcf->worker_thread_vm_pool_size = NGX_CONF_UNSET; + dd("nginx Lua module main config structure initialized!"); return lmcf; @@ -1041,6 +1197,19 @@ ngx_http_lua_init_main_conf(ngx_conf_t *cf, void *conf) } #endif + if (lmcf->worker_thread_vm_pool_size == NGX_CONF_UNSET_UINT) { + lmcf->worker_thread_vm_pool_size = 10; + } + + if (ngx_http_lua_init_builtin_headers_out(cf, lmcf) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "init header out error"); + + return NGX_CONF_ERROR; + } + + dd("init built in headers out hash size: %ld", + lmcf->builtin_headers_out.size); + return NGX_CONF_OK; } @@ -1056,31 +1225,49 @@ ngx_http_lua_create_srv_conf(ngx_conf_t *cf) } /* set by ngx_pcalloc: + * lscf->srv.ssl_client_hello_handler = NULL; + * lscf->srv.ssl_client_hello_src = { 0, NULL }; + * lscf->srv.ssl_client_hello_chunkname = NULL; + * lscf->srv.ssl_client_hello_src_key = NULL; + * * lscf->srv.ssl_cert_handler = NULL; * lscf->srv.ssl_cert_src = { 0, NULL }; + * lscf->srv.ssl_cert_chunkname = NULL; * lscf->srv.ssl_cert_src_key = NULL; * - * lscf->srv.ssl_session_store_handler = NULL; - * lscf->srv.ssl_session_store_src = { 0, NULL }; - * lscf->srv.ssl_session_store_src_key = NULL; + * lscf->srv.ssl_sess_store_handler = NULL; + * lscf->srv.ssl_sess_store_src = { 0, NULL }; + * lscf->srv.ssl_sess_store_chunkname = NULL; + * lscf->srv.ssl_sess_store_src_key = NULL; + * + * lscf->srv.ssl_sess_fetch_handler = NULL; + * lscf->srv.ssl_sess_fetch_src = { 0, NULL }; + * lscf->srv.ssl_sess_fetch_chunkname = NULL; + * lscf->srv.ssl_sess_fetch_src_key = NULL; * - * lscf->srv.ssl_session_fetch_handler = NULL; - * lscf->srv.ssl_session_fetch_src = { 0, NULL }; - * lscf->srv.ssl_session_fetch_src_key = NULL; + * lscf->srv.server_rewrite_handler = NULL; + * lscf->srv.server_rewrite_src = { 0, NULL }; + * lscf->srv.server_rewrite_chunkname = NULL; + * lscf->srv.server_rewrite_src_key = NULL; * + * lscf->balancer.original_init_upstream = NULL; + * lscf->balancer.original_init_peer = NULL; * lscf->balancer.handler = NULL; * lscf->balancer.src = { 0, NULL }; + * lscf->balancer.chunkname = NULL; * lscf->balancer.src_key = NULL; */ #if (NGX_HTTP_SSL) + lscf->srv.ssl_client_hello_src_ref = LUA_REFNIL; lscf->srv.ssl_cert_src_ref = LUA_REFNIL; lscf->srv.ssl_sess_store_src_ref = LUA_REFNIL; lscf->srv.ssl_sess_fetch_src_ref = LUA_REFNIL; #endif + lscf->srv.server_rewrite_src_ref = LUA_REFNIL; lscf->balancer.src_ref = LUA_REFNIL; - + lscf->balancer.max_cached = NGX_CONF_UNSET_UINT; return lscf; } @@ -1088,19 +1275,62 @@ ngx_http_lua_create_srv_conf(ngx_conf_t *cf) static char * ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { + ngx_http_lua_srv_conf_t *conf = child; + ngx_http_lua_srv_conf_t *prev = parent; + #if (NGX_HTTP_SSL) - ngx_http_lua_srv_conf_t *prev = parent; - ngx_http_lua_srv_conf_t *conf = child; ngx_http_ssl_srv_conf_t *sscf; dd("merge srv conf"); + if (conf->srv.ssl_client_hello_src.len == 0) { + conf->srv.ssl_client_hello_src = prev->srv.ssl_client_hello_src; + conf->srv.ssl_client_hello_src_ref = prev->srv.ssl_client_hello_src_ref; + conf->srv.ssl_client_hello_src_key = prev->srv.ssl_client_hello_src_key; + conf->srv.ssl_client_hello_handler = prev->srv.ssl_client_hello_handler; + conf->srv.ssl_client_hello_chunkname + = prev->srv.ssl_client_hello_chunkname; + } + + if (conf->srv.ssl_client_hello_src.len) { + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); + if (sscf == NULL || sscf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl configured for the server"); + + return NGX_CONF_ERROR; + } +#ifdef LIBRESSL_VERSION_NUMBER + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support by ssl_client_hello_by_lua*"); + return NGX_CONF_ERROR; + +#else + +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + + SSL_CTX_set_client_hello_cb(sscf->ssl.ctx, + ngx_http_lua_ssl_client_hello_handler, + NULL); + +#else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "OpenSSL too old to support " + "ssl_client_hello_by_lua*"); + return NGX_CONF_ERROR; + +#endif +#endif + } + if (conf->srv.ssl_cert_src.len == 0) { conf->srv.ssl_cert_src = prev->srv.ssl_cert_src; conf->srv.ssl_cert_src_ref = prev->srv.ssl_cert_src_ref; conf->srv.ssl_cert_src_key = prev->srv.ssl_cert_src_key; conf->srv.ssl_cert_handler = prev->srv.ssl_cert_handler; + conf->srv.ssl_cert_chunkname = prev->srv.ssl_cert_chunkname; } if (conf->srv.ssl_cert_src.len) { @@ -1140,6 +1370,7 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) conf->srv.ssl_sess_store_src_ref = prev->srv.ssl_sess_store_src_ref; conf->srv.ssl_sess_store_src_key = prev->srv.ssl_sess_store_src_key; conf->srv.ssl_sess_store_handler = prev->srv.ssl_sess_store_handler; + conf->srv.ssl_sess_store_chunkname = prev->srv.ssl_sess_store_chunkname; } if (conf->srv.ssl_sess_store_src.len) { @@ -1163,6 +1394,7 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) conf->srv.ssl_sess_fetch_src_ref = prev->srv.ssl_sess_fetch_src_ref; conf->srv.ssl_sess_fetch_src_key = prev->srv.ssl_sess_fetch_src_key; conf->srv.ssl_sess_fetch_handler = prev->srv.ssl_sess_fetch_handler; + conf->srv.ssl_sess_fetch_chunkname = prev->srv.ssl_sess_fetch_chunkname; } if (conf->srv.ssl_sess_fetch_src.len) { @@ -1182,6 +1414,16 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } #endif /* NGX_HTTP_SSL */ + + if (conf->srv.server_rewrite_src.value.len == 0) { + conf->srv.server_rewrite_src = prev->srv.server_rewrite_src; + conf->srv.server_rewrite_src_ref = prev->srv.server_rewrite_src_ref; + conf->srv.server_rewrite_src_key = prev->srv.server_rewrite_src_key; + conf->srv.server_rewrite_handler = prev->srv.server_rewrite_handler; + conf->srv.server_rewrite_chunkname + = prev->srv.server_rewrite_chunkname; + } + return NGX_CONF_OK; } @@ -1199,31 +1441,45 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) /* set by ngx_pcalloc: * conf->access_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->access_src_key = NULL + * conf->access_handler = NULL; + * conf->access_chunkname = NULL; + * * conf->rewrite_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->rewrite_src_key = NULL; * conf->rewrite_handler = NULL; + * conf->rewrite_chunkname = NULL; * * conf->content_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->content_src_key = NULL; * conf->content_handler = NULL; + * conf->content_chunkname = NULL; * * conf->log_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->log_src_key = NULL; * conf->log_handler = NULL; + * conf->log_chunkname = NULL; * * conf->header_filter_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->header_filter_src_key = NULL; * conf->header_filter_handler = NULL; + * conf->header_filter_chunkname = NULL; * * conf->body_filter_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->body_filter_src_key = NULL; * conf->body_filter_handler = NULL; + * conf->body_filter_chunkname = NULL; * * conf->ssl = 0; * conf->ssl_protocols = 0; * conf->ssl_ciphers = { 0, NULL }; * conf->ssl_trusted_certificate = { 0, NULL }; * conf->ssl_crl = { 0, NULL }; + * conf->ssl_key_log = { 0, NULL }; + * + * conf->proxy_ssl_verify_handler = NULL; + * conf->proxy_ssl_verify_src = { 0, NULL }; + * conf->proxy_ssl_verify_chunkname = NULL; + * conf->proxy_ssl_verify_src_key = NULL; */ conf->force_read_body = NGX_CONF_UNSET; @@ -1252,6 +1508,15 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) #if (NGX_HTTP_SSL) conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; + conf->ssl_certificates = NGX_CONF_UNSET_PTR; + conf->ssl_certificate_keys = NGX_CONF_UNSET_PTR; +#if (nginx_version >= 1019004) + conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; +#endif +#ifdef HAVE_PROXY_SSL_PATCH + conf->proxy_ssl_verify_src_ref = LUA_REFNIL; + conf->upstream_skip_openssl_default_verify = NGX_CONF_UNSET; +#endif #endif return conf; @@ -1301,6 +1566,7 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) conf->header_filter_handler = prev->header_filter_handler; conf->header_filter_src_ref = prev->header_filter_src_ref; conf->header_filter_src_key = prev->header_filter_src_key; + conf->header_filter_chunkname = prev->header_filter_chunkname; } if (conf->body_filter_src.value.len == 0) { @@ -1308,23 +1574,61 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) conf->body_filter_handler = prev->body_filter_handler; conf->body_filter_src_ref = prev->body_filter_src_ref; conf->body_filter_src_key = prev->body_filter_src_key; + conf->body_filter_chunkname = prev->body_filter_chunkname; } #if (NGX_HTTP_SSL) + if (ngx_http_lua_merge_ssl(cf, conf, prev) != NGX_OK) { + return NGX_CONF_ERROR; + } + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3 + (NGX_CONF_BITMASK_SET |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2)); + |NGX_SSL_TLSv1_2 +#ifdef NGX_SSL_TLSv1_3 + |NGX_SSL_TLSv1_3 +#endif + )); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); ngx_conf_merge_uint_value(conf->ssl_verify_depth, prev->ssl_verify_depth, 1); + ngx_conf_merge_ptr_value(conf->ssl_certificates, + prev->ssl_certificates, NULL); + ngx_conf_merge_ptr_value(conf->ssl_certificate_keys, + prev->ssl_certificate_keys, NULL); ngx_conf_merge_str_value(conf->ssl_trusted_certificate, prev->ssl_trusted_certificate, ""); ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, ""); + ngx_conf_merge_str_value(conf->ssl_key_log, prev->ssl_key_log, ""); + +#if (nginx_version >= 1019004) + ngx_conf_merge_ptr_value(conf->ssl_conf_commands, prev->ssl_conf_commands, + NULL); +#endif + +#ifdef HAVE_PROXY_SSL_PATCH + if (conf->proxy_ssl_verify_src.len == 0) { + conf->proxy_ssl_verify_src = prev->proxy_ssl_verify_src; + conf->proxy_ssl_verify_handler = prev->proxy_ssl_verify_handler; + conf->proxy_ssl_verify_src_ref = prev->proxy_ssl_verify_src_ref; + conf->proxy_ssl_verify_src_key = prev->proxy_ssl_verify_src_key; + conf->proxy_ssl_verify_chunkname = prev->proxy_ssl_verify_chunkname; + } + + if (conf->proxy_ssl_verify_src.len) { + if (ngx_http_lua_proxy_ssl_verify_set_callback(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + ngx_conf_merge_value(conf->upstream_skip_openssl_default_verify, + prev->upstream_skip_openssl_default_verify, 0); +#endif if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; @@ -1370,17 +1674,78 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #if (NGX_HTTP_SSL) +static ngx_int_t +ngx_http_lua_merge_ssl(ngx_conf_t *cf, + ngx_http_lua_loc_conf_t *conf, ngx_http_lua_loc_conf_t *prev) +{ + ngx_uint_t preserve; + + if (conf->ssl_protocols == 0 + && conf->ssl_ciphers.data == NULL + && conf->ssl_verify_depth == NGX_CONF_UNSET_UINT + && conf->ssl_certificates == NGX_CONF_UNSET_PTR + && conf->ssl_certificate_keys == NGX_CONF_UNSET_PTR + && conf->ssl_trusted_certificate.data == NULL + && conf->ssl_crl.data == NULL + && conf->ssl_key_log.data == NULL +#if (nginx_version >= 1019004) + && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR +#endif + ) + { + if (prev->ssl) { + conf->ssl = prev->ssl; + return NGX_OK; + } + + preserve = 1; + + } else { + preserve = 0; + } + + conf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); + if (conf->ssl == NULL) { + return NGX_ERROR; + } + + conf->ssl->log = cf->log; + + /* + * special handling to preserve conf->ssl_* in the "http" section + * to inherit it to all servers + */ + + if (preserve) { + prev->ssl = conf->ssl; + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf) { ngx_pool_cleanup_t *cln; - llcf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); - if (llcf->ssl == NULL) { - return NGX_ERROR; + if (llcf->ssl->ctx) { + return NGX_OK; } - llcf->ssl->log = cf->log; + if (llcf->ssl_certificates) { + if (llcf->ssl_certificate_keys == NULL + || llcf->ssl_certificate_keys->nelts + < llcf->ssl_certificates->nelts) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"lua_ssl_certificate_key\" is defined " + "for certificate \"%V\"", + ((ngx_str_t *) llcf->ssl_certificates->elts) + + llcf->ssl_certificates->nelts - 1); + return NGX_ERROR; + } + } if (ngx_ssl_create(llcf->ssl, llcf->ssl_protocols, NULL) != NGX_OK) { return NGX_ERROR; @@ -1388,6 +1753,7 @@ ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf) cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { + ngx_ssl_cleanup_ctx(llcf->ssl); return NGX_ERROR; } @@ -1404,6 +1770,16 @@ ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf) return NGX_ERROR; } + if (llcf->ssl_certificates + && ngx_ssl_certificates(cf, llcf->ssl, + llcf->ssl_certificates, + llcf->ssl_certificate_keys, + NULL) + != NGX_OK) + { + return NGX_ERROR; + } + if (llcf->ssl_trusted_certificate.len && ngx_ssl_trusted_certificate(cf, llcf->ssl, &llcf->ssl_trusted_certificate, @@ -1419,9 +1795,131 @@ ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf) return NGX_ERROR; } + if (ngx_http_lua_ssl_key_log(cf, llcf->ssl, &llcf->ssl_key_log) + != NGX_OK) + { + return NGX_ERROR; + } + +#if (nginx_version >= 1019004) + if (ngx_ssl_conf_commands(cf, llcf->ssl, llcf->ssl_conf_commands) + != NGX_OK) + { + return NGX_ERROR; + } +#endif + return NGX_OK; } + +static void +key_log_callback(const ngx_ssl_conn_t *ssl_conn, const char *line) +{ + ngx_http_lua_ssl_key_log_t *ssl_key_log; + ngx_connection_t *c; + + ssl_key_log = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn), + ngx_http_lua_ssl_key_log_index); + if (ssl_key_log == NULL) { + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0, "get ssl key log failed"); + + return; + } + + (void) ngx_write_fd(ssl_key_log->fd, (void *) line, ngx_strlen(line)); + (void) ngx_write_fd(ssl_key_log->fd, (void *) "\n", 1); +} + + +static void +ngx_http_lua_ssl_cleanup_key_log(void *data) +{ + ngx_http_lua_ssl_key_log_t *ssl_key_log = data; + + if (ngx_close_file(ssl_key_log->fd) == NGX_FILE_ERROR) { + ngx_ssl_error(NGX_LOG_ALERT, ssl_key_log->ssl->log, 0, + ngx_close_file_n "(\"%V\") failed", ssl_key_log->name); + } +} + + +static ngx_int_t +ngx_http_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) +{ + ngx_fd_t fd; + ngx_http_lua_ssl_key_log_t *ssl_key_log; + ngx_pool_cleanup_t *cln; + + if (!file->len) { + return NGX_OK; + } + + if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_ERROR; + } + + /* + * append so that existing keylog file contents can be preserved + */ + fd = ngx_open_file(file->data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + if (fd == NGX_INVALID_FILE) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_open_file_n + "(\"%V\") failed", file); + return NGX_ERROR; + } + + ssl_key_log = ngx_palloc(cf->pool, sizeof(ngx_http_lua_ssl_key_log_t)); + if (ssl_key_log == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "ngx_pcalloc() failed"); + return NGX_ERROR; + } + + ssl_key_log->ssl = ssl; + ssl_key_log->fd = fd; + ssl_key_log->name = *file; + + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_http_lua_ssl_key_log_index, + ssl_key_log) == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + ngx_http_lua_ssl_cleanup_key_log(ssl_key_log); + return NGX_ERROR; + } + + cln->handler = ngx_http_lua_ssl_cleanup_key_log; + cln->data = ssl_key_log; + + SSL_CTX_set_keylog_callback(ssl->ctx, key_log_callback); + + return NGX_OK; +} + + +#if (nginx_version >= 1019004) +static char * +ngx_http_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#endif + + return NGX_CONF_OK; +} +#endif + #endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_output.c b/src/ngx_http_lua_output.c index c0aadffb4f..8681947263 100644 --- a/src/ngx_http_lua_output.c +++ b/src/ngx_http_lua_output.c @@ -63,6 +63,7 @@ ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); @@ -244,6 +245,11 @@ ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) return 2; } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "%s has %sbusy bufs", + newline ? "lua say response" : "lua print response", + ctx->busy_bufs != NULL ? "" : "no "); + dd("downstream write: %d, buf len: %d", (int) rc, (int) (b->last - b->pos)); @@ -494,6 +500,7 @@ ngx_http_lua_ngx_flush(lua_State *L) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); @@ -648,6 +655,7 @@ ngx_http_lua_ngx_eof(lua_State *L) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); @@ -710,19 +718,18 @@ ngx_http_lua_ngx_send_headers(lua_State *L) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); - if (!r->header_sent && !ctx->header_sent) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua send headers"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua send headers"); - rc = ngx_http_lua_send_header_if_needed(r, ctx); - if (rc == NGX_ERROR || rc > NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "nginx output filter error"); - return 2; - } + rc = ngx_http_lua_send_header_if_needed(r, ctx); + if (rc == NGX_ERROR || rc > NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "nginx output filter error"); + return 2; } lua_pushinteger(L, 1); diff --git a/src/ngx_http_lua_pcrefix.c b/src/ngx_http_lua_pcrefix.c index 562847a639..5693b5495c 100644 --- a/src/ngx_http_lua_pcrefix.c +++ b/src/ngx_http_lua_pcrefix.c @@ -18,15 +18,61 @@ static ngx_pool_t *ngx_http_lua_pcre_pool = NULL; + +#if (NGX_PCRE2) +static ngx_uint_t ngx_regex_direct_alloc; +#else static void *(*old_pcre_malloc)(size_t); static void (*old_pcre_free)(void *ptr); +#endif /* XXX: work-around to nginx regex subsystem, must init a memory pool * to use PCRE functions. As PCRE still has memory-leaking problems, * and nginx overwrote pcre_malloc/free hooks with its own static * functions, so nobody else can reuse nginx regex subsystem... */ -static void * +#if (NGX_PCRE2) + +void * +ngx_http_lua_pcre_malloc(size_t size, void *data) +{ + dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); + + if (ngx_http_lua_pcre_pool) { + return ngx_palloc(ngx_http_lua_pcre_pool, size); + } + + if (ngx_regex_direct_alloc) { + return ngx_alloc(size, ngx_cycle->log); + } + + fprintf(stderr, "error: lua pcre malloc failed due to empty pcre pool"); + + return NULL; +} + + +void +ngx_http_lua_pcre_free(void *ptr, void *data) +{ + dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); + + if (ngx_http_lua_pcre_pool) { + ngx_pfree(ngx_http_lua_pcre_pool, ptr); + return; + } + + if (ngx_regex_direct_alloc) { + ngx_free(ptr); + return; + } + + fprintf(stderr, "error: lua pcre free failed due to empty pcre pool"); +} + +#else + +void * ngx_http_lua_pcre_malloc(size_t size) { dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); @@ -54,6 +100,41 @@ ngx_http_lua_pcre_free(void *ptr) fprintf(stderr, "error: lua pcre free failed due to empty pcre pool"); } +#endif + + +#if (NGX_PCRE2) + +ngx_pool_t * +ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool) +{ + ngx_pool_t *old_pool; + + dd("lua pcre pool was %p", ngx_http_lua_pcre_pool); + + ngx_regex_direct_alloc = (pool == NULL) ? 1 : 0; + + old_pool = ngx_http_lua_pcre_pool; + ngx_http_lua_pcre_pool = pool; + + dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); + + return old_pool; +} + + +void +ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool) +{ + dd("lua pcre pool was %p", ngx_http_lua_pcre_pool); + + ngx_http_lua_pcre_pool = old_pool; + ngx_regex_direct_alloc = 0; + + dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); +} + +#else ngx_pool_t * ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool) @@ -101,6 +182,7 @@ ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool) } } +#endif #endif /* NGX_PCRE */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_pcrefix.h b/src/ngx_http_lua_pcrefix.h index 80f29f9e75..e1097b72eb 100644 --- a/src/ngx_http_lua_pcrefix.h +++ b/src/ngx_http_lua_pcrefix.h @@ -13,8 +13,15 @@ #if (NGX_PCRE) + ngx_pool_t *ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool); void ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool); + +#if NGX_PCRE2 +void *ngx_http_lua_pcre_malloc(size_t size, void *data); +void ngx_http_lua_pcre_free(void *ptr, void *data); +#endif + #endif diff --git a/src/ngx_http_lua_pipe.c b/src/ngx_http_lua_pipe.c index c555d7bc94..8c0884bcc2 100644 --- a/src/ngx_http_lua_pipe.c +++ b/src/ngx_http_lua_pipe.c @@ -78,10 +78,15 @@ static void ngx_http_lua_pipe_proc_read_stdout_cleanup(void *data); static void ngx_http_lua_pipe_proc_read_stderr_cleanup(void *data); static void ngx_http_lua_pipe_proc_write_cleanup(void *data); static void ngx_http_lua_pipe_proc_wait_cleanup(void *data); +static void ngx_http_lua_pipe_reap_pids(ngx_event_t *ev); +static void ngx_http_lua_pipe_reap_timer_handler(ngx_event_t *ev); +void ngx_http_lua_ffi_pipe_proc_destroy( + ngx_http_lua_ffi_pipe_proc_t *proc); static ngx_rbtree_t ngx_http_lua_pipe_rbtree; static ngx_rbtree_node_t ngx_http_lua_pipe_proc_sentinel; +static ngx_event_t ngx_reap_pid_event; #if (NGX_HTTP_LUA_HAVE_SIGNALFD) @@ -160,6 +165,15 @@ ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle) struct sigaction sa; #endif + ngx_reap_pid_event.handler = ngx_http_lua_pipe_reap_timer_handler; + ngx_reap_pid_event.log = cycle->log; + ngx_reap_pid_event.data = cycle; + ngx_reap_pid_event.cancelable = 1; + + if (!ngx_reap_pid_event.timer_set) { + ngx_add_timer(&ngx_reap_pid_event, 1000); + } + #if (NGX_HTTP_LUA_HAVE_SIGNALFD) if (sigemptyset(&set) != 0) { ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, @@ -351,11 +365,7 @@ static void ngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev) { int n; - int status; - ngx_pid_t pid; ngx_connection_t *c = ev->data; - ngx_rbtree_node_t *node; - ngx_http_lua_pipe_node_t *pipe_node; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "lua pipe reaping children"); @@ -377,72 +387,99 @@ ngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev) break; } - for ( ;; ) { - pid = waitpid(-1, &status, WNOHANG); + ngx_http_lua_pipe_reap_pids(ev); + } +} - if (pid == 0) { - break; - } - if (pid < 0) { - if (ngx_errno != NGX_ECHILD) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "lua pipe waitpid failed"); - } +static void +ngx_http_lua_pipe_reap_pids(ngx_event_t *ev) +{ + int status; + ngx_pid_t pid; + ngx_rbtree_node_t *node; + ngx_http_lua_pipe_node_t *pipe_node; - break; - } + for ( ;; ) { + pid = waitpid(-1, &status, WNOHANG); - /* This log is ported from Nginx's signal handler since we override - * or block it in this implementation. */ - ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, - "signal %d (SIGCHLD) received from %P", - SIGCHLD, pid); + if (pid == 0) { + break; + } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua pipe SIGCHLD fd read pid:%P status:%d", pid, - status); - - node = ngx_http_lua_pipe_lookup_pid(pid); - if (node != NULL) { - pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; - if (pipe_node->wait_co_ctx != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua pipe resume process:%p waiting for %P", - pipe_node->proc, pid); - - /* - * We need the extra parentheses around the first argument - * of ngx_post_event() just to work around macro issues in - * nginx cores older than 1.7.12 (exclusive). - */ - ngx_post_event((&pipe_node->wait_co_ctx->sleep), - &ngx_posted_events); - } + if (pid < 0) { + if (ngx_errno != NGX_ECHILD) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe waitpid failed"); + } - pipe_node->proc->pipe->dead = 1; + break; + } - if (WIFSIGNALED(status)) { - pipe_node->status = WTERMSIG(status); - pipe_node->reason_code = REASON_SIGNAL_CODE; + /* This log is ported from Nginx's signal handler since we override + * or block it in this implementation. */ + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "signal %d (SIGCHLD) received from %P", + SIGCHLD, pid); - } else if (WIFEXITED(status)) { - pipe_node->status = WEXITSTATUS(status); - pipe_node->reason_code = REASON_EXIT_CODE; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe SIGCHLD fd read pid:%P status:%d", pid, + status); + + node = ngx_http_lua_pipe_lookup_pid(pid); + if (node != NULL) { + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + if (pipe_node->wait_co_ctx != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe resume process:%p waiting for %P", + pipe_node->proc, pid); + + /* + * We need the extra parentheses around the first argument + * of ngx_post_event() just to work around macro issues in + * nginx cores older than 1.7.12 (exclusive). + */ + ngx_post_event((&pipe_node->wait_co_ctx->sleep), + &ngx_posted_events); + } - } else { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, - "lua pipe unknown exit status %d from " - "process %P", status, pid); - pipe_node->status = status; - pipe_node->reason_code = REASON_UNKNOWN_CODE; - } + /* TODO: we should proactively close and free up the pipe after + * the user consume all the data in the pipe. + */ + pipe_node->proc->pipe->dead = 1; + + if (WIFSIGNALED(status)) { + pipe_node->status = WTERMSIG(status); + pipe_node->reason_code = REASON_SIGNAL_CODE; + + } else if (WIFEXITED(status)) { + pipe_node->status = WEXITSTATUS(status); + pipe_node->reason_code = REASON_EXIT_CODE; + + } else { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "lua pipe unknown exit status %d from " + "process %P", status, pid); + pipe_node->status = status; + pipe_node->reason_code = REASON_UNKNOWN_CODE; } } } } +static void +ngx_http_lua_pipe_reap_timer_handler(ngx_event_t *ev) +{ + ngx_http_lua_pipe_reap_pids(ev); + + if (!ngx_exiting) { + ngx_add_timer(&ngx_reap_pid_event, 1000); + ngx_reap_pid_event.timedout = 0; + } +} + + static ssize_t ngx_http_lua_pipe_fd_read(ngx_connection_t *c, u_char *buf, size_t size) { @@ -545,8 +582,25 @@ ngx_http_lua_pipe_fd_write(ngx_connection_t *c, u_char *buf, size_t size) } +#if !(NGX_HTTP_LUA_HAVE_EXECVPE) +static int +ngx_http_lua_execvpe(const char *program, char * const argv[], + char * const envp[]) +{ + int rc; + char **saved = environ; + + environ = (char **) envp; + rc = execvp(program, argv); + environ = saved; + return rc; +} +#endif + + int -ngx_http_lua_ffi_pipe_spawn(ngx_http_lua_ffi_pipe_proc_t *proc, +ngx_http_lua_ffi_pipe_spawn(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, const char *file, const char **argv, int merge_stderr, size_t buffer_size, const char **environ, u_char *errbuf, size_t *errbuf_size) { @@ -566,17 +620,9 @@ ngx_http_lua_ffi_pipe_spawn(ngx_http_lua_ffi_pipe_proc_t *proc, ngx_http_lua_pipe_node_t *pipe_node; struct sigaction sa; ngx_http_lua_pipe_signal_t *sig; + ngx_pool_cleanup_t *cln; sigset_t set; -#if !(NGX_HTTP_LUA_HAVE_EXECVPE) - if (environ != NULL) { - *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, - "environ option not supported") - - errbuf; - return NGX_ERROR; - } -#endif - pool_size = ngx_align(NGX_MIN_POOL_SIZE + buffer_size * 2, NGX_POOL_ALIGNMENT); @@ -766,9 +812,30 @@ ngx_http_lua_ffi_pipe_spawn(ngx_http_lua_ffi_pipe_proc_t *proc, } } -#if (NGX_HTTP_LUA_HAVE_EXECVPE) + if (close(in[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe failed to close the in[0]"); + } + + if (close(out[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe failed to close the out[1]"); + } + + if (!merge_stderr) { + if (close(err[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe failed to close the err[1]"); + } + } + if (environ != NULL) { +#if (NGX_HTTP_LUA_HAVE_EXECVPE) if (execvpe(file, (char * const *) argv, (char * const *) environ) +#else + if (ngx_http_lua_execvpe(file, (char * const *) argv, + (char * const *) environ) +#endif == -1) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, @@ -784,14 +851,6 @@ ngx_http_lua_ffi_pipe_spawn(ngx_http_lua_ffi_pipe_proc_t *proc, } } -#else - if (execvp(file, (char * const *) argv) == -1) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "lua pipe child execvp() failed while executing %s", - file); - } -#endif - exit(EXIT_FAILURE); } @@ -849,6 +908,21 @@ ngx_http_lua_ffi_pipe_spawn(ngx_http_lua_ffi_pipe_proc_t *proc, pp->stderr_fd = stderr_fd; } + if (pp->cleanup == NULL) { + cln = ngx_pool_cleanup_add(r->pool, 0); + + if (cln == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + goto close_in_out_err_fd; + } + + cln->handler = (ngx_pool_cleanup_pt) ngx_http_lua_ffi_pipe_proc_destroy; + cln->data = proc; + pp->cleanup = &cln->handler; + pp->r = r; + } + node = (ngx_rbtree_node_t *) (pp + 1); node->key = pid; pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; @@ -1119,6 +1193,12 @@ ngx_http_lua_ffi_pipe_proc_destroy(ngx_http_lua_ffi_pipe_proc_t *proc) } } + if (pipe->cleanup != NULL) { + *pipe->cleanup = NULL; + ngx_http_lua_cleanup_free(pipe->r, pipe->cleanup); + pipe->cleanup = NULL; + } + ngx_http_lua_pipe_proc_finalize(proc); ngx_destroy_pool(pipe->pool); proc->pipe = NULL; @@ -1132,7 +1212,7 @@ ngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r, int rc; *ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { + if (*ctx == NULL) { return NGX_HTTP_LUA_FFI_NO_REQ_CTX; } diff --git a/src/ngx_http_lua_pipe.h b/src/ngx_http_lua_pipe.h index ecb86c8806..f1c9283526 100644 --- a/src/ngx_http_lua_pipe.h +++ b/src/ngx_http_lua_pipe.h @@ -57,6 +57,8 @@ struct ngx_http_lua_pipe_s { ngx_http_lua_pipe_ctx_t *stdout_ctx; ngx_http_lua_pipe_ctx_t *stderr_ctx; ngx_http_lua_pipe_retval_handler retval_handler; + ngx_http_cleanup_pt *cleanup; + ngx_http_request_t *r; size_t buffer_size; unsigned closed:1; unsigned dead:1; diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c new file mode 100644 index 0000000000..c8d783ab4b --- /dev/null +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -0,0 +1,877 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_HTTP_SSL) + + +#include "ngx_http_lua_cache.h" +#include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_ssl_module.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_directive.h" +#include "ngx_http_lua_ssl.h" + +#ifdef HAVE_PROXY_SSL_PATCH +#include "ngx_http_lua_proxy_ssl_verifyby.h" + + +#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x30000020uL) +static void ngx_http_lua_proxy_ssl_verify_done(void *data); +static void ngx_http_lua_proxy_ssl_verify_aborted(void *data); +#endif +static ngx_int_t ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, + ngx_http_request_t *r); + + +ngx_int_t +ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf) +{ + +#if defined(LIBRESSL_VERSION_NUMBER) + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support by proxy_ssl_verify_by_lua*"); + + return NGX_ERROR; + +#elif defined(OPENSSL_IS_BORINGSSL) + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "BoringSSL does not support by proxy_ssl_verify_by_lua*"); + + return NGX_ERROR; + +#else + + void *plcf; + ngx_http_upstream_conf_t *ucf; + ngx_ssl_t *ssl; + + /* + * Nginx doesn't export ngx_http_proxy_loc_conf_t, so we can't directly + * get plcf here, but the first member of plcf is ngx_http_upstream_conf_t + */ + plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); + ucf = plcf; + + ssl = ucf->ssl; + + if (!ssl->ctx) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "proxy_ssl_verify_by_lua* " + "should be used with proxy_pass https url"); + + return NGX_ERROR; + } + +#if (!defined SSL_ERROR_WANT_RETRY_VERIFY \ + || OPENSSL_VERSION_NUMBER < 0x30000020L) + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "OpenSSL too old to support " + "proxy_ssl_verify_by_lua*"); + + return NGX_ERROR; + +#else + + SSL_CTX_set_cert_verify_callback(ssl->ctx, + ngx_http_lua_proxy_ssl_verify_handler, + NULL); + return NGX_OK; + +#endif + +#endif +} + + +ngx_int_t +ngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadfile(r->connection->log, L, + llcf->proxy_ssl_verify_src.data, + &llcf->proxy_ssl_verify_src_ref, + llcf->proxy_ssl_verify_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r); +} + + +ngx_int_t +ngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, + llcf->proxy_ssl_verify_src.data, + llcf->proxy_ssl_verify_src.len, + &llcf->proxy_ssl_verify_src_ref, + llcf->proxy_ssl_verify_src_key, + (const char *) llcf->proxy_ssl_verify_chunkname); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r); +} + + +char * +ngx_http_lua_proxy_ssl_verify_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_http_lua_proxy_ssl_verify_by_lua; + cf->handler_conf = conf; + + rv = ngx_http_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#if defined(LIBRESSL_VERSION_NUMBER) + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support by proxy_ssl_verify_by_lua*"); + + return NGX_CONF_ERROR; + +#elif defined(OPENSSL_IS_BORINGSSL) + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "BoringSSL does not support by proxy_ssl_verify_by_lua*"); + + return NGX_CONF_ERROR; + +#else + +#if (!defined SSL_ERROR_WANT_RETRY_VERIFY \ + || OPENSSL_VERSION_NUMBER < 0x30000020L) + + /* SSL_set_retry_verify() was added in OpenSSL 3.0.2 */ + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 3.0.2 required but found " + OPENSSL_VERSION_TEXT); + + return NGX_CONF_ERROR; + +#else + + size_t chunkname_len; + u_char *chunkname; + u_char *cache_key = NULL; + u_char *name; + ngx_str_t *value; + ngx_http_lua_loc_conf_t *llcf = conf; + + /* must specify a concrete handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (llcf->proxy_ssl_verify_handler) { + return "is duplicate"; + } + + if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + llcf->proxy_ssl_verify_handler = + (ngx_http_lua_loc_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_http_lua_proxy_ssl_verify_handler_file) { + /* Lua code in an external file */ + + name = ngx_http_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + + llcf->proxy_ssl_verify_src.data = name; + llcf->proxy_ssl_verify_src.len = ngx_strlen(name); + + } else { + cache_key = ngx_http_lua_gen_chunk_cache_key(cf, + "proxy_ssl_verify_by_lua", + value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + + chunkname = ngx_http_lua_gen_chunk_name(cf, "proxy_ssl_verify_by_lua", + sizeof("proxy_ssl_verify_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + /* Don't eval nginx variables for inline lua code */ + llcf->proxy_ssl_verify_src = value[1]; + llcf->proxy_ssl_verify_chunkname = chunkname; + } + + llcf->proxy_ssl_verify_src_key = cache_key; + + return NGX_CONF_OK; + +#endif /* SSL_ERROR_WANT_RETRY_VERIFY */ + +#endif +} + + +int +ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) +{ +#if defined(LIBRESSL_VERSION_NUMBER) + ngx_connection_t *c; + + c = ngx_ssl_get_connection(ssl_conn); /* upstream connection */ + ngx_ssl_conn_t *ssl_conn; + + ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, + SSL_get_ex_data_X509_STORE_CTX_idx()); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "LibreSSL does not support by proxy_ssl_verify_by_lua*"); + + return 1; + +#elif defined(OPENSSL_IS_BORINGSSL) + ngx_connection_t *c; + ngx_ssl_conn_t *ssl_conn; + + ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, + SSL_get_ex_data_X509_STORE_CTX_idx()); + c = ngx_ssl_get_connection(ssl_conn); /* upstream connection */ + + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "BoringSSL does not support by proxy_ssl_verify_by_lua*"); + + return 1; +#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x30000020uL) + + ngx_connection_t *c; + ngx_ssl_conn_t *ssl_conn; + + ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, + SSL_get_ex_data_X509_STORE_CTX_idx()); + c = ngx_ssl_get_connection(ssl_conn); /* upstream connection */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "OpenSSL(< 3.0.2) does not support by proxy_ssl_verify_by_lua*"); + + return 1; + +#else + + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_request_t *r = NULL; + ngx_pool_cleanup_t *cln; + ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_ssl_ctx_t *cctx; + ngx_ssl_conn_t *ssl_conn; + + ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + c = ngx_ssl_get_connection(ssl_conn); /* upstream connection */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy ssl verify: connection reusable: %ud", c->reusable); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + dd("proxy ssl verify handler, cert-verify-ctx=%p", cctx); + + if (cctx && cctx->entered_proxy_ssl_verify_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_ssl_verify_by_lua: " + "cert verify callback exit code: %d", + cctx->exit_code); + + dd("lua proxy ssl verify done, finally"); + return cctx->exit_code; + } + + return SSL_set_retry_verify(ssl_conn); + } + + dd("first time"); + +#if (nginx_version < 1017009) + ngx_reusable_connection(c, 0); +#endif + + r = c->data; + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + + cctx->ctx_ref = LUA_NOREF; + } + + cctx->connection = c; + cctx->request = r; + cctx->x509_store = x509_store; + cctx->exit_code = 1; /* successful by default */ + cctx->original_request_count = r->main->count; + cctx->done = 0; + cctx->entered_proxy_ssl_verify_handler = 1; + cctx->pool = ngx_create_pool(128, c->log); + if (cctx->pool == NULL) { + goto failed; + } + + dd("setting cctx"); + + if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, + cctx) == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (llcf->upstream_skip_openssl_default_verify == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_ssl_verify_by_lua: openssl default verify"); + + rc = X509_verify_cert(x509_store); + if (rc == 0) { + goto failed; + } + } + + /* TODO honor lua_code_cache off */ + L = ngx_http_lua_get_lua_vm(r, NULL); + + c->log->action = "loading proxy ssl verify by lua"; + + rc = llcf->proxy_ssl_verify_handler(r, llcf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_ssl_verify_by_lua: handler return value: %i, " + "cert verify callback exit code: %d", rc, + cctx->exit_code); + + c->log->action = "proxy pass SSL handshaking"; + return cctx->exit_code; + } + + /* rc == NGX_DONE */ + + cln = ngx_pool_cleanup_add(cctx->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_http_lua_proxy_ssl_verify_done; + cln->data = cctx; + + if (cctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->data = cctx; + cctx->cleanup = &cln->handler; + } + + *cctx->cleanup = ngx_http_lua_proxy_ssl_verify_aborted; + + return SSL_set_retry_verify(ssl_conn); + +failed: + + if (cctx && cctx->pool) { + ngx_destroy_pool(cctx->pool); + } + + return 0; /* verify failure or error */ + +#endif +} + + +#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x30000020uL) +static void +ngx_http_lua_proxy_ssl_verify_done(void *data) +{ + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua proxy ssl verify done"); + + if (cctx->aborted) { + return; + } + + ngx_http_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + c = cctx->connection; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + c->log->action = "proxy pass SSL handshaking"; + + ngx_post_event(c->write, &ngx_posted_events); +} + + +static void +ngx_http_lua_proxy_ssl_verify_aborted(void *data) +{ + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua proxy ssl verify aborted"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, + "proxy_ssl_verify_by_lua: cert verify callback aborted"); + + cctx->aborted = 1; + cctx->connection->ssl = NULL; + cctx->exit_code = 0; + if (cctx->pool) { + ngx_destroy_pool(cctx->pool); + cctx->pool = NULL; + } +} +#endif + + +static ngx_int_t +ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_http_lua_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + ngx_http_upstream_t *u; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + } else { + dd("reset ctx"); + ngx_http_lua_reset_ctx(r, L, ctx); + } + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_http_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine to handle request"); + + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_http_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_http_lua_set_req(co, r); + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx); + + /* register request cleanup hooks */ + if (ctx->cleanup == NULL) { + u = r->upstream; + c = u->peer.connection; + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + cln = ngx_pool_cleanup_add(cctx->pool, 0); + if (cln == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + cln->handler = ngx_http_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + + ctx->context = NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY; + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_http_lua_finalize_request(r, rc); + return rc; +} + + +/* + * openssl's doc of SSL_CTX_set_cert_verify_callback: + * In any case a viable verification result value must + * be reflected in the error member of x509_store_ctx, + * which can be done using X509_STORE_CTX_set_error. + */ +int +ngx_http_lua_ffi_proxy_ssl_set_verify_result(ngx_http_request_t *r, + int verify_result, char **err) +{ +#if defined(LIBRESSL_VERSION_NUMBER) + + *err = "LibreSSL does not support this function"; + + return NGX_ERROR; + +#elif defined(OPENSSL_IS_BORINGSSL) + + *err = "BoringSSL does not support this function"; + + return NGX_ERROR; + +#else + +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx; + X509_STORE_CTX *x509_store; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + c = u->peer.connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad upstream connection"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + dd("get cctx session"); + + c = ngx_ssl_get_connection(ssl_conn); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + if (cctx == NULL) { + *err = "bad lua context"; + return NGX_ERROR; + } + + x509_store = cctx->x509_store; + + X509_STORE_CTX_set_error(x509_store, verify_result); + + return NGX_OK; +#else + *err = "OpenSSL too old to support this function"; + + return NGX_ERROR; +#endif + +#endif +} + + +int +ngx_http_lua_ffi_proxy_ssl_get_verify_result(ngx_http_request_t *r, char **err) +{ +#if defined(LIBRESSL_VERSION_NUMBER) + + *err = "LibreSSL does not support this function"; + + return NGX_ERROR; + +#elif defined(OPENSSL_IS_BORINGSSL) + + *err = "BoringSSL does not support this function"; + + return NGX_ERROR; + +#else + +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx; + X509_STORE_CTX *x509_store; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + c = u->peer.connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad upstream connection"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + dd("get cctx session"); + + c = ngx_ssl_get_connection(ssl_conn); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + if (cctx == NULL) { + *err = "bad lua context"; + return NGX_ERROR; + } + + x509_store = cctx->x509_store; + + return X509_STORE_CTX_get_error(x509_store); +#else + *err = "OpenSSL too old to support this function"; + + return NGX_ERROR; +#endif + +#endif +} + + +void +ngx_http_lua_ffi_proxy_ssl_free_verify_cert(void *cdata) +{ + X509 *cert = cdata; + + X509_free(cert); +} + + +void * +ngx_http_lua_ffi_proxy_ssl_get_verify_cert(ngx_http_request_t *r, char **err) +{ +#if defined(LIBRESSL_VERSION_NUMBER) + + *err = "LibreSSL does not support this function"; + + return NGX_ERROR; + +#elif defined(OPENSSL_IS_BORINGSSL) + + *err = "BoringSSL does not support this function"; + + return NGX_ERROR; + +#else + +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx; + X509_STORE_CTX *x509_store; + X509 *x509; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NULL; + } + + c = u->peer.connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad upstream connection"; + return NULL; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NULL; + } + + dd("get cctx session"); + + c = ngx_ssl_get_connection(ssl_conn); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + if (cctx == NULL) { + *err = "bad lua context"; + return NULL; + } + + x509_store = cctx->x509_store; + + x509 = X509_STORE_CTX_get0_cert(x509_store); + + if (!X509_up_ref(x509)) { + *err = "get verify result failed"; + return NULL; + } + + return x509; +#else + *err = "OpenSSL too old to support this function"; + + return NULL; +#endif + +#endif +} + + +#else /* HAVE_PROXY_SSL_PATCH */ + + +int +ngx_http_lua_ffi_proxy_ssl_set_verify_result(ngx_http_request_t *r, + int verify_result, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_proxy_ssl_get_verify_result(ngx_http_request_t *r, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +void +ngx_http_lua_ffi_proxy_ssl_free_verify_cert(void *cdata) +{ +} + + +void * +ngx_http_lua_ffi_proxy_ssl_get_verify_cert(ngx_http_request_t *r, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NULL; +} + +#endif /* HAVE_PROXY_SSL_PATCH */ +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.h b/src/ngx_http_lua_proxy_ssl_verifyby.h new file mode 100644 index 0000000000..3e0b178dee --- /dev/null +++ b/src/ngx_http_lua_proxy_ssl_verifyby.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +#if (NGX_HTTP_SSL) +#ifdef HAVE_PROXY_SSL_PATCH + +/* do not introduce ngx_http_proxy_module to pollute ngx_http_lua_module.c */ +extern ngx_module_t ngx_http_proxy_module; + +ngx_int_t ngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L); + +ngx_int_t ngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L); + +char *ngx_http_lua_proxy_ssl_verify_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +char *ngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, + void *arg); + +ngx_int_t ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf); + +#endif /* HAVE_PROXY_SSL_PATCH */ +#endif /* NGX_HTTP_SSL */ + + +#endif /* _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_regex.c b/src/ngx_http_lua_regex.c index e4f36bc44e..35fe2a055c 100644 --- a/src/ngx_http_lua_regex.c +++ b/src/ngx_http_lua_regex.c @@ -9,7 +9,6 @@ #endif #include "ddebug.h" - #if (NGX_PCRE) #include "ngx_http_lua_pcrefix.h" @@ -17,13 +16,24 @@ #include "ngx_http_lua_util.h" -#if (PCRE_MAJOR >= 6) +#if (PCRE_MAJOR >= 6 || NGX_PCRE2) # define LUA_HAVE_PCRE_DFA 1 #else # define LUA_HAVE_PCRE_DFA 0 #endif +#if (NGX_PCRE2) +static pcre2_compile_context *ngx_regex_compile_context; +static pcre2_match_context *ngx_regex_match_context; +static pcre2_match_data *ngx_regex_match_data; +static ngx_uint_t ngx_regex_match_data_size = 0; + +#define PCRE2_VERSION_SIZE 64 +static char ngx_pcre2_version[PCRE2_VERSION_SIZE]; +#endif + + #define NGX_LUA_RE_MODE_DFA (1<<1) #define NGX_LUA_RE_MODE_JIT (1<<2) #define NGX_LUA_RE_NO_UTF8_CHECK (1<<4) @@ -42,8 +52,17 @@ typedef struct { int ncaptures; int *captures; +#if (NGX_PCRE2) + pcre2_code *regex; + /* + * pcre2 doesn't use pcre_extra any more, + * just for keeping same memory layout in the lua ffi cdef + */ + void *regex_sd; +#else pcre *regex; pcre_extra *regex_sd; +#endif ngx_http_lua_complex_value_t *replace; @@ -57,7 +76,11 @@ typedef struct { ngx_pool_t *pool; ngx_int_t options; +#if (NGX_PCRE2) + pcre2_code *regex; +#else pcre *regex; +#endif int captures; ngx_str_t err; } ngx_http_lua_regex_compile_t; @@ -65,8 +88,12 @@ typedef struct { typedef struct { ngx_http_request_t *request; +#if (NGX_PCRE2) + pcre2_code *regex; +#else pcre *regex; pcre_extra *regex_sd; +#endif int ncaptures; int *captures; int captures_len; @@ -74,8 +101,6 @@ typedef struct { } ngx_http_lua_regex_ctx_t; -static void ngx_http_lua_regex_free_study_data(ngx_pool_t *pool, - pcre_extra *sd); static ngx_int_t ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc); @@ -91,21 +116,155 @@ static ngx_int_t ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc); static void -ngx_http_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd) +ngx_http_lua_regex_free_study_data(ngx_pool_t *pool, ngx_http_lua_regex_t *re) { - ngx_pool_t *old_pool; + ngx_pool_t *old_pool; - old_pool = ngx_http_lua_pcre_malloc_init(pool); +#if (NGX_PCRE2) + if (re && re->regex) { + old_pool = ngx_http_lua_pcre_malloc_init(pool); + + pcre2_code_free(re->regex); + + ngx_http_lua_pcre_malloc_done(old_pool); + re->regex = NULL; + } +#else + if (re && re->regex_sd) { + old_pool = ngx_http_lua_pcre_malloc_init(pool); #if LUA_HAVE_PCRE_JIT - pcre_free_study(sd); + pcre_free_study(re->regex_sd); #else - pcre_free(sd); + pcre_free(re->regex_sd); +#endif + ngx_http_lua_pcre_malloc_done(old_pool); + + re->regex_sd = NULL; + } #endif +} + + +#if (NGX_PCRE2) +static ngx_int_t +ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc) +{ + int n, errcode; + char *p; + size_t erroff; + u_char errstr[128]; + pcre2_code *re; + ngx_pool_t *old_pool; + pcre2_general_context *gctx; + pcre2_compile_context *cctx; + + ngx_http_lua_main_conf_t *lmcf; + + if (ngx_regex_compile_context == NULL) { + /* + * Allocate a compile context if not yet allocated. This uses + * direct allocations from heap, so the result can be cached + * even at runtime. + */ + + old_pool = ngx_http_lua_pcre_malloc_init(NULL); + + gctx = pcre2_general_context_create(ngx_http_lua_pcre_malloc, + ngx_http_lua_pcre_free, + NULL); + if (gctx == NULL) { + ngx_http_lua_pcre_malloc_done(old_pool); + goto nomem; + } + + cctx = pcre2_compile_context_create(gctx); + if (cctx == NULL) { + pcre2_general_context_free(gctx); + ngx_http_lua_pcre_malloc_done(old_pool); + goto nomem; + } + + ngx_regex_compile_context = cctx; + + ngx_regex_match_context = pcre2_match_context_create(gctx); + if (ngx_regex_match_context == NULL) { + pcre2_general_context_free(gctx); + ngx_http_lua_pcre_malloc_done(old_pool); + goto nomem; + } + + lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, + ngx_http_lua_module); + if (lmcf && lmcf->regex_match_limit > 0) { + pcre2_set_match_limit(ngx_regex_match_context, + lmcf->regex_match_limit); + } + + pcre2_general_context_free(gctx); + ngx_http_lua_pcre_malloc_done(old_pool); + } + + old_pool = ngx_http_lua_pcre_malloc_init(rc->pool); + + re = pcre2_compile(rc->pattern.data, + rc->pattern.len, rc->options, + &errcode, &erroff, ngx_regex_compile_context); ngx_http_lua_pcre_malloc_done(old_pool); + + if (re == NULL) { + pcre2_get_error_message(errcode, errstr, 128); + + if ((size_t) erroff == rc->pattern.len) { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre2_compile() failed: %s in \"%V\"", + errstr, &rc->pattern) + - rc->err.data; + + } else { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre2_compile() failed: %s in " + "\"%V\" at \"%s\"", errstr, &rc->pattern, + rc->pattern.data + erroff) + - rc->err.data; + } + + return NGX_ERROR; + } + + rc->regex = re; + + n = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &rc->captures); + if (n < 0) { + p = "pcre2_pattern_info(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d"; + goto failed; + } + +#if (NGX_DEBUG) + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "pcre2_compile: pattern[%V], options 0x%08Xd, ncaptures %d", + &rc->pattern, rc->options, rc->captures); +#endif + + return NGX_OK; + +failed: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n) + - rc->err.data; + return NGX_ERROR; + +nomem: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "regex \"%V\" compilation failed: no memory", + &rc->pattern) + - rc->err.data; + return NGX_ERROR; } +#else static ngx_int_t ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc) @@ -159,13 +318,14 @@ ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc) - rc->err.data; return NGX_OK; } +#endif ngx_int_t ngx_http_lua_ffi_set_jit_stack_size(int size, u_char *errstr, size_t *errstr_size) { -#if LUA_HAVE_PCRE_JIT +#if (LUA_HAVE_PCRE_JIT) ngx_http_lua_main_conf_t *lmcf; ngx_pool_t *pool, *old_pool; @@ -173,6 +333,8 @@ ngx_http_lua_ffi_set_jit_stack_size(int size, u_char *errstr, lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_lua_module); + ngx_http_lua_assert(lmcf != NULL); + if (size < NGX_LUA_RE_MIN_JIT_STACK_SIZE) { size = NGX_LUA_RE_MIN_JIT_STACK_SIZE; } @@ -184,15 +346,24 @@ ngx_http_lua_ffi_set_jit_stack_size(int size, u_char *errstr, if (lmcf->jit_stack) { old_pool = ngx_http_lua_pcre_malloc_init(pool); +#if (NGX_PCRE2) + pcre2_jit_stack_free(lmcf->jit_stack); +#else pcre_jit_stack_free(lmcf->jit_stack); +#endif ngx_http_lua_pcre_malloc_done(old_pool); } old_pool = ngx_http_lua_pcre_malloc_init(pool); +#if (NGX_PCRE2) + lmcf->jit_stack = pcre2_jit_stack_create(NGX_LUA_RE_MIN_JIT_STACK_SIZE, + size, NULL); +#else lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE, size); +#endif ngx_http_lua_pcre_malloc_done(old_pool); @@ -212,8 +383,153 @@ ngx_http_lua_ffi_set_jit_stack_size(int size, u_char *errstr, - errstr; return NGX_ERROR; +#endif +} + + +#if (NGX_PCRE2) +static void +ngx_http_lua_regex_jit_compile(ngx_http_lua_regex_t *re, int flags, + ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf, + ngx_http_lua_regex_compile_t *re_comp) +{ + ngx_int_t ret; + ngx_pool_t *old_pool; + + if (flags & NGX_LUA_RE_MODE_JIT) { + old_pool = ngx_http_lua_pcre_malloc_init(pool); + ret = pcre2_jit_compile(re_comp->regex, PCRE2_JIT_COMPLETE); + + if (ret != 0) { + ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, + "pcre2_jit_compile() failed: %d in \"%V\", " + "ignored", + ret, &re_comp->pattern); + +#if (NGX_DEBUG) + + } else { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "pcre2 JIT compiled successfully"); +# endif /* !(NGX_DEBUG) */ + } + + ngx_http_lua_pcre_malloc_done(old_pool); + + } + + if (lmcf && lmcf->jit_stack) { + pcre2_jit_stack_assign(ngx_regex_match_context, NULL, + lmcf->jit_stack); + } + + return; +} + +#else + +static void +ngx_http_lua_regex_jit_compile(ngx_http_lua_regex_t *re, int flags, + ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf, + ngx_http_lua_regex_compile_t *re_comp) +{ + const char *msg; + pcre_extra *sd = NULL; + ngx_pool_t *old_pool; + + +#if (LUA_HAVE_PCRE_JIT) + if (flags & NGX_LUA_RE_MODE_JIT) { + old_pool = ngx_http_lua_pcre_malloc_init(pool); + sd = pcre_study(re_comp->regex, PCRE_STUDY_JIT_COMPILE, &msg); + ngx_http_lua_pcre_malloc_done(old_pool); + +# if (NGX_DEBUG) + if (msg != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "pcre study failed with PCRE_STUDY_JIT_COMPILE: " + "%s (%p)", msg, sd); + } + + if (sd != NULL) { + int jitted; + + old_pool = ngx_http_lua_pcre_malloc_init(pool); + + pcre_fullinfo(re_comp->regex, sd, PCRE_INFO_JIT, &jitted); + + ngx_http_lua_pcre_malloc_done(old_pool); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "pcre JIT compiling result: %d", jitted); + } +# endif /* !(NGX_DEBUG) */ + + } else { + old_pool = ngx_http_lua_pcre_malloc_init(pool); + sd = pcre_study(re_comp->regex, 0, &msg); + ngx_http_lua_pcre_malloc_done(old_pool); + } + + if (sd && lmcf && lmcf->jit_stack) { + pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack); + } + + if (sd + && lmcf && lmcf->regex_match_limit > 0 + && !(flags & NGX_LUA_RE_MODE_DFA)) + { + sd->flags |= PCRE_EXTRA_MATCH_LIMIT; + sd->match_limit = lmcf->regex_match_limit; + } + #endif /* LUA_HAVE_PCRE_JIT */ + + re->regex_sd = sd; +} +#endif + + +#if (NGX_PCRE2) +void +ngx_http_lua_regex_cleanup(void *data) +{ + ngx_pool_t *old_pool; + ngx_http_lua_main_conf_t *lmcf; + + lmcf = data; + + if (ngx_regex_compile_context) { + old_pool = ngx_http_lua_pcre_malloc_init(NULL); + if (ngx_regex_match_context != NULL) { + pcre2_match_context_free(ngx_regex_match_context); + ngx_regex_match_context = NULL; + } + + pcre2_compile_context_free(ngx_regex_compile_context); + ngx_regex_compile_context = NULL; + ngx_http_lua_pcre_malloc_done(old_pool); + } + + if (lmcf && lmcf->jit_stack) { + old_pool = ngx_http_lua_pcre_malloc_init(NULL); + + pcre2_jit_stack_free(lmcf->jit_stack); + lmcf->jit_stack = NULL; + + ngx_http_lua_pcre_malloc_done(old_pool); + } + + if (ngx_regex_match_data) { + old_pool = ngx_http_lua_pcre_malloc_init(NULL); + pcre2_match_data_free(ngx_regex_match_data); + ngx_regex_match_data = NULL; + ngx_regex_match_data_size = 0; + ngx_http_lua_pcre_malloc_done(old_pool); + } + } +#endif ngx_http_lua_regex_t * @@ -226,8 +542,7 @@ ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, ngx_int_t rc; const char *msg; ngx_pool_t *pool, *old_pool; - pcre_extra *sd = NULL; - ngx_http_lua_regex_t *re; + ngx_http_lua_regex_t *re = NULL; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_regex_compile_t re_comp; @@ -249,6 +564,8 @@ ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, } re->pool = pool; + re->regex = NULL; + re->regex_sd = NULL; re_comp.options = pcre_opts; re_comp.pattern.data = (u_char *) pat; @@ -270,58 +587,20 @@ ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_lua_module); -#if (LUA_HAVE_PCRE_JIT) - - if (flags & NGX_LUA_RE_MODE_JIT) { - - old_pool = ngx_http_lua_pcre_malloc_init(pool); - sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); - ngx_http_lua_pcre_malloc_done(old_pool); - -# if (NGX_DEBUG) - if (msg != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "pcre study failed with PCRE_STUDY_JIT_COMPILE: " - "%s (%p)", msg, sd); - } - - if (sd != NULL) { - int jitted; - - old_pool = ngx_http_lua_pcre_malloc_init(pool); - - pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); - - ngx_http_lua_pcre_malloc_done(old_pool); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "pcre JIT compiling result: %d", jitted); - } -# endif /* !(NGX_DEBUG) */ - - } else { - old_pool = ngx_http_lua_pcre_malloc_init(pool); - sd = pcre_study(re_comp.regex, 0, &msg); - ngx_http_lua_pcre_malloc_done(old_pool); - } - - if (sd && lmcf->jit_stack) { - pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack); - } - -#endif /* LUA_HAVE_PCRE_JIT */ + ngx_http_lua_assert(lmcf != NULL); - if (sd && lmcf && lmcf->regex_match_limit > 0) { - sd->flags |= PCRE_EXTRA_MATCH_LIMIT; - sd->match_limit = lmcf->regex_match_limit; - } + ngx_http_lua_regex_jit_compile(re, flags, pool, lmcf, &re_comp); if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; re_comp.captures = 0; } else { +#if (NGX_PCRE2) + ovecsize = (re_comp.captures + 1) * 2; +#else ovecsize = (re_comp.captures + 1) * 3; +#endif } dd("allocating cap with size: %d", (int) ovecsize); @@ -332,6 +611,31 @@ ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, goto error; } +#if (NGX_PCRE2) + if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMECOUNT, + &re->name_count) < 0) + { + msg = "cannot acquire named subpattern count"; + goto error; + } + + if (re->name_count > 0) { + if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMEENTRYSIZE, + &re->name_entry_size) != 0) + { + msg = "cannot acquire named subpattern entry size"; + goto error; + } + + if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMETABLE, + &re->name_table) != 0) + { + msg = "cannot acquire named subpattern table"; + goto error; + } + } + +#else if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT, &re->name_count) != 0) { @@ -354,9 +658,9 @@ ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, goto error; } } +#endif re->regex = re_comp.regex; - re->regex_sd = sd; re->ncaptures = re_comp.captures; re->captures = cap; re->replace = NULL; @@ -372,9 +676,7 @@ ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, p = ngx_snprintf(errstr, errstr_size - 1, "%s", msg); *p = '\0'; - if (sd) { - ngx_http_lua_regex_free_study_data(pool, sd); - } + ngx_http_lua_regex_free_study_data(pool, re); if (pool) { ngx_destroy_pool(pool); @@ -384,6 +686,103 @@ ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, } +#if (NGX_PCRE2) +int +ngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags, + const u_char *s, size_t len, int pos) +{ + int rc, exec_opts = 0; + size_t *ov; + ngx_uint_t ovecpair, n, i; + ngx_pool_t *old_pool; + + if (flags & NGX_LUA_RE_MODE_DFA) { + ovecpair = 1; + re->ncaptures = 0; + + } else { + ovecpair = re->ncaptures + 1; + } + + old_pool = ngx_http_lua_pcre_malloc_init(NULL); + + if (ngx_regex_match_data == NULL + || ovecpair > ngx_regex_match_data_size) + { + /* + * Allocate a match data if not yet allocated or smaller than + * needed. + */ + + if (ngx_regex_match_data) { + pcre2_match_data_free(ngx_regex_match_data); + } + + ngx_regex_match_data_size = ovecpair; + ngx_regex_match_data = pcre2_match_data_create(ovecpair, NULL); + + if (ngx_regex_match_data == NULL) { + rc = PCRE2_ERROR_NOMEMORY; + goto failed; + } + } + + if (flags & NGX_LUA_RE_NO_UTF8_CHECK) { + exec_opts = PCRE2_NO_UTF_CHECK; + + } else { + exec_opts = 0; + } + + if (flags & NGX_LUA_RE_MODE_DFA) { + int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; + rc = pcre2_dfa_match(re->regex, s, len, pos, exec_opts, + ngx_regex_match_data, ngx_regex_match_context, + ws, sizeof(ws) / sizeof(ws[0])); + + + } else { + rc = pcre2_match(re->regex, s, len, pos, exec_opts, + ngx_regex_match_data, ngx_regex_match_context); + } + + if (rc < 0) { +#if (NGX_DEBUG) + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "pcre2_match failed: flags 0x%05Xd, options 0x%08Xd, " + "rc %d, ovecpair %ui", flags, exec_opts, rc, ovecpair); +#endif + + goto failed; + } + + n = pcre2_get_ovector_count(ngx_regex_match_data); + ov = pcre2_get_ovector_pointer(ngx_regex_match_data); + +#if (NGX_DEBUG) + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "pcre2_match: flags 0x%05Xd, options 0x%08Xd, rc %d, " + "n %ui, ovecpair %ui", flags, exec_opts, rc, n, ovecpair); +#endif + + if (n > ovecpair) { + n = ovecpair; + } + + for (i = 0; i < n; i++) { + re->captures[i * 2] = ov[i * 2]; + re->captures[i * 2 + 1] = ov[i * 2 + 1]; + } + +failed: + + ngx_http_lua_pcre_malloc_done(old_pool); + + return rc; +} + +#else + int ngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags, const u_char *s, size_t len, int pos) @@ -420,7 +819,8 @@ ngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags, int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; rc = ngx_http_lua_regex_dfa_exec(re->regex, sd, &subj, (int) pos, cap, ovecsize, ws, - sizeof(ws)/sizeof(ws[0]), exec_opts); + sizeof(ws) / sizeof(ws[0]), + exec_opts); #else @@ -436,28 +836,19 @@ ngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags, return rc; } +#endif + void ngx_http_lua_ffi_destroy_regex(ngx_http_lua_regex_t *re) { - ngx_pool_t *old_pool; - dd("destroy regex called"); if (re == NULL || re->pool == NULL) { return; } - if (re->regex_sd) { - old_pool = ngx_http_lua_pcre_malloc_init(re->pool); -#if LUA_HAVE_PCRE_JIT - pcre_free_study(re->regex_sd); -#else - pcre_free(re->regex_sd); -#endif - ngx_http_lua_pcre_malloc_done(old_pool); - re->regex_sd = NULL; - } + ngx_http_lua_regex_free_study_data(re->pool, re); ngx_destroy_pool(re->pool); } @@ -585,7 +976,13 @@ ngx_http_lua_ffi_max_regex_cache_size(void) const char * ngx_http_lua_ffi_pcre_version(void) { +#if (NGX_PCRE2) + pcre2_config(PCRE2_CONFIG_VERSION, ngx_pcre2_version); + + return ngx_pcre2_version; +#else return pcre_version(); +#endif } diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index d6e5640a0b..5d69735cde 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -101,6 +101,7 @@ ngx_http_lua_ngx_req_read_body(lua_State *L) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); @@ -228,15 +229,21 @@ ngx_http_lua_ngx_req_get_body_data(lua_State *L) { ngx_http_request_t *r; int n; - size_t len; + size_t len, max; + size_t size, rest; ngx_chain_t *cl; u_char *p; u_char *buf; n = lua_gettop(L); - if (n != 0) { - return luaL_error(L, "expecting 0 arguments but seen %d", n); + if (n != 0 && n != 1) { + return luaL_error(L, "expecting 0 or 1 arguments but seen %d", n); + } + + max = 0; + if (n == 1) { + max = (size_t) luaL_checknumber(L, 1); } r = ngx_http_lua_get_req(L); @@ -264,6 +271,7 @@ ngx_http_lua_ngx_req_get_body_data(lua_State *L) return 1; } + len = (max > 0 && len > max) ? max : len; lua_pushlstring(L, (char *) cl->buf->pos, len); return 1; } @@ -274,7 +282,13 @@ ngx_http_lua_ngx_req_get_body_data(lua_State *L) for (; cl; cl = cl->next) { dd("body chunk len: %d", (int) ngx_buf_size(cl->buf)); - len += cl->buf->last - cl->buf->pos; + size = cl->buf->last - cl->buf->pos; + if (max > 0 && (len + size > max)) { + len = max; + break; + } + + len += size; } if (len == 0) { @@ -285,8 +299,15 @@ ngx_http_lua_ngx_req_get_body_data(lua_State *L) buf = (u_char *) lua_newuserdata(L, len); p = buf; - for (cl = r->request_body->bufs; cl; cl = cl->next) { - p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); + rest = len; + for (cl = r->request_body->bufs; cl != NULL && rest > 0; cl = cl->next) { + size = ngx_buf_size(cl->buf); + if (size > rest) { /* reach limit*/ + size = rest; + } + + p = ngx_copy(p, cl->buf->pos, size); + rest -= size; } lua_pushlstring(L, (char *) buf, len); diff --git a/src/ngx_http_lua_rewriteby.c b/src/ngx_http_lua_rewriteby.c index d1eabeccdd..69302ba523 100644 --- a/src/ngx_http_lua_rewriteby.c +++ b/src/ngx_http_lua_rewriteby.c @@ -107,7 +107,9 @@ ngx_http_lua_rewrite_handler(ngx_http_request_t *r) } if (rc == NGX_DECLINED) { - if (r->header_sent) { + if (r->header_sent + || (r->headers_out.status != 0 && ctx->out != NULL)) + { dd("header already sent"); /* response header was already generated in rewrite_by_lua*, @@ -175,6 +177,10 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r) llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->rewrite_src_ref = LUA_REFNIL; + } + /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, llcf->rewrite_src.value.data, @@ -215,6 +221,10 @@ ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r) L = ngx_http_lua_get_lua_vm(r, NULL); + if (!llcf->enable_code_cache) { + llcf->rewrite_src_ref = LUA_REFNIL; + } + /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, &llcf->rewrite_src_ref, @@ -241,7 +251,7 @@ ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) ngx_event_t *rev; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; @@ -291,9 +301,9 @@ ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) /* }}} */ - /* {{{ register request cleanup hooks */ + /* {{{ register nginx pool cleanup hooks */ if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -349,7 +359,9 @@ ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) } if (rc == NGX_OK || rc == NGX_DECLINED) { - if (r->header_sent) { + if (r->header_sent + || (r->headers_out.status != 0 && ctx->out != NULL)) + { dd("header already sent"); /* response header was already generated in rewrite_by_lua*, diff --git a/src/ngx_http_lua_semaphore.c b/src/ngx_http_lua_semaphore.c index 8ca6efb345..435beaa29a 100644 --- a/src/ngx_http_lua_semaphore.c +++ b/src/ngx_http_lua_semaphore.c @@ -343,7 +343,7 @@ ngx_http_lua_ffi_sema_post(ngx_http_lua_sema_t *sem, int n) sem->resource_count += n; if (!ngx_queue_empty(&sem->wait_queue)) { - /* we need the extra paranthese around the first argument of + /* we need the extra parentheses around the first argument of * ngx_post_event() just to work around macro issues in nginx * cores older than nginx 1.7.12 (exclusive). */ @@ -466,8 +466,11 @@ ngx_http_lua_sema_handler(ngx_event_t *ev) sem = ev->data; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "semaphore handler: wait queue: %sempty, resource count: %d", + ngx_queue_empty(&sem->wait_queue) ? "" : "not ", + sem->resource_count); while (!ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) { - q = ngx_queue_head(&sem->wait_queue); ngx_queue_remove(q); @@ -566,6 +569,10 @@ ngx_http_lua_ffi_sema_gc(ngx_http_lua_sema_t *sem) "destroyed", sem); } + if (sem->sem_event.posted) { + ngx_delete_posted_event(&sem->sem_event); + } + ngx_http_lua_free_sema(sem); } diff --git a/src/ngx_http_lua_server_rewriteby.c b/src/ngx_http_lua_server_rewriteby.c new file mode 100644 index 0000000000..d012a8e341 --- /dev/null +++ b/src/ngx_http_lua_server_rewriteby.c @@ -0,0 +1,343 @@ + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#include +#include "ngx_http_lua_server_rewriteby.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_lua_exception.h" +#include "ngx_http_lua_cache.h" + +static ngx_int_t ngx_http_lua_server_rewrite_by_chunk(lua_State *L, + ngx_http_request_t *r); + +ngx_int_t +ngx_http_lua_server_rewrite_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + lua_State *L; + ngx_http_lua_srv_conf_t *lscf; + ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_ctx_t *ctx; + + /* XXX we need to take into account ngx_rewrite's location dump */ + if (r->uri_changed) { + return NGX_DECLINED; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua server rewrite handler, uri:\"%V\" c:%ud", &r->uri, + r->main->count); + + lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module); + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + L = ngx_http_lua_get_lua_vm(r, NULL); + + if (lscf->srv.server_rewrite_handler == NULL) { + dd("no rewrite handler found"); + return NGX_DECLINED; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + dd("entered? %d", (int) ctx->entered_server_rewrite_phase); + + if (ctx->entered_server_rewrite_phase) { + dd("rewriteby: calling wev handler"); + rc = ctx->resume_handler(r); + dd("rewriteby: wev handler returns %d", (int) rc); + + if (rc == NGX_OK) { + rc = NGX_DECLINED; + } + + if (rc == NGX_DECLINED) { + if (r->header_sent + || (r->headers_out.status != 0 && ctx->out != NULL)) + { + dd("header already sent"); + + /* response header was already generated in rewrite_by_lua*, + * so it is no longer safe to proceed to later phases + * which may generate responses again */ + + if (!ctx->eof) { + dd("eof not yet sent"); + + rc = ngx_http_lua_send_chain_link(r, ctx, NULL + /* indicate last_buf */); + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + } + + return NGX_HTTP_OK; + } + + r->write_event_handler = ngx_http_core_run_phases; + ctx->entered_server_rewrite_phase = 0; + + return NGX_DECLINED; + } + + return rc; + } + + if (ctx->waiting_more_body) { + return NGX_DONE; + } + + /* TODO: lscf do not have force_read_body */ + if (llcf->force_read_body && !ctx->read_body_done) { + r->request_body_in_single_buf = 1; + r->request_body_in_persistent_file = 1; + r->request_body_in_clean_file = 1; + + rc = ngx_http_read_client_request_body(r, + ngx_http_lua_generic_phase_post_read); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + if (rc == NGX_AGAIN) { + ctx->waiting_more_body = 1; + return NGX_DONE; + } + } + + dd("calling server rewrite handler"); + return lscf->srv.server_rewrite_handler(r, lscf, L); +} + + +ngx_int_t +ngx_http_lua_server_rewrite_handler_inline(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + dd("server_rewrite by lua inline"); + + + /* load Lua inline script (w/ cache) sp = 1 */ + rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, + lscf->srv.server_rewrite_src.value.data, + lscf->srv.server_rewrite_src.value.len, + &lscf->srv.server_rewrite_src_ref, + lscf->srv.server_rewrite_src_key, + (const char *) + lscf->srv.server_rewrite_chunkname); + if (rc != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return ngx_http_lua_server_rewrite_by_chunk(L, r); +} + + +ngx_int_t +ngx_http_lua_server_rewrite_handler_file(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + u_char *script_path; + ngx_str_t eval_src; + + + if (ngx_http_complex_value(r, &lscf->srv.server_rewrite_src, + &eval_src) != NGX_OK) + { + return NGX_ERROR; + } + + script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, + eval_src.len); + + if (script_path == NULL) { + return NGX_ERROR; + } + + /* load Lua script file (w/ cache) sp = 1 */ + rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path, + &lscf->srv.server_rewrite_src_ref, + lscf->srv.server_rewrite_src_key); + if (rc != NGX_OK) { + if (rc < NGX_HTTP_SPECIAL_RESPONSE) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return rc; + } + + return ngx_http_lua_server_rewrite_by_chunk(L, r); +} + + +static ngx_int_t +ngx_http_lua_server_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) +{ + int co_ref; + lua_State *co; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_event_t *rev; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + + ngx_http_lua_loc_conf_t *llcf; + + /* {{{ new coroutine to handle request */ + co = ngx_http_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine to handle request"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_http_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_http_lua_set_req(co, r); + + /* {{{ initialize request context */ + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_lua_reset_ctx(r, L, ctx); + + ctx->entered_server_rewrite_phase = 1; + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx); + + /* }}} */ + + /* {{{ register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cln->handler = ngx_http_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + /* }}} */ + + ctx->context = NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->check_client_abort) { + r->read_event_handler = ngx_http_lua_rd_check_broken_connection; + +#if (NGX_HTTP_V2) + if (!r->stream) { +#endif + + rev = r->connection->read; + + if (!rev->active) { + if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { + return NGX_ERROR; + } + } + +#if (NGX_HTTP_V2) + } +#endif + + } else { + r->read_event_handler = ngx_http_block_reading; + } + + c = r->connection; + nreqs = c->requests; + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + + if (rc == NGX_AGAIN) { + rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs); + + } else if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs); + } + + if (rc == NGX_OK || rc == NGX_DECLINED) { + if (r->header_sent + || (r->headers_out.status != 0 && ctx->out != NULL)) + { + dd("header already sent"); + + /* response header was already generated in rewrite_by_lua*, + * so it is no longer safe to proceed to later phases + * which may generate responses again */ + + if (!ctx->eof) { + dd("eof not yet sent"); + + rc = ngx_http_lua_send_chain_link(r, ctx, NULL + /* indicate last_buf */); + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + } + + return NGX_HTTP_OK; + } + + r->write_event_handler = ngx_http_core_run_phases; + ctx->entered_server_rewrite_phase = 0; + + return NGX_DECLINED; + } + + return rc; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_server_rewriteby.h b/src/ngx_http_lua_server_rewriteby.h new file mode 100644 index 0000000000..a35ebd2b72 --- /dev/null +++ b/src/ngx_http_lua_server_rewriteby.h @@ -0,0 +1,20 @@ + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_ + +#include "ngx_http_lua_common.h" + +ngx_int_t ngx_http_lua_server_rewrite_handler(ngx_http_request_t *r); +ngx_int_t ngx_http_lua_server_rewrite_handler_inline(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L); +ngx_int_t ngx_http_lua_server_rewrite_handler_file(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L); + +#endif /* _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_setby.c b/src/ngx_http_lua_setby.c index f00468c903..98e8d47f5a 100644 --- a/src/ngx_http_lua_setby.c +++ b/src/ngx_http_lua_setby.c @@ -123,16 +123,15 @@ ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val, } -int -ngx_http_lua_setby_param_get(lua_State *L, ngx_http_request_t *r) +void +ngx_http_lua_ffi_get_setby_param(ngx_http_request_t *r, int idx, + u_char **data_p, size_t *len_p) { - int idx; int n; ngx_http_variable_value_t *v; ngx_http_lua_main_conf_t *lmcf; - idx = luaL_checkint(L, 2); idx--; lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); @@ -144,13 +143,12 @@ ngx_http_lua_setby_param_get(lua_State *L, ngx_http_request_t *r) v = lmcf->setby_args; if (idx < 0 || idx > n - 1) { - lua_pushnil(L); + *len_p = 0; } else { - lua_pushlstring(L, (const char *) (v[idx].data), v[idx].len); + *data_p = v[idx].data; + *len_p = v[idx].len; } - - return 1; } diff --git a/src/ngx_http_lua_setby.h b/src/ngx_http_lua_setby.h index f43eef753e..8fd1e2c7b3 100644 --- a/src/ngx_http_lua_setby.h +++ b/src/ngx_http_lua_setby.h @@ -7,7 +7,6 @@ ngx_int_t ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val, ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *script); -int ngx_http_lua_setby_param_get(lua_State *L, ngx_http_request_t *r); #endif /* _NGX_HTTP_LUA_SET_BY_H_INCLUDED_ */ diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index 07effa88e2..146974d954 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -198,9 +198,6 @@ ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len); if (rc == 0) { - ngx_queue_remove(&sd->queue); - ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); - *sdp = sd; dd("node expires: %lld", (long long) sd->expires); @@ -213,12 +210,15 @@ ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, dd("time to live: %lld", (long long) ms); - if (ms < 0) { + if (ms <= 0) { dd("node already expired"); return NGX_DONE; } } + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + return NGX_OK; } @@ -654,7 +654,7 @@ ngx_http_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data, return NGX_ERROR; } - ngx_memcpy(&value->value.b, data, len); + ngx_memcpy(&value->value.n, data, len); break; case SHDICT_TBOOLEAN: @@ -815,7 +815,7 @@ ngx_http_lua_shdict_push_helper(lua_State *L, int flags) "type matched, reusing it"); sd->expires = 0; - + sd->value_len = 0; /* free list nodes */ queue = ngx_http_lua_shdict_get_list_head(sd, key.len); @@ -943,7 +943,7 @@ ngx_http_lua_shdict_push_helper(lua_State *L, int flags) ngx_shmtx_unlock(&ctx->shpool->mutex); - lua_pushboolean(L, 0); + lua_pushnil(L); lua_pushliteral(L, "no memory"); return 2; } @@ -1426,8 +1426,6 @@ ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, 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 @@ -1439,14 +1437,11 @@ ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, sd->user_flags = user_flags; - sd->value_len = (uint32_t) str_value_len; - dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; - p = ngx_copy(sd->data, key, key_len); - ngx_memcpy(p, str_value_buf, str_value_len); + ngx_memcpy(sd->data + key_len, str_value_buf, str_value_len); ngx_shmtx_unlock(&ctx->shpool->mutex); @@ -2097,4 +2092,41 @@ ngx_http_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone) #endif +#if (NGX_DARWIN) +int +ngx_http_lua_ffi_shdict_get_macos(ngx_http_lua_shdict_get_params_t *p) +{ + return ngx_http_lua_ffi_shdict_get(p->zone, + (u_char *) p->key, p->key_len, + p->value_type, p->str_value_buf, + p->str_value_len, p->num_value, + p->user_flags, p->get_stale, + p->is_stale, p->errmsg); +} + + +int +ngx_http_lua_ffi_shdict_store_macos(ngx_http_lua_shdict_store_params_t *p) +{ + return ngx_http_lua_ffi_shdict_store(p->zone, p->op, + (u_char *) p->key, p->key_len, + p->value_type, + (u_char *) p->str_value_buf, + p->str_value_len, p->num_value, + p->exptime, p->user_flags, + p->errmsg, p->forcible); +} + + +int +ngx_http_lua_ffi_shdict_incr_macos(ngx_http_lua_shdict_incr_params_t *p) +{ + return ngx_http_lua_ffi_shdict_incr(p->zone, (u_char *) p->key, p->key_len, + p->num_value, p->errmsg, + p->has_init, p->init, p->init_ttl, + p->forcible); +} +#endif + + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_shdict.h b/src/ngx_http_lua_shdict.h index 90a0099f5d..67dd4d4eb0 100644 --- a/src/ngx_http_lua_shdict.h +++ b/src/ngx_http_lua_shdict.h @@ -55,6 +55,52 @@ typedef struct { } ngx_http_lua_shm_zone_ctx_t; +#if (NGX_DARWIN) +typedef struct { + void *zone; + const unsigned char *key; + size_t key_len; + int *value_type; + unsigned char **str_value_buf; + size_t *str_value_len; + double *num_value; + int *user_flags; + int get_stale; + int *is_stale; + char **errmsg; +} ngx_http_lua_shdict_get_params_t; + + +typedef struct { + void *zone; + int op; + const unsigned char *key; + size_t key_len; + int value_type; + const unsigned char *str_value_buf; + size_t str_value_len; + double num_value; + long exptime; + int user_flags; + char **errmsg; + int *forcible; +} ngx_http_lua_shdict_store_params_t; + + +typedef struct { + void *zone; + const unsigned char *key; + size_t key_len; + double *num_value; + char **errmsg; + int has_init; + double init; + long init_ttl; + int *forcible; +} ngx_http_lua_shdict_incr_params_t; +#endif + + ngx_int_t ngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data); void ngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 55bd203d9c..85cf49069e 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -20,9 +20,12 @@ static int ngx_http_lua_socket_tcp(lua_State *L); +static int ngx_http_lua_socket_tcp_bind(lua_State *L); static int ngx_http_lua_socket_tcp_connect(lua_State *L); #if (NGX_HTTP_SSL) -static int ngx_http_lua_socket_tcp_sslhandshake(lua_State *L); +static void ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c); +static int ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); #endif static int ngx_http_lua_socket_tcp_receive(lua_State *L); static int ngx_http_lua_socket_tcp_receiveany(lua_State *L); @@ -72,6 +75,8 @@ static void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static int ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static void ngx_http_lua_socket_tcp_read_prepare(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, void *data, lua_State *L); static ngx_int_t ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static int ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, @@ -123,7 +128,7 @@ static int ngx_http_lua_socket_tcp_conn_op_resume_retval_handler( ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static int ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L); static int ngx_http_lua_socket_downstream_destroy(lua_State *L); -static ngx_int_t ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, +static void ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static ngx_int_t ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r, @@ -147,16 +152,8 @@ static void ngx_http_lua_socket_free_pool(ngx_log_t *log, static int ngx_http_lua_socket_shutdown_pool(lua_State *L); static void ngx_http_lua_socket_shutdown_pool_helper( ngx_http_lua_socket_pool_t *spool); -static void - ngx_http_lua_socket_empty_resolve_handler(ngx_resolver_ctx_t *ctx); static int ngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type); -#if (NGX_HTTP_SSL) -static int ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static void ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c); -static int ngx_http_lua_ssl_free_session(lua_State *L); -#endif static void ngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c); @@ -166,14 +163,17 @@ enum { SOCKET_CONNECT_TIMEOUT_INDEX = 2, SOCKET_SEND_TIMEOUT_INDEX = 4, SOCKET_READ_TIMEOUT_INDEX = 5, + SOCKET_CLIENT_CERT_INDEX = 6 , + SOCKET_CLIENT_PKEY_INDEX = 7 , + SOCKET_BIND_INDEX = 8 /* only in upstream cosocket */ }; enum { - SOCKET_OP_CONNECT, - SOCKET_OP_READ, - SOCKET_OP_WRITE, - SOCKET_OP_RESUME_CONN, + SOCKET_OP_CONNECT = 0x01, + SOCKET_OP_READ = 0x02, + SOCKET_OP_WRITE = 0x04, + SOCKET_OP_RESUME_CONN = 0x08, }; @@ -224,12 +224,13 @@ static char ngx_http_lua_upstream_udata_metatable_key; static char ngx_http_lua_downstream_udata_metatable_key; static char ngx_http_lua_pool_udata_metatable_key; static char ngx_http_lua_pattern_udata_metatable_key; -#if (NGX_HTTP_SSL) -static char ngx_http_lua_ssl_session_metatable_key; -#endif #define ngx_http_lua_tcp_socket_metatable_literal_key "__tcp_cosocket_mt" +#define ngx_http_lua_tcp_req_socket_metatable_literal_key \ + "__tcp_req_cosocket_mt" +#define ngx_http_lua_tcp_raw_req_socket_metatable_literal_key \ + "__tcp_raw_req_cosocket_mt" void @@ -286,6 +287,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); + lua_rawset(L, LUA_REGISTRYINDEX); + + lua_pushliteral(L, ngx_http_lua_tcp_req_socket_metatable_literal_key); + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + req_socket_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ @@ -315,24 +322,26 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); + lua_rawset(L, LUA_REGISTRYINDEX); + + lua_pushliteral(L, ngx_http_lua_tcp_raw_req_socket_metatable_literal_key); + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + raw_req_socket_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ /* {{{tcp object metatable */ lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( tcp_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 14 /* nrec */); + lua_createtable(L, 0 /* narr */, 16 /* nrec */); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_bind); + lua_setfield(L, -2, "bind"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_connect); lua_setfield(L, -2, "connect"); -#if (NGX_HTTP_SSL) - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_sslhandshake); - lua_setfield(L, -2, "sslhandshake"); - -#endif - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); lua_setfield(L, -2, "receive"); @@ -406,19 +415,6 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_setfield(L, -2, "__gc"); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ - -#if (NGX_HTTP_SSL) - - /* {{{ssl session userdata metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - ssl_session_metatable_key)); - lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ - lua_pushcfunction(L, ngx_http_lua_ssl_free_session); - lua_setfield(L, -2, "__gc"); - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - -#endif } @@ -453,7 +449,7 @@ ngx_http_lua_socket_tcp(lua_State *L) ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); - lua_createtable(L, 5 /* narr */, 1 /* nrec */); + lua_createtable(L, 7 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( tcp_socket_metatable_key)); lua_rawget(L, LUA_REGISTRYINDEX); @@ -854,6 +850,81 @@ ngx_http_lua_socket_tcp_connect_helper(lua_State *L, } +static int +ngx_http_lua_socket_tcp_bind(lua_State *L) +{ + int port; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + int n; + u_char *text; + size_t len; + ngx_addr_t *local; + + n = lua_gettop(L); + + /* Correct the parameter check and allow 2 or 3 parameters */ + if (n != 2 && n != 3) { + return luaL_error(L, "expecting 2 or 3 arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + + luaL_checktype(L, 1, LUA_TTABLE); + + port = 0; + /* handle case: host:port */ + /* Hit the following parameter combination: + * sock:bind("127.0.0.1", port) */ + if (n == 3) { + if (!lua_isnumber(L, 3)) { + lua_pushnil(L); + lua_pushfstring(L, "expecting port to be a" + "number but got type: %s", luaL_typename(L, 3)); + return 2; + } + + port = (int) lua_tointeger(L, 3); + if (port < 0 || port > 65535) { + lua_pushnil(L); + lua_pushfstring(L, "bad port number: %d", port); + return 2; + } + } + + text = (u_char *) luaL_checklstring(L, 2, &len); + + local = ngx_http_lua_parse_addr(L, text, len); + if (local == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "bad address"); + return 2; + } + + if (port > 0) { + ngx_inet_set_port(local->sockaddr, (in_port_t) port); + } + /* TODO: we may reuse the userdata here */ + lua_rawseti(L, 1, SOCKET_BIND_INDEX); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket bind ip: %V", &local->name); + + lua_pushboolean(L, 1); + return 1; +} + + static int ngx_http_lua_socket_tcp_connect(lua_State *L) { @@ -865,6 +936,7 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) size_t len; ngx_http_lua_loc_conf_t *llcf; ngx_peer_connection_t *pc; + ngx_addr_t *local; int connect_timeout, send_timeout, read_timeout; unsigned custom_pool; int key_index; @@ -1095,6 +1167,20 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) dd("lua peer connection log: %p", pc->log); + lua_rawgeti(L, 1, SOCKET_BIND_INDEX); + local = lua_touserdata(L, -1); + + if (local != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket sock:connect ip: %V", &local->name); + } + + lua_pop(L, 1); + + if (local) { + u->peer.local = local; + } + lua_rawgeti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); lua_rawgeti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); lua_rawgeti(L, 1, SOCKET_READ_TIMEOUT_INDEX); @@ -1151,13 +1237,6 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) } -static void -ngx_http_lua_socket_empty_resolve_handler(ngx_resolver_ctx_t *ctx) -{ - /* do nothing */ -} - - static void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) { @@ -1568,64 +1647,73 @@ ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r, #if (NGX_HTTP_SSL) -static int -ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) +static const char * +ngx_http_lua_socket_tcp_check_busy(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, unsigned int ops) { - int n, top; - ngx_int_t rc; - ngx_str_t name = ngx_null_string; - ngx_connection_t *c; - ngx_ssl_session_t **psession; - ngx_http_request_t *r; - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - - ngx_http_lua_socket_tcp_upstream_t *u; - - /* Lua function arguments: self [,session] [,host] [,verify] - [,send_status_req] */ + if ((ops & SOCKET_OP_CONNECT) && u->conn_waiting) { + return "socket busy connecting"; + } - n = lua_gettop(L); - if (n < 1 || n > 5) { - return luaL_error(L, "ngx.socket sslhandshake: expecting 1 ~ 5 " - "arguments (including the object), but seen %d", n); + if ((ops & SOCKET_OP_READ) && u->read_waiting) { + return "socket busy reading"; } - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); + if ((ops & SOCKET_OP_WRITE) + && (u->write_waiting + || (u->raw_downstream + && (r->connection->buffered & NGX_HTTP_LOWLEVEL_BUFFERED)))) + { + return "socket busy writing"; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket ssl handshake"); + return NULL; +} - luaL_checktype(L, 1, LUA_TTABLE); - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); +int +ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_session_t *sess, + int enable_session_reuse, ngx_str_t *server_name, int verify, + int ocsp_status_req, STACK_OF(X509) *chain, EVP_PKEY *pkey, + const char **errmsg) +{ + ngx_int_t rc, i; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + const char *busy_msg; + ngx_ssl_conn_t *ssl_conn; + X509 *x509; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket ssl handshake"); if (u == NULL || u->peer.connection == NULL || u->read_closed || u->write_closed) { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; + *errmsg = "closed"; + return NGX_ERROR; } if (u->request != r) { - return luaL_error(L, "bad request"); + *errmsg = "bad request"; + return NGX_ERROR; } - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - ngx_http_lua_socket_check_busy_writing(r, u, L); + busy_msg = ngx_http_lua_socket_tcp_check_busy(r, u, SOCKET_OP_CONNECT + | SOCKET_OP_READ + | SOCKET_OP_WRITE); + if (busy_msg != NULL) { + *errmsg = busy_msg; + return NGX_ERROR; + } if (u->raw_downstream || u->body_downstream) { - lua_pushnil(L); - lua_pushliteral(L, "not supported for downstream"); - return 2; + *errmsg = "not supported for downstream sockets"; + return NGX_ERROR; } c = u->peer.connection; @@ -1633,122 +1721,140 @@ ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) u->ssl_session_reuse = 1; if (c->ssl && c->ssl->handshaked) { - switch (lua_type(L, 2)) { - case LUA_TUSERDATA: - lua_pushvalue(L, 2); - break; + if (sess != NULL) { + return NGX_DONE; + } - case LUA_TBOOLEAN: - if (!lua_toboolean(L, 2)) { - /* avoid generating the ssl session */ - lua_pushboolean(L, 1); - break; - } - /* fall through */ + u->ssl_session_reuse = enable_session_reuse; - default: - ngx_http_lua_ssl_handshake_retval_handler(r, u, L); - break; - } + (void) ngx_http_lua_ssl_handshake_retval_handler(r, u, NULL); - return 1; + return NGX_OK; } if (ngx_ssl_create_connection(u->conf->ssl, c, NGX_SSL_BUFFER|NGX_SSL_CLIENT) != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "failed to create ssl connection"); - return 2; + *errmsg = "failed to create ssl connection"; + return NGX_ERROR; } + ssl_conn = c->ssl->connection; + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { - return luaL_error(L, "no ctx found"); + return NGX_HTTP_LUA_FFI_NO_REQ_CTX; } coctx = ctx->cur_co_ctx; c->sendfile = 0; - if (n >= 2) { - if (lua_type(L, 2) == LUA_TBOOLEAN) { - u->ssl_session_reuse = lua_toboolean(L, 2); + if (sess != NULL) { + if (ngx_ssl_set_session(c, sess) != NGX_OK) { + *errmsg = "ssl set session failed"; + return NGX_ERROR; + } - } else { - psession = lua_touserdata(L, 2); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua ssl set session: %p", sess); - if (psession != NULL && *psession != NULL) { - if (ngx_ssl_set_session(c, *psession) != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "lua ssl set session failed"); - return 2; - } + } else { + u->ssl_session_reuse = enable_session_reuse; + } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua ssl set session: %p", *psession); - } + if (chain != NULL) { + ngx_http_lua_assert(pkey != NULL); /* ensured by resty.core */ + + if (sk_X509_num(chain) < 1) { + ERR_clear_error(); + *errmsg = "invalid client certificate chain"; + return NGX_ERROR; } - if (n >= 3) { - name.data = (u_char *) lua_tolstring(L, 3, &name.len); + x509 = sk_X509_value(chain, 0); + if (x509 == NULL) { + ERR_clear_error(); + *errmsg = "ssl fetch client certificate from chain failed"; + return NGX_ERROR; + } - if (name.data) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua ssl server name: \"%*s\"", name.len, - name.data); + if (SSL_use_certificate(ssl_conn, x509) == 0) { + ERR_clear_error(); + *errmsg = "ssl set client certificate failed"; + return NGX_ERROR; + } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + /* read rest of the chain */ - if (SSL_set_tlsext_host_name(c->ssl->connection, - (char *) name.data) - == 0) - { - lua_pushnil(L); - lua_pushliteral(L, "SSL_set_tlsext_host_name failed"); - return 2; - } + for (i = 1; i < (ngx_int_t) sk_X509_num(chain); i++) { + x509 = sk_X509_value(chain, i); + if (x509 == NULL) { + ERR_clear_error(); + *errmsg = "ssl fetch client intermediate certificate from " + "chain failed"; + return NGX_ERROR; + } -#else + if (SSL_add1_chain_cert(ssl_conn, x509) == 0) { + ERR_clear_error(); + *errmsg = "ssl set client intermediate certificate failed"; + return NGX_ERROR; + } + } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua socket SNI disabled because the current " - "version of OpenSSL lacks the support"); + if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) { + ERR_clear_error(); + *errmsg = "ssl set client private key failed"; + return NGX_ERROR; + } + } + + if (server_name != NULL && server_name->data != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua ssl server name: \"%V\"", server_name); +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (SSL_set_tlsext_host_name(c->ssl->connection, + (char *) server_name->data) + == 0) + { + *errmsg = "SSL_set_tlsext_host_name failed"; + return NGX_ERROR; + } + +#else + *errmsg = "no TLS extension support"; + return NGX_ERROR; #endif - } + } - if (n >= 4) { - u->ssl_verify = lua_toboolean(L, 4); + u->ssl_verify = verify; - if (n >= 5) { - if (lua_toboolean(L, 5)) { + if (ocsp_status_req) { #ifdef NGX_HTTP_LUA_USE_OCSP - SSL_set_tlsext_status_type(c->ssl->connection, - TLSEXT_STATUSTYPE_ocsp); + SSL_set_tlsext_status_type(c->ssl->connection, + TLSEXT_STATUSTYPE_ocsp); + #else - return luaL_error(L, "no OCSP support"); + *errmsg = "no OCSP support"; + return NGX_ERROR; #endif - } - } - } - } } - dd("found sni name: %.*s %p", (int) name.len, name.data, name.data); - - if (name.len == 0) { + if (server_name == NULL || server_name->len == 0) { u->ssl_name.len = 0; } else { if (u->ssl_name.data) { /* buffer already allocated */ - if (u->ssl_name.len >= name.len) { + if (u->ssl_name.len >= server_name->len) { /* reuse it */ - ngx_memcpy(u->ssl_name.data, name.data, name.len); - u->ssl_name.len = name.len; + ngx_memcpy(u->ssl_name.data, server_name->data, + server_name->len); + u->ssl_name.len = server_name->len; } else { ngx_free(u->ssl_name.data); @@ -1759,17 +1865,15 @@ ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) new_ssl_name: - u->ssl_name.data = ngx_alloc(name.len, ngx_cycle->log); + u->ssl_name.data = ngx_alloc(server_name->len, ngx_cycle->log); if (u->ssl_name.data == NULL) { u->ssl_name.len = 0; - - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; + *errmsg = "no memory"; + return NGX_ERROR; } - ngx_memcpy(u->ssl_name.data, name.data, name.len); - u->ssl_name.len = name.len; + ngx_memcpy(u->ssl_name.data, server_name->data, server_name->len); + u->ssl_name.len = server_name->len; } } @@ -1783,7 +1887,8 @@ ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) rc = ngx_ssl_handshake(c); - dd("ngx_ssl_handshake returned %d", (int) rc); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ngx_ssl_handshake returned: %d", rc); if (rc == NGX_AGAIN) { if (c->write->timer_set) { @@ -1808,21 +1913,24 @@ ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) r->write_event_handler = ngx_http_core_run_phases; } - return lua_yield(L, 0); + return NGX_AGAIN; } - top = lua_gettop(L); ngx_http_lua_ssl_handshake_handler(c); - return lua_gettop(L) - top; + + if (rc == NGX_ERROR) { + *errmsg = u->error_ret; + return NGX_ERROR; + } + + return NGX_OK; } static void ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) { - const char *err; int waiting; - lua_State *L; ngx_int_t rc; ngx_connection_t *dc; /* downstream connection */ ngx_http_request_t *r; @@ -1845,11 +1953,9 @@ ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) waiting = u->conn_waiting; dc = r->connection; - L = u->write_co_ctx->co; if (c->read->timedout) { - lua_pushnil(L); - lua_pushliteral(L, "timeout"); + u->error_ret = "timeout"; goto failed; } @@ -1858,19 +1964,18 @@ ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) } if (c->ssl->handshaked) { - if (u->ssl_verify) { rc = SSL_get_verify_result(c->ssl->connection); if (rc != X509_V_OK) { - lua_pushnil(L); - err = lua_pushfstring(L, "%d: %s", (int) rc, - X509_verify_cert_error_string(rc)); + u->error_ret = X509_verify_cert_error_string(rc); + u->openssl_error_code_ret = rc; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, dc->log, 0, "lua ssl " - "certificate verify error: (%s)", err); + "certificate verify error: (%d: %s)", + rc, u->error_ret); } goto failed; @@ -1881,8 +1986,7 @@ ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) if (u->ssl_name.len && ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "certificate host mismatch"); + u->error_ret = "certificate host mismatch"; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { @@ -1901,7 +2005,7 @@ ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) ngx_http_lua_socket_handle_conn_success(r, u); } else { - (void) ngx_http_lua_ssl_handshake_retval_handler(r, u, L); + (void) ngx_http_lua_ssl_handshake_retval_handler(r, u, NULL); } if (waiting) { @@ -1911,21 +2015,42 @@ ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) return; } - lua_pushnil(L); - lua_pushliteral(L, "handshake failed"); + u->error_ret = "handshake failed"; failed: if (waiting) { u->write_prepare_retvals = - ngx_http_lua_socket_conn_error_retval_handler; - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_SSL); + ngx_http_lua_socket_conn_error_retval_handler; + ngx_http_lua_socket_handle_conn_error(r, u, NGX_HTTP_LUA_SOCKET_FT_SSL); ngx_http_run_posted_requests(dc); } else { - (void) ngx_http_lua_socket_conn_error_retval_handler(r, u, L); + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_SSL; + + (void) ngx_http_lua_socket_conn_error_retval_handler(r, u, NULL); + } +} + + +int +ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_session_t **sess, + const char **errmsg, int *openssl_error_code) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua cosocket get SSL handshake result for upstream: %p", u); + + if (u->error_ret != NULL) { + *errmsg = u->error_ret; + *openssl_error_code = u->openssl_error_code_ret; + + return NGX_ERROR; } + + *sess = u->ssl_session_ret; + + return NGX_OK; } @@ -1934,37 +2059,39 @@ ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { ngx_connection_t *c; - ngx_ssl_session_t *ssl_session, **ud; + ngx_ssl_session_t *ssl_session; if (!u->ssl_session_reuse) { - lua_pushboolean(L, 1); - return 1; + return 0; } - ud = lua_newuserdata(L, sizeof(ngx_ssl_session_t *)); - c = u->peer.connection; ssl_session = ngx_ssl_get_session(c); if (ssl_session == NULL) { - *ud = NULL; + u->ssl_session_ret = NULL; } else { - *ud = ssl_session; + u->ssl_session_ret = ssl_session; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua ssl save session: %p", ssl_session); - - /* set up the __gc metamethod */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - ssl_session_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); } - return 1; + return 0; } + +void +ngx_http_lua_ffi_ssl_free_session(ngx_ssl_session_t *sess) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua ssl free session: %p", sess); + + ngx_ssl_free_session(sess); +} + + #endif /* NGX_HTTP_SSL */ @@ -2017,12 +2144,14 @@ ngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r, u_char errstr[NGX_MAX_ERROR_STR]; u_char *p; - if (ft_type & (NGX_HTTP_LUA_SOCKET_FT_RESOLVER - | NGX_HTTP_LUA_SOCKET_FT_SSL)) - { + if (ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) { return 2; } + if (ft_type & NGX_HTTP_LUA_SOCKET_FT_SSL) { + return 0; + } + lua_pushnil(L); if (ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { @@ -2078,8 +2207,6 @@ ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; - u->input_filter_ctx = u; - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (u->bufs_in == NULL) { @@ -2108,6 +2235,8 @@ ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, u->read_waiting = 0; u->read_co_ctx = NULL; + ngx_http_lua_socket_tcp_read_prepare(r, u, u, L); + rc = ngx_http_lua_socket_tcp_read(r, u); if (rc == NGX_ERROR) { @@ -2324,7 +2453,7 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) case LUA_TNUMBER: bytes = lua_tointeger(L, 2); if (bytes < 0) { - return luaL_argerror(L, 2, "bad pattern argument"); + return luaL_argerror(L, 2, "bad number argument"); } #if 1 @@ -2341,7 +2470,7 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) break; default: - return luaL_argerror(L, 2, "bad pattern argument"); + return luaL_argerror(L, 2, "bad argument"); break; } @@ -2429,6 +2558,87 @@ ngx_http_lua_socket_read_any(void *data, ssize_t bytes) } +static void +ngx_http_lua_socket_tcp_read_prepare(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, void *data, lua_State *L) +{ + ngx_http_lua_ctx_t *ctx; + ngx_chain_t *new_cl; + ngx_buf_t *b; + off_t size; + + ngx_http_lua_socket_compiled_pattern_t *cp; + + /* input_filter_ctx doesn't change, no need recovering */ + if (u->input_filter_ctx == data) { + return; + } + + /* last input_filter_ctx is null or upstream, no data pending */ + if (u->input_filter_ctx == NULL || u->input_filter_ctx == u) { + u->input_filter_ctx = data; + return; + } + + /* compiled pattern may be with data pending */ + + cp = u->input_filter_ctx; + u->input_filter_ctx = data; + + cp->upstream = NULL; + + /* no data pending */ + if (cp->state <= 0) { + return; + } + + b = &u->buffer; + + if (b->pos - b->start >= cp->state) { + dd("pending data in one buffer"); + + b->pos -= cp->state; + + u->buf_in->buf->pos = b->pos; + u->buf_in->buf->last = b->pos; + + /* reset dfa state for future matching */ + cp->state = 0; + return; + } + + dd("pending data in multiple buffers"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + size = ngx_buf_size(b); + + new_cl = + ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + cp->state + size); + + if (new_cl == NULL) { + luaL_error(L, "no memory"); + return; + } + + ngx_memcpy(b, new_cl->buf, sizeof(ngx_buf_t)); + + b->last = ngx_copy(b->last, cp->pattern.data, cp->state); + b->last = ngx_copy(b->last, u->buf_in->buf->pos, size); + + u->buf_in->next = ctx->free_recv_bufs; + ctx->free_recv_bufs = u->buf_in; + + u->bufs_in = new_cl; + u->buf_in = new_cl; + + /* reset dfa state for future matching */ + cp->state = 0; +} + + static ngx_int_t ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) @@ -2450,6 +2660,14 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, "lua tcp socket read data: wait:%d", (int) u->read_waiting); + /* ngx_shutdown_timer_handler will set c->close and c->error on timeout + * when worker_shutdown_timeout is configured. + * The rev->ready is false at that time, so we need to set u->eof. + */ + if (c->close && c->error) { + u->eof = 1; + } + b = &u->buffer; read = 0; @@ -2834,7 +3052,7 @@ ngx_http_lua_socket_tcp_send(lua_State *L) switch (type) { case LUA_TNUMBER: - b->last = ngx_http_lua_write_num(L, 2, b->last); + b->last = ngx_http_lua_write_num(L, 2, b->last); break; case LUA_TSTRING: @@ -2981,7 +3199,6 @@ ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { int n; - ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_event_t *ev; @@ -3028,12 +3245,7 @@ ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, dd("u->bufs_in: %p", u->bufs_in); if (u->bufs_in) { - rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); - if (rc == NGX_ERROR) { - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; - } + ngx_http_lua_socket_push_input_data(r, ctx, u, L); (void) ngx_http_lua_socket_read_error_retval_handler(r, u, L); @@ -3047,12 +3259,7 @@ ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, return n + 1; } - rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); - if (rc == NGX_ERROR) { - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; - } + ngx_http_lua_socket_push_input_data(r, ctx, u, L); return 1; } @@ -3121,7 +3328,7 @@ ngx_http_lua_socket_tcp_settimeout(lua_State *L) n = lua_gettop(L); if (n != 2) { - return luaL_error(L, "ngx.socket settimout: expecting 2 arguments " + return luaL_error(L, "ngx.socket settimeout: expecting 2 arguments " "(including the object) but seen %d", lua_gettop(L)); } @@ -3168,7 +3375,7 @@ ngx_http_lua_socket_tcp_settimeouts(lua_State *L) n = lua_gettop(L); if (n != 4) { - return luaL_error(L, "ngx.socket settimout: expecting 4 arguments " + return luaL_error(L, "ngx.socket settimeouts: expecting 4 arguments " "(including the object) but seen %d", lua_gettop(L)); } @@ -3258,7 +3465,7 @@ ngx_http_lua_socket_tcp_handler(ngx_event_t *ev) static ngx_int_t ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data) { - /* empty */ + pc->type = SOCK_STREAM; return NGX_OK; } @@ -3372,7 +3579,7 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, ngx_chain_update_chains(r->pool, - &ctx->free_bufs, &ctx->busy_bufs, + &ctx->free_bufs, &u->busy_bufs, &u->request_bufs, (ngx_buf_tag_t) &ngx_http_lua_module); @@ -4150,6 +4357,11 @@ ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, ngx_http_lua_socket_tcp_finalize_read_part(r, u); ngx_http_lua_socket_tcp_finalize_write_part(r, u); + if (u->input_filter_ctx != NULL && u->input_filter_ctx != u) { + ((ngx_http_lua_socket_compiled_pattern_t *) + u->input_filter_ctx)->upstream = NULL; + } + if (u->raw_downstream || u->body_downstream) { u->peer.connection = NULL; return; @@ -4403,7 +4615,7 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) n = lua_gettop(L); if (n > 1) { - return luaL_error(L, "expecting 0 or 1 arguments, " + return luaL_error(L, "expecting 0 or 1 argument, " "but seen %d", n); } @@ -4466,8 +4678,6 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) (u_char *) lua_tolstring(L, lua_upvalueindex(2), &cp->pattern.len); - u->input_filter_ctx = cp; - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (u->bufs_in == NULL) { @@ -4494,6 +4704,8 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) u->read_waiting = 0; u->read_co_ctx = NULL; + ngx_http_lua_socket_tcp_read_prepare(r, u, cp, L); + rc = ngx_http_lua_socket_tcp_read(r, u); if (rc == NGX_ERROR) { @@ -4651,6 +4863,7 @@ ngx_http_lua_socket_read_until(void *data, ssize_t bytes) u_char c; u_char *pat; size_t pat_len; + size_t pending_len; int i; int state; int old_state = 0; /* just to make old @@ -4779,11 +4992,12 @@ ngx_http_lua_socket_read_until(void *data, ssize_t bytes) /* matched */ - dd("adding pending data: %.*s", (int) (old_state + 1 - state), - (char *) pat); + pending_len = old_state + 1 - state; + + dd("adding pending data: %.*s", (int) pending_len, (char *) pat); rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat, - old_state + 1 - state, + pending_len, old_state); if (rc != NGX_OK) { @@ -4794,14 +5008,14 @@ ngx_http_lua_socket_read_until(void *data, ssize_t bytes) i++; if (u->length) { - if (u->rest <= (size_t) state) { + if (u->rest <= pending_len) { u->rest = 0; cp->state = state; b->pos += i; return NGX_OK; } else { - u->rest -= state; + u->rest -= pending_len; } } @@ -4820,13 +5034,24 @@ ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L) { ngx_http_lua_socket_compiled_pattern_t *cp; - ngx_http_lua_dfa_edge_t *edge, *p; - unsigned i; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_dfa_edge_t *edge, *p; + unsigned i; dd("cleanup compiled pattern"); cp = lua_touserdata(L, 1); - if (cp == NULL || cp->recovering == NULL) { + if (cp == NULL) { + return 0; + } + + u = cp->upstream; + if (u != NULL) { + ngx_http_lua_socket_tcp_read_prepare(u->request, u, NULL, L); + u->input_filter_ctx = NULL; + } + + if (cp->recovering == NULL) { return 0; } @@ -4880,7 +5105,7 @@ ngx_http_lua_req_socket(lua_State *L) lua_pop(L, 1); } else { - return luaL_error(L, "expecting zero arguments, but got %d", + return luaL_error(L, "expecting 0 or 1 argument, but got %d", lua_gettop(L)); } @@ -4903,6 +5128,12 @@ ngx_http_lua_req_socket(lua_State *L) } #endif +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + return luaL_error(L, "http v3 not supported yet"); + } +#endif + if (!raw && r->headers_in.chunked) { lua_pushnil(L); lua_pushliteral(L, "chunked request bodies not supported yet"); @@ -4915,6 +5146,7 @@ ngx_http_lua_req_socket(lua_State *L) } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); @@ -5182,6 +5414,34 @@ ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) luaL_checktype(L, 1, LUA_TTABLE); + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + /* luaL_checkinteger will throw error if the argument is not a number. + * e.g.: bad argument \#2 to '?' (number expected, got string) + * + * We should check the argument in advance; otherwise, + * throwing an exception in the middle can compromise data integrity. + * e.g.: set pc->connection to NULL without following cleanup. + */ + if (n >= 2 && !lua_isnil(L, 2)) { + timeout = (ngx_msec_t) luaL_checkinteger(L, 2); + + } else { + timeout = llcf->keepalive_timeout; + } + + if (n >= 3 && !lua_isnil(L, 3)) { + pool_size = luaL_checkinteger(L, 3); + + } else { + pool_size = llcf->pool_size; + } + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); @@ -5197,17 +5457,17 @@ ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) pc = &u->peer; c = pc->connection; - if (c == NULL || u->read_closed || u->write_closed) { + /* When the server closes the connection, + * epoll will return EPOLLRDHUP event and nginx will set pending_eof. + */ + if (c == NULL || u->read_closed || u->write_closed + || c->read->eof || c->read->pending_eof) + { lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - if (u->request != r) { return luaL_error(L, "bad request"); } @@ -5227,8 +5487,7 @@ ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) return 2; } - if (c->read->eof - || c->read->error + if (c->read->error || c->read->timedout || c->write->error || c->write->timedout) @@ -5279,18 +5538,8 @@ ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) /* stack: obj timeout? size? pools cache_key */ - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (spool == NULL) { /* create a new socket pool for the current peer key */ - - if (n >= 3 && !lua_isnil(L, 3)) { - pool_size = luaL_checkinteger(L, 3); - - } else { - pool_size = llcf->pool_size; - } - if (pool_size <= 0) { msg = lua_pushfstring(L, "bad \"pool_size\" option value: %d", pool_size); @@ -5354,13 +5603,6 @@ ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) ngx_del_timer(c->write); } - if (n >= 2 && !lua_isnil(L, 2)) { - timeout = (ngx_msec_t) luaL_checkinteger(L, 2); - - } else { - timeout = llcf->keepalive_timeout; - } - #if (NGX_DEBUG) if (timeout == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -5394,6 +5636,7 @@ ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) if (c->read->ready) { rc = ngx_http_lua_socket_keepalive_close_handler(c->read); if (rc != NGX_OK) { + ngx_http_lua_socket_tcp_finalize(r, u); lua_pushnil(L); lua_pushliteral(L, "connection in dubious state"); return 2; @@ -5517,7 +5760,7 @@ ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev) ngx_http_lua_socket_pool_t *spool; int n; - char buf[1]; + unsigned char buf[1]; ngx_connection_t *c; c = ev->data; @@ -5538,9 +5781,10 @@ ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "lua tcp socket keepalive close handler check stale events"); - n = recv(c->fd, buf, 1, MSG_PEEK); + /* consume the possible ssl-layer data implicitly */ + n = c->recv(c, buf, 1); - if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + if (n == NGX_AGAIN) { /* stale event */ if (ngx_handle_read_event(c->read, 0) != NGX_OK) { @@ -5699,7 +5943,7 @@ ngx_http_lua_socket_downstream_destroy(lua_State *L) } -static ngx_int_t +static void ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) @@ -5771,8 +6015,6 @@ ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, u->buf_in->buf->last = u->buffer.pos; u->buf_in->buf->pos = u->buffer.pos; } - - return NGX_OK; } @@ -6084,10 +6326,8 @@ ngx_http_lua_tcp_resolve_cleanup(void *data) return; } - /* just to be safer */ - rctx->handler = ngx_http_lua_socket_empty_resolve_handler; - - ngx_resolve_name_done(rctx); + /* postpone free the rctx in the handler */ + rctx->handler = ngx_resolve_name_done; } @@ -6112,27 +6352,6 @@ ngx_http_lua_coctx_cleanup(void *data) } -#if (NGX_HTTP_SSL) - -static int -ngx_http_lua_ssl_free_session(lua_State *L) -{ - ngx_ssl_session_t **psession; - - psession = lua_touserdata(L, 1); - if (psession && *psession != NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua ssl free session: %p", *psession); - - ngx_ssl_free_session(*psession); - } - - return 0; -} - -#endif /* NGX_HTTP_SSL */ - - void ngx_http_lua_cleanup_conn_pools(lua_State *L) { @@ -6406,7 +6625,7 @@ ngx_http_lua_ffi_socket_tcp_getoption(ngx_http_lua_socket_tcp_upstream_t *u, fd = u->peer.connection->fd; - if (fd == (ngx_socket_t) -1) { + if (fd == (int) -1) { *errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err; return NGX_ERROR; } @@ -6463,7 +6682,7 @@ ngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u, fd = u->peer.connection->fd; - if (fd == (ngx_socket_t) -1) { + if (fd == (int) -1) { *errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err; return NGX_ERROR; } @@ -6511,6 +6730,28 @@ ngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u, } +int +ngx_http_lua_ffi_socket_tcp_getfd(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, const char **errmsg) +{ + int fd; + + *errmsg = NULL; + + if (u == NULL || u->peer.connection == NULL) { + *errmsg = "closed"; + return -1; + } + + fd = u->peer.connection->fd; + if (fd == -1) { + *errmsg = "faked connection"; + } + + return fd; +} + + /* just hack the fd for testing bad case, it will also return the original fd */ int ngx_http_lua_ffi_socket_tcp_hack_fd(ngx_http_lua_socket_tcp_upstream_t *u, @@ -6524,7 +6765,7 @@ ngx_http_lua_ffi_socket_tcp_hack_fd(ngx_http_lua_socket_tcp_upstream_t *u, } rc = u->peer.connection->fd; - if (rc == (ngx_socket_t) -1) { + if (rc == (int) -1) { *errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err; return -1; } diff --git a/src/ngx_http_lua_socket_tcp.h b/src/ngx_http_lua_socket_tcp.h index a0a5a5181a..0cc6641538 100644 --- a/src/ngx_http_lua_socket_tcp.h +++ b/src/ngx_http_lua_socket_tcp.h @@ -120,8 +120,13 @@ struct ngx_http_lua_socket_tcp_upstream_s { #if (NGX_HTTP_SSL) ngx_str_t ssl_name; + ngx_ssl_session_t *ssl_session_ret; + const char *error_ret; + int openssl_error_code_ret; #endif + ngx_chain_t *busy_bufs; + unsigned ft_type:16; unsigned no_close:1; unsigned conn_waiting:1; diff --git a/src/ngx_http_lua_socket_udp.c b/src/ngx_http_lua_socket_udp.c index fd3e074270..d57a55e123 100644 --- a/src/ngx_http_lua_socket_udp.c +++ b/src/ngx_http_lua_socket_udp.c @@ -24,7 +24,7 @@ #endif -#define UDP_MAX_DATAGRAM_SIZE 8192 +#define UDP_MAX_DATAGRAM_SIZE 65536 static int ngx_http_lua_socket_udp(lua_State *L); @@ -54,16 +54,19 @@ static void ngx_http_lua_socket_udp_read_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); static void ngx_http_lua_socket_udp_handle_success(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); -static ngx_int_t ngx_http_lua_udp_connect(ngx_http_lua_udp_connection_t *uc); +static ngx_int_t ngx_http_lua_udp_connect(ngx_http_lua_udp_connection_t *uc, + ngx_addr_t *local); static int ngx_http_lua_socket_udp_close(lua_State *L); static ngx_int_t ngx_http_lua_socket_udp_resume(ngx_http_request_t *r); static void ngx_http_lua_udp_resolve_cleanup(void *data); static void ngx_http_lua_udp_socket_cleanup(void *data); +static int ngx_http_lua_socket_udp_bind(lua_State *L); enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, + SOCKET_BIND_INDEX = 3, }; @@ -100,6 +103,9 @@ ngx_http_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_http_lua_socket_udp_close); lua_setfield(L, -2, "close"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_http_lua_socket_udp_bind); + lua_setfield(L, -2, "bind"); + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); @@ -159,6 +165,7 @@ ngx_http_lua_socket_udp_setpeername(lua_State *L) ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_str_t host; + ngx_addr_t *local; int port; ngx_resolver_ctx_t *rctx, temp; ngx_http_core_loc_conf_t *clcf; @@ -291,6 +298,13 @@ ngx_http_lua_socket_udp_setpeername(lua_State *L) u->read_timeout = u->conf->read_timeout; } + lua_rawgeti(L, 1, SOCKET_BIND_INDEX); + local = lua_touserdata(L, -1); + lua_pop(L, 1); + if (local != NULL) { + u->local = local; + } + ngx_memzero(&url, sizeof(ngx_url_t)); url.url.len = host.len; @@ -591,7 +605,7 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_connection_t *c; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ngx_http_upstream_resolved_t *ur; ngx_int_t rc; ngx_http_lua_udp_connection_t *uc; @@ -618,14 +632,14 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, return 2; } - rc = ngx_http_lua_udp_connect(uc); + rc = ngx_http_lua_udp_connect(uc, u->local); if (rc != NGX_OK) { u->socket_errno = ngx_socket_errno; } if (u->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; lua_pushnil(L); @@ -721,6 +735,56 @@ ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, } +static int +ngx_http_lua_socket_udp_bind(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + int n; + u_char *text; + size_t len; + ngx_addr_t *local; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + + luaL_checktype(L, 1, LUA_TTABLE); + + text = (u_char *) luaL_checklstring(L, 2, &len); + + local = ngx_http_lua_parse_addr(L, text, len); + if (local == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "bad address"); + return 2; + } + + lua_rawseti(L, 1, SOCKET_BIND_INDEX); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua udp socket bind ip: %V", &local->name); + + lua_pushinteger(L, 1); + return 1; +} + + static int ngx_http_lua_socket_udp_send(lua_State *L) { @@ -1044,7 +1108,7 @@ ngx_http_lua_socket_udp_settimeout(lua_State *L) n = lua_gettop(L); if (n != 2) { - return luaL_error(L, "ngx.socket settimout: expecting at least 2 " + return luaL_error(L, "ngx.socket settimeout: expecting at least 2 " "arguments (including the object) but seen %d", lua_gettop(L)); } @@ -1143,7 +1207,7 @@ ngx_http_lua_socket_udp_read(ngx_http_request_t *r, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua udp socket read data: waiting: %d", (int) u->waiting); - n = ngx_udp_recv(u->udp_connection.connection, + n = ngx_udp_recv(c, ngx_http_lua_socket_udp_buffer, u->recv_buf_size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -1340,7 +1404,7 @@ ngx_http_lua_socket_udp_handle_success(ngx_http_request_t *r, static ngx_int_t -ngx_http_lua_udp_connect(ngx_http_lua_udp_connection_t *uc) +ngx_http_lua_udp_connect(ngx_http_lua_udp_connection_t *uc, ngx_addr_t *local) { int rc; ngx_int_t event; @@ -1413,8 +1477,18 @@ ngx_http_lua_udp_connect(ngx_http_lua_udp_connection_t *uc) return NGX_ERROR; } } + #endif + if (local != NULL) { + if (bind(s, local->sockaddr, local->socklen) == -1) { + ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, + "bind(%V) failed", &local->name); + + return NGX_ERROR; + } + } + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "connect to %V, fd:%d #%d", &uc->server, s, c->number); @@ -1584,7 +1658,8 @@ ngx_http_lua_udp_resolve_cleanup(void *data) return; } - ngx_resolve_name_done(rctx); + /* postpone free the rctx in the handler */ + rctx->handler = ngx_resolve_name_done; } diff --git a/src/ngx_http_lua_socket_udp.h b/src/ngx_http_lua_socket_udp.h index 83333461db..61245b3647 100644 --- a/src/ngx_http_lua_socket_udp.h +++ b/src/ngx_http_lua_socket_udp.h @@ -42,6 +42,8 @@ struct ngx_http_lua_socket_udp_upstream_s { ngx_http_request_t *request; ngx_http_lua_udp_connection_t udp_connection; + ngx_addr_t *local; + ngx_msec_t read_timeout; ngx_http_upstream_resolved_t *resolved; @@ -53,7 +55,7 @@ struct ngx_http_lua_socket_udp_upstream_s { ngx_http_lua_co_ctx_t *co_ctx; - unsigned waiting; /* :1 */ + unsigned waiting:1; /* :1 */ }; diff --git a/src/ngx_http_lua_ssl.c b/src/ngx_http_lua_ssl.c index 8ed7b95417..25e4f1b456 100644 --- a/src/ngx_http_lua_ssl.c +++ b/src/ngx_http_lua_ssl.c @@ -14,6 +14,7 @@ int ngx_http_lua_ssl_ctx_index = -1; +int ngx_http_lua_ssl_key_log_index = -1; ngx_int_t @@ -30,6 +31,17 @@ ngx_http_lua_ssl_init(ngx_log_t *log) } } + if (ngx_http_lua_ssl_key_log_index == -1) { + ngx_http_lua_ssl_key_log_index = SSL_get_ex_new_index(0, NULL, NULL, + NULL, NULL); + + if (ngx_http_lua_ssl_key_log_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "lua: SSL_get_ex_new_index() for key log failed"); + return NGX_ERROR; + } + } + return NGX_OK; } diff --git a/src/ngx_http_lua_ssl.h b/src/ngx_http_lua_ssl.h index f50ecc5194..f709e6530f 100644 --- a/src/ngx_http_lua_ssl.h +++ b/src/ngx_http_lua_ssl.h @@ -16,33 +16,56 @@ typedef struct { ngx_connection_t *connection; /* original true connection */ - ngx_http_request_t *request; /* fake request */ + ngx_http_request_t *request; ngx_pool_cleanup_pt *cleanup; - ngx_ssl_session_t *session; /* retrurn value for openssl's + ngx_ssl_session_t *session; /* return value for openssl's * session_get_cb */ ngx_str_t session_id; +#ifdef HAVE_PROXY_SSL_PATCH + X509_STORE_CTX *x509_store; + ngx_pool_t *pool; +#endif + int exit_code; /* exit code for openssl's - set_cert_cb callback */ + set_client_hello_cb or + set_cert_cb callback or + SSL_CTX_set_cert_verify_callback */ int ctx_ref; /* reference to anchor request ctx data in lua registry */ +#ifdef HAVE_PROXY_SSL_PATCH + /* same size as count field of ngx_http_request_t */ + unsigned original_request_count:16; +#endif unsigned done:1; unsigned aborted:1; + unsigned entered_client_hello_handler:1; unsigned entered_cert_handler:1; unsigned entered_sess_fetch_handler:1; +#ifdef HAVE_PROXY_SSL_PATCH + unsigned entered_proxy_ssl_verify_handler:1; +#endif } ngx_http_lua_ssl_ctx_t; +typedef struct { + ngx_ssl_t *ssl; + ngx_fd_t fd; + ngx_str_t name; +} ngx_http_lua_ssl_key_log_t; + + ngx_int_t ngx_http_lua_ssl_init(ngx_log_t *log); extern int ngx_http_lua_ssl_ctx_index; +extern int ngx_http_lua_ssl_key_log_index; #endif diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index dfaa9f15a3..5d16834212 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -37,6 +37,10 @@ static u_char *ngx_http_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, static ngx_int_t ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r); +#ifndef OPENSSL_IS_BORINGSSL +static int ngx_http_lua_is_grease_cipher(uint16_t cipher_id); +#endif + ngx_int_t ngx_http_lua_ssl_cert_handler_file(ngx_http_request_t *r, @@ -70,7 +74,8 @@ ngx_http_lua_ssl_cert_handler_inline(ngx_http_request_t *r, lscf->srv.ssl_cert_src.len, &lscf->srv.ssl_cert_src_ref, lscf->srv.ssl_cert_src_key, - "=ssl_certificate_by_lua"); + (const char *) + lscf->srv.ssl_cert_chunkname); if (rc != NGX_OK) { return rc; } @@ -115,6 +120,8 @@ ngx_http_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, #else + size_t chunkname_len; + u_char *chunkname; u_char *cache_key = NULL; u_char *name; ngx_str_t *value; @@ -163,8 +170,15 @@ ngx_http_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_ERROR; } + chunkname = ngx_http_lua_gen_chunk_name(cf, "ssl_certificate_by_lua", + sizeof("ssl_certificate_by_lua") - 1, &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + /* Don't eval nginx variables for inline lua code */ lscf->srv.ssl_cert_src = value[1]; + lscf->srv.ssl_cert_chunkname = chunkname; } lscf->srv.ssl_cert_src_key = cache_key; @@ -203,7 +217,7 @@ ngx_http_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data) if (cctx->done) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua_certificate_by_lua: cert cb exit code: %d", + "ssl_certificate_by_lua: cert cb exit code: %d", cctx->exit_code); dd("lua ssl cert done, finally"); @@ -305,7 +319,7 @@ ngx_http_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data) } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua_certificate_by_lua: handler return value: %i, " + "ssl_certificate_by_lua: handler return value: %i, " "cert cb exit code: %d", rc, cctx->exit_code); c->log->action = "SSL handshaking"; @@ -336,7 +350,6 @@ ngx_http_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data) return -1; -#if 1 failed: if (r && r->pool) { @@ -348,15 +361,14 @@ ngx_http_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data) } return 0; -#endif } static void ngx_http_lua_ssl_cert_done(void *data) { - ngx_connection_t *c; - ngx_http_lua_ssl_ctx_t *cctx = data; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx = data; dd("lua ssl cert done"); @@ -377,6 +389,14 @@ ngx_http_lua_ssl_cert_done(void *data) c->log->action = "SSL handshaking"; ngx_post_event(c->write, &ngx_posted_events); + +#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3) +# if OPENSSL_VERSION_NUMBER >= 0x1000205fL +# if (NGX_QUIC_OPENSSL_COMPAT || NGX_QUIC_OPENSSL_API) + ngx_http_lua_resume_quic_ssl_handshake(c); +# endif +# endif +#endif } @@ -385,7 +405,7 @@ ngx_http_lua_ssl_cert_aborted(void *data) { ngx_http_lua_ssl_ctx_t *cctx = data; - dd("lua ssl cert done"); + dd("lua ssl cert aborted"); if (cctx->done) { /* completed successfully already */ @@ -393,7 +413,7 @@ ngx_http_lua_ssl_cert_aborted(void *data) } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, - "lua_certificate_by_lua: cert cb aborted"); + "ssl_certificate_by_lua: cert cb aborted"); cctx->aborted = 1; cctx->request->connection->ssl = NULL; @@ -420,7 +440,7 @@ ngx_http_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, size_t len) c = log->data; - if (c->addr_text.len) { + if (c && c->addr_text.len) { p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); len -= p - buf; buf = p; @@ -436,6 +456,18 @@ ngx_http_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, size_t len) } +#ifndef OPENSSL_IS_BORINGSSL +static int +ngx_http_lua_is_grease_cipher(uint16_t cipher_id) +{ + /* GREASE values follow pattern: 0x?A?A where ? can be any hex digit */ + /* and both ? must be the same */ + /* Check if both bytes follow ?A pattern and high nibbles match */ + return (cipher_id & 0x0F0F) == 0x0A0A; +} +#endif + + static ngx_int_t ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r) { @@ -443,7 +475,7 @@ ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r) ngx_int_t rc; lua_State *co; ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); @@ -497,7 +529,7 @@ ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r) /* register request cleanup hooks */ if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { rc = NGX_ERROR; ngx_http_lua_finalize_request(r, rc); @@ -823,6 +855,70 @@ ngx_http_lua_ffi_ssl_raw_server_addr(ngx_http_request_t *r, char **addr, } +int +ngx_http_lua_ffi_req_shared_ssl_ciphers(ngx_http_request_t *r, + uint16_t *ciphers, uint16_t *nciphers, int filter_grease, char **err) +{ +#ifdef OPENSSL_IS_BORINGSSL + + *err = "BoringSSL is not supported for SSL cipher operations"; + return NGX_ERROR; + +#else + ngx_ssl_conn_t *ssl_conn; + STACK_OF(SSL_CIPHER) *sk, *ck; + int sn, cn, i, n; + uint16_t cipher; + + if (r == NULL || r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + sk = SSL_get1_supported_ciphers(ssl_conn); + ck = SSL_get_client_ciphers(ssl_conn); + sn = sk_SSL_CIPHER_num(sk); + cn = sk_SSL_CIPHER_num(ck); + + if (sn > *nciphers) { + *err = "buffer too small"; + *nciphers = 0; + sk_SSL_CIPHER_free(sk); + return NGX_ERROR; + } + + for (*nciphers = 0, i = 0; i < sn; i++) { + cipher = SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(sk, i)); + + /* Skip GREASE ciphers if filtering is enabled */ + if (filter_grease && ngx_http_lua_is_grease_cipher(cipher)) { + continue; + } + + for (n = 0; n < cn; n++) { + if (SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(ck, n)) + == cipher) + { + ciphers[(*nciphers)++] = cipher; + break; + } + } + } + + sk_SSL_CIPHER_free(sk); + + return NGX_OK; +#endif + +} + + int ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name, size_t *namelen, char **err) @@ -1042,7 +1138,7 @@ ngx_http_lua_ffi_cert_pem_to_der(const u_char *pem, size_t pem_len, u_char *der, int ngx_http_lua_ffi_priv_key_pem_to_der(const u_char *pem, size_t pem_len, - u_char *der, char **err) + const u_char *passphrase, u_char *der, char **err) { int len; BIO *in; @@ -1055,7 +1151,7 @@ ngx_http_lua_ffi_priv_key_pem_to_der(const u_char *pem, size_t pem_len, return NGX_ERROR; } - pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); + pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (void *) passphrase); if (pkey == NULL) { BIO_free(in); *err = "PEM_read_bio_PrivateKey() failed"; @@ -1162,6 +1258,81 @@ ngx_http_lua_ffi_parse_pem_cert(const u_char *pem, size_t pem_len, } +void * +ngx_http_lua_ffi_parse_der_cert(const char *data, size_t len, + char **err) +{ + BIO *bio; + X509 *x509; + STACK_OF(X509) *chain; + + if (data == NULL || len == 0) { + *err = "invalid argument"; + ERR_clear_error(); + return NULL; + } + + bio = BIO_new_mem_buf((char *) data, len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + ERR_clear_error(); + return NULL; + } + + x509 = d2i_X509_bio(bio, NULL); + if (x509 == NULL) { + *err = "d2i_X509_bio() failed"; + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + chain = sk_X509_new_null(); + if (chain == NULL) { + *err = "sk_X509_new_null() failed"; + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + sk_X509_free(chain); + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + /* read rest of the chain */ + + while (!BIO_eof(bio)) { + x509 = d2i_X509_bio(bio, NULL); + if (x509 == NULL) { + *err = "d2i_X509_bio() failed in rest of chain"; + sk_X509_pop_free(chain, X509_free); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed in rest of chain"; + sk_X509_pop_free(chain, X509_free); + X509_free(x509); + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + } + + BIO_free(bio); + + return chain; +} + + void ngx_http_lua_ffi_free_cert(void *cdata) { @@ -1199,6 +1370,40 @@ ngx_http_lua_ffi_parse_pem_priv_key(const u_char *pem, size_t pem_len, } +void * +ngx_http_lua_ffi_parse_der_priv_key(const char *data, size_t len, + char **err) +{ + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + + if (data == NULL || len == 0) { + *err = "invalid argument"; + ERR_clear_error(); + return NULL; + } + + bio = BIO_new_mem_buf((char *) data, len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + ERR_clear_error(); + return NULL; + } + + pkey = d2i_PrivateKey_bio(bio, NULL); + if (pkey == NULL) { + *err = "d2i_PrivateKey_bio() failed"; + BIO_free(bio); + ERR_clear_error(); + return NULL; + } + + BIO_free(bio); + + return pkey; +} + + void ngx_http_lua_ffi_free_priv_key(void *cdata) { @@ -1314,7 +1519,7 @@ ngx_http_lua_ffi_set_priv_key(ngx_http_request_t *r, pkey = cdata; if (pkey == NULL) { - *err = "invalid private key failed"; + *err = "invalid private key"; goto failed; } @@ -1333,6 +1538,7 @@ ngx_http_lua_ffi_set_priv_key(ngx_http_request_t *r, } +#ifndef LIBRESSL_VERSION_NUMBER static int ngx_http_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { @@ -1344,16 +1550,25 @@ ngx_http_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) */ return 1; } +#endif int -ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *ca_certs, - int depth, char **err) +ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *client_certs, + void *trusted_certs, int depth, char **err) { +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + ngx_http_lua_ctx_t *ctx; ngx_ssl_conn_t *ssl_conn; ngx_http_ssl_srv_conf_t *sscf; - STACK_OF(X509) *chain = ca_certs; + STACK_OF(X509) *client_chain = client_certs; + STACK_OF(X509) *trusted_chain = trusted_certs; STACK_OF(X509_NAME) *name_chain = NULL; X509 *x509 = NULL; X509_NAME *subject = NULL; @@ -1407,54 +1622,75 @@ ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *ca_certs, /* set CA chain */ - if (chain != NULL) { + if (client_chain != NULL || trusted_chain != NULL) { + ca_store = X509_STORE_new(); if (ca_store == NULL) { *err = "X509_STORE_new() failed"; return NGX_ERROR; } - /* construct name chain */ - - name_chain = sk_X509_NAME_new_null(); - if (name_chain == NULL) { - *err = "sk_X509_NAME_new_null() failed"; - goto failed; - } + if (client_chain != NULL) { - for (i = 0; i < sk_X509_num(chain); i++) { - x509 = sk_X509_value(chain, i); - if (x509 == NULL) { - *err = "sk_X509_value() failed"; + /* construct name chain */ + name_chain = sk_X509_NAME_new_null(); + if (name_chain == NULL) { + *err = "sk_X509_NAME_new_null() failed"; goto failed; } - /* add subject to name chain, which will be sent to client */ - subject = X509_NAME_dup(X509_get_subject_name(x509)); - if (subject == NULL) { - *err = "X509_get_subject_name() failed"; - goto failed; + for (i = 0; i < sk_X509_num(client_chain); i++) { + x509 = sk_X509_value(client_chain, i); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + /* add subject to name chain, which will be sent to client */ + subject = X509_NAME_dup(X509_get_subject_name(x509)); + if (subject == NULL) { + *err = "X509_get_subject_name() failed"; + goto failed; + } + + if (!sk_X509_NAME_push(name_chain, subject)) { + *err = "sk_X509_NAME_push() failed"; + X509_NAME_free(subject); + goto failed; + } + + /* add to trusted CA store */ + if (X509_STORE_add_cert(ca_store, x509) == 0) { + *err = "X509_STORE_add_cert() failed"; + goto failed; + } } - if (!sk_X509_NAME_push(name_chain, subject)) { - *err = "sk_X509_NAME_push() failed"; - X509_NAME_free(subject); - goto failed; - } + /* clean subject name list, and set it for send to client */ + SSL_set_client_CA_list(ssl_conn, name_chain); + } - /* add to trusted CA store */ - if (X509_STORE_add_cert(ca_store, x509) == 0) { - *err = "X509_STORE_add_cert() failed"; - goto failed; + if (trusted_chain != NULL) { + for (i = 0; i < sk_X509_num(trusted_chain); i++) { + x509 = sk_X509_value(trusted_chain, i); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + /* add to trusted CA store */ + if (X509_STORE_add_cert(ca_store, x509) == 0) { + *err = "X509_STORE_add_cert() failed"; + goto failed; + } } } + /* clean ca_store, and store new ca_store */ if (SSL_set0_verify_cert_store(ssl_conn, ca_store) == 0) { *err = "SSL_set0_verify_cert_store() failed"; goto failed; } - - SSL_set_client_CA_list(ssl_conn, name_chain); } return NGX_OK; @@ -1466,6 +1702,47 @@ ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *ca_certs, X509_STORE_free(ca_store); return NGX_ERROR; +#endif +} + + +ngx_ssl_conn_t * +ngx_http_lua_ffi_get_req_ssl_pointer(ngx_http_request_t *r, const char **err) +{ + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NULL; + } + + if (r->connection->ssl->connection == NULL) { + *err = "bad ssl connection"; + return NULL; + } + + return r->connection->ssl->connection; +} + + +int +ngx_http_lua_ffi_ssl_client_random(ngx_http_request_t *r, + unsigned char *out, size_t *outlen, char **err) +{ + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + *outlen = SSL_get_client_random(ssl_conn, out, *outlen); + + return NGX_OK; } diff --git a/src/ngx_http_lua_ssl_client_helloby.c b/src/ngx_http_lua_ssl_client_helloby.c new file mode 100644 index 0000000000..283d1017f0 --- /dev/null +++ b/src/ngx_http_lua_ssl_client_helloby.c @@ -0,0 +1,833 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_HTTP_SSL) + + +#include "ngx_http_lua_cache.h" +#include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_ssl_module.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_ssl_client_helloby.h" +#include "ngx_http_lua_directive.h" +#include "ngx_http_lua_ssl.h" + + +static void ngx_http_lua_ssl_client_hello_done(void *data); +static void ngx_http_lua_ssl_client_hello_aborted(void *data); +static u_char *ngx_http_lua_log_ssl_client_hello_error(ngx_log_t *log, + u_char *buf, size_t len); +static ngx_int_t ngx_http_lua_ssl_client_hello_by_chunk(lua_State *L, + ngx_http_request_t *r); + + +ngx_int_t +ngx_http_lua_ssl_client_hello_handler_file(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadfile(r->connection->log, L, + lscf->srv.ssl_client_hello_src.data, + &lscf->srv.ssl_client_hello_src_ref, + lscf->srv.ssl_client_hello_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_ssl_client_hello_by_chunk(L, r); +} + + +ngx_int_t +ngx_http_lua_ssl_client_hello_handler_inline(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, + lscf->srv.ssl_client_hello_src.data, + lscf->srv.ssl_client_hello_src.len, + &lscf->srv.ssl_client_hello_src_ref, + lscf->srv.ssl_client_hello_src_key, + (const char *) lscf->srv.ssl_client_hello_chunkname); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_ssl_client_hello_by_chunk(L, r); +} + + +char * +ngx_http_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_http_lua_ssl_client_hello_by_lua; + cf->handler_conf = conf; + + rv = ngx_http_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_http_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#ifndef SSL_ERROR_WANT_CLIENT_HELLO_CB + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 1.1.1 required but found " + OPENSSL_VERSION_TEXT); + + return NGX_CONF_ERROR; + +#else + + size_t chunkname_len; + u_char *chunkname; + u_char *cache_key = NULL; + u_char *name; + ngx_str_t *value; + ngx_http_lua_srv_conf_t *lscf = conf; + + /* must specify a concrete handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->srv.ssl_client_hello_handler) { + return "is duplicate"; + } + + if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + lscf->srv.ssl_client_hello_handler = + (ngx_http_lua_srv_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_http_lua_ssl_client_hello_handler_file) { + /* Lua code in an external file */ + + name = ngx_http_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_client_hello_src.data = name; + lscf->srv.ssl_client_hello_src.len = ngx_strlen(name); + + } else { + cache_key = ngx_http_lua_gen_chunk_cache_key(cf, + "ssl_client_hello_by_lua", + value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + + chunkname = ngx_http_lua_gen_chunk_name(cf, "ssl_client_hello_by_lua", + sizeof("ssl_client_hello_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + /* Don't eval nginx variables for inline lua code */ + lscf->srv.ssl_client_hello_src = value[1]; + lscf->srv.ssl_client_hello_chunkname = chunkname; + } + + lscf->srv.ssl_client_hello_src_key = cache_key; + + return NGX_CONF_OK; + +#endif /* NO SSL_ERROR_WANT_CLIENT_HELLO_CB */ +} + + +int +ngx_http_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn, + int *al, void *arg) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c, *fc; + ngx_http_request_t *r = NULL; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_lua_srv_conf_t *lscf; + ngx_http_core_loc_conf_t *clcf; + ngx_http_lua_ssl_ctx_t *cctx; + ngx_http_core_srv_conf_t *cscf; + + c = ngx_ssl_get_connection(ssl_conn); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ssl client hello: connection reusable: %ud", c->reusable); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + dd("ssl client hello handler, client-hello-ctx=%p", cctx); + + if (cctx && cctx->entered_client_hello_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ssl_client_hello_by_lua: " + "client hello cb exit code: %d", + cctx->exit_code); + + dd("lua ssl client hello done, finally"); + return cctx->exit_code; + } + + return -1; + } + + dd("first time"); + +#if (nginx_version > 1029001) + /* see commit 0373fe5d98c1515640 for more details */ + rc = ngx_ssl_client_hello_callback(ssl_conn, al, arg); + + if (rc == 0) { + return rc; + } +#endif + +#if (nginx_version < 1017009) + ngx_reusable_connection(c, 0); +#endif + + hc = c->data; + + fc = ngx_http_lua_create_fake_connection(NULL); + if (fc == NULL) { + goto failed; + } + + fc->log->handler = ngx_http_lua_log_ssl_client_hello_error; + fc->log->data = fc; + + fc->addr_text = c->addr_text; + fc->listening = c->listening; + + r = ngx_http_lua_create_fake_request(fc); + if (r == NULL) { + goto failed; + } + + r->main_conf = hc->conf_ctx->main_conf; + r->srv_conf = hc->conf_ctx->srv_conf; + r->loc_conf = hc->conf_ctx->loc_conf; + + fc->log->file = c->log->file; + fc->log->log_level = c->log->log_level; + fc->ssl = c->ssl; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + +#if nginx_version >= 1009000 + ngx_set_connection_log(fc, clcf->error_log); +#else + ngx_http_set_connection_log(fc, clcf->error_log); +#endif + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + + cctx->ctx_ref = LUA_NOREF; + } + + cctx->exit_code = 1; /* successful by default */ + cctx->connection = c; + cctx->request = r; + cctx->entered_client_hello_handler = 1; + cctx->done = 0; + + dd("setting cctx"); + + if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx) + == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module); + + /* TODO honor lua_code_cache off */ + L = ngx_http_lua_get_lua_vm(r, NULL); + + c->log->action = "loading SSL client hello by lua"; + + if (lscf->srv.ssl_client_hello_handler == NULL) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "no ssl_client_hello_by_lua* defined in " + "server %V", &cscf->server_name); + + goto failed; + } + + rc = lscf->srv.ssl_client_hello_handler(r, lscf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ssl_client_hello_by_lua: handler return value: %i, " + "client hello cb exit code: %d", rc, cctx->exit_code); + + c->log->action = "SSL handshaking"; + return cctx->exit_code; + } + + /* rc == NGX_DONE */ + + cln = ngx_pool_cleanup_add(fc->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_http_lua_ssl_client_hello_done; + cln->data = cctx; + + if (cctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->data = cctx; + cctx->cleanup = &cln->handler; + } + + *cctx->cleanup = ngx_http_lua_ssl_client_hello_aborted; + + return -1; + +failed: + + if (r && r->pool) { + ngx_http_lua_free_fake_request(r); + } + + if (fc) { + ngx_http_lua_close_fake_connection(fc); + } + + return 0; +} + + +static void +ngx_http_lua_ssl_client_hello_done(void *data) +{ + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl client hello done"); + + if (cctx->aborted) { + return; + } + + ngx_http_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + c = cctx->connection; + + c->log->action = "SSL handshaking"; + + ngx_post_event(c->write, &ngx_posted_events); + +#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3) +# if defined(SSL_ERROR_WANT_CLIENT_HELLO_CB) +# if (NGX_QUIC_OPENSSL_COMPAT || NGX_QUIC_OPENSSL_API) + ngx_http_lua_resume_quic_ssl_handshake(c); +# endif +# endif +#endif +} + + +static void +ngx_http_lua_ssl_client_hello_aborted(void *data) +{ + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl client hello aborted"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, + "ssl_client_hello_by_lua: client hello cb aborted"); + + cctx->aborted = 1; + cctx->request->connection->ssl = NULL; + + ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR); +} + + +static u_char * +ngx_http_lua_log_ssl_client_hello_error(ngx_log_t *log, + u_char *buf, size_t len) +{ + u_char *p; + ngx_connection_t *c; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + p = ngx_snprintf(buf, len, ", context: ssl_client_hello_by_lua*"); + len -= p - buf; + buf = p; + + c = log->data; + + if (c && c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } + + if (c && c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } + + return buf; +} + + +static ngx_int_t +ngx_http_lua_ssl_client_hello_by_chunk(lua_State *L, ngx_http_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_http_lua_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + } else { + dd("reset ctx"); + ngx_http_lua_reset_ctx(r, L, ctx); + } + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_http_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine to handle request"); + + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_http_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_http_lua_set_req(co, r); + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx); + + /* register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + cln->handler = ngx_http_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + + ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO; + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_http_lua_finalize_request(r, rc); + return rc; +} + + +int +ngx_http_lua_ffi_ssl_get_client_hello_server_name(ngx_http_request_t *r, + const char **name, size_t *namelen, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + *err = "LibreSSL does not support by ssl_client_hello_by_lua*"; + return NGX_ERROR; +#else + ngx_ssl_conn_t *ssl_conn; +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + const unsigned char *p; + size_t remaining, len; +#endif + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + remaining = 0; + + /* This code block is taken from OpenSSL's client_hello_select_server_ctx() + * */ + if (!SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name, &p, + &remaining)) + { + return NGX_DECLINED; + } + + if (remaining <= 2) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + len = (*(p++) << 8); + len += *(p++); + if (len + 2 != remaining) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + remaining = len; + if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + remaining--; + if (remaining <= 2) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + len = (*(p++) << 8); + len += *(p++); + if (len + 2 > remaining) { + *err = "Bad SSL Client Hello Extension"; + return NGX_ERROR; + } + + remaining = len; + *name = (const char *) p; + *namelen = len; + + return NGX_OK; + +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; + +#endif + +#else + + *err = "no TLS extension support"; + return NGX_ERROR; +#endif +#endif /* LIBRESSL_VERSION_NUMBER */ +} + + +int +ngx_http_lua_ffi_ssl_get_client_hello_ext(ngx_http_request_t *r, + unsigned int type, const unsigned char **out, size_t *outlen, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + *err = "LibreSSL does not support by ssl_client_hello_by_lua*"; + return NGX_ERROR; +#else + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + if (SSL_client_hello_get0_ext(ssl_conn, type, out, outlen) == 0) { + return NGX_DECLINED; + } + + return NGX_OK; +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; +#endif +#endif /* LIBRESSL_VERSION_NUMBER */ +} + + +int +ngx_http_lua_ffi_ssl_get_client_hello_ext_present(ngx_http_request_t *r, + int **extensions, size_t *extensions_len, char **err) +{ +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + ngx_ssl_conn_t *ssl_conn; + int got_extensions; + size_t ext_len; + int *ext_out; + /* OPENSSL will allocate memory for us and make the ext_out point to it */ + + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + got_extensions = SSL_client_hello_get1_extensions_present(ssl_conn, + &ext_out, &ext_len); + if (!got_extensions || !ext_out || !ext_len) { + *err = "failed SSL_client_hello_get1_extensions_present()"; + return NGX_DECLINED; + } + + *extensions = ngx_palloc(r->connection->pool, sizeof(int) * ext_len); + if (*extensions != NULL) { + ngx_memcpy(*extensions, ext_out, sizeof(int) * ext_len); + *extensions_len = ext_len; + } + + OPENSSL_free(ext_out); + return NGX_OK; +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; +#endif +} + + +int ngx_http_lua_ffi_ssl_get_client_hello_ciphers(ngx_http_request_t *r, + unsigned short *ciphers, size_t ciphers_size, char **err) +{ +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + int i; + size_t ciphers_cnt; + size_t ciphersuites_bytes; + ngx_ssl_conn_t *ssl_conn; + const unsigned char *ciphers_raw; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + ciphersuites_bytes = SSL_client_hello_get0_ciphers(ssl_conn, &ciphers_raw); + + if (ciphersuites_bytes == 0) { + *err = "failed SSL_client_hello_get0_ciphers()"; + return NGX_DECLINED; + } + + if (ciphersuites_bytes % 2 != 0) { + *err = "SSL_client_hello_get0_ciphers() odd ciphersuites_bytes"; + return NGX_DECLINED; + } + + ciphers_cnt = ciphersuites_bytes / 2; + ciphers_cnt = ciphers_cnt > ciphers_size ? ciphers_size : ciphers_cnt; + + for (i = 0 ; i < (int) ciphers_cnt ; i++) { + ciphers[i] = (ciphers_raw[i * 2] << 8) | ciphers_raw[i * 2 + 1]; + } + + return ciphers_cnt; +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; +#endif +} + + +int +ngx_http_lua_ffi_ssl_set_protocols(ngx_http_request_t *r, + int protocols, char **err) +{ + + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#if OPENSSL_VERSION_NUMBER >= 0x009080dfL + /* only in 0.9.8m+ */ + SSL_clear_options(ssl_conn, + SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1); +#endif + + if (!(protocols & NGX_SSL_SSLv2)) { + SSL_set_options(ssl_conn, SSL_OP_NO_SSLv2); + } + + if (!(protocols & NGX_SSL_SSLv3)) { + SSL_set_options(ssl_conn, SSL_OP_NO_SSLv3); + } + + if (!(protocols & NGX_SSL_TLSv1)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1); + } + +#ifdef SSL_OP_NO_TLSv1_1 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_1); + if (!(protocols & NGX_SSL_TLSv1_1)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_1); + } +#endif + +#ifdef SSL_OP_NO_TLSv1_2 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_2); + if (!(protocols & NGX_SSL_TLSv1_2)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_2); + } +#endif + +#if defined(NGX_SSL_TLSv1_3) && defined( SSL_OP_NO_TLSv1_3) + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_3); + if (!(protocols & NGX_SSL_TLSv1_3)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_3); + } +#endif + + return NGX_OK; +} + +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_ssl_client_helloby.h b/src/ngx_http_lua_ssl_client_helloby.h new file mode 100644 index 0000000000..682a7cf292 --- /dev/null +++ b/src/ngx_http_lua_ssl_client_helloby.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +#if (NGX_HTTP_SSL) + +ngx_int_t ngx_http_lua_ssl_client_hello_handler_inline(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L); + +ngx_int_t ngx_http_lua_ssl_client_hello_handler_file(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L); + +char *ngx_http_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +char *ngx_http_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_http_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn, + int *al, void *arg); + + +#endif /* NGX_HTTP_SSL */ + + +#endif /* _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_ssl_export_keying_material.c b/src/ngx_http_lua_ssl_export_keying_material.c new file mode 100644 index 0000000000..ec64c049a9 --- /dev/null +++ b/src/ngx_http_lua_ssl_export_keying_material.c @@ -0,0 +1,125 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif + + +#include "ddebug.h" + +#if (NGX_HTTP_SSL) + +#include + +#include "ngx_http_lua_cache.h" +#include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_ssl_module.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_ssl_session_fetchby.h" +#include "ngx_http_lua_ssl.h" +#include "ngx_http_lua_directive.h" +#include "ngx_http_lua_ssl_export_keying_material.h" + + +ngx_int_t +ngx_http_lua_ffi_ssl_export_keying_material(ngx_http_request_t *r, + u_char *out, size_t out_size, const char *label, size_t llen, + const u_char *context, size_t ctxlen, int use_ctx, char **err) +{ +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x10101000L + *err = "BoringSSL does not support SSL_export_keying_material"; + return NGX_ERROR; +#elif defined(LIBRESSL_VERSION_NUMBER) + *err = "LibreSSL does not support SSL_export_keying_material"; + return NGX_ERROR; +#elif OPENSSL_VERSION_NUMBER < 0x10101000L + *err = "OpenSSL too old"; + return NGX_ERROR; +#else + ngx_connection_t *c; + ngx_ssl_conn_t *ssl_conn; + int rc; + + c = r->connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl connection"; + return NGX_ERROR; + } + + rc = SSL_export_keying_material(ssl_conn, out, out_size, label, llen, + context, ctxlen, use_ctx); + if (rc == 1) { + return NGX_OK; + } + + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, + "SSL_export_keying_material rc: %d, error: %s", + rc, ERR_error_string(ERR_get_error(), NULL)); + + *err = "SSL_export_keying_material() failed"; + + return NGX_ERROR; +#endif +} + + +ngx_int_t +ngx_http_lua_ffi_ssl_export_keying_material_early(ngx_http_request_t *r, + u_char *out, size_t out_size, const char *label, size_t llen, + const u_char *context, size_t ctxlen, char **err) +{ +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x10101000L + *err = "BoringSSL does not support SSL_export_keying_material"; + return NGX_ERROR; +#elif defined(LIBRESSL_VERSION_NUMBER) + *err = "LibreSSL does not support SSL_export_keying_material"; + return NGX_ERROR; +#elif OPENSSL_VERSION_NUMBER < 0x10101000L + *err = "OpenSSL too old"; + return NGX_ERROR; +#else + int rc; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + + c = r->connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl connection"; + return NGX_ERROR; + } + + rc = SSL_export_keying_material_early(ssl_conn, out, out_size, + label, llen, context, ctxlen); + + if (rc == 1) { + return NGX_OK; + } + + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, + "SSL_export_keying_material_early rc: %d, error: %s", + rc, ERR_error_string(ERR_get_error(), NULL)); + + *err = "SSL_export_keying_material_early() failed"; + + return NGX_ERROR; +#endif +} + +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_ssl_export_keying_material.h b/src/ngx_http_lua_ssl_export_keying_material.h new file mode 100644 index 0000000000..7a54bcda35 --- /dev/null +++ b/src/ngx_http_lua_ssl_export_keying_material.h @@ -0,0 +1,24 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_HTTP_LUA_SSL_EXPORT_KEYING_MATERIAL_H_INCLUDED_ +#define _NGX_HTTP_LUA_SSL_EXPORT_KEYING_MATERIAL_H_INCLUDED_ + +#include "ngx_http_lua_common.h" + +#if (NGX_HTTP_SSL) +ngx_int_t ngx_http_lua_ffi_ssl_export_keying_material(ngx_http_request_t *r, + u_char *out, size_t out_size, const char *label, size_t llen, + const u_char *ctx, size_t ctxlen, int use_ctx, char **err); + +ngx_int_t ngx_http_lua_ffi_ssl_export_keying_material_early( + ngx_http_request_t *r, u_char *out, size_t out_size, const char *label, + size_t llen, const u_char *ctx, size_t ctxlen, char **err); +#endif + +#endif /* _NGX_HTTP_LUA_SSL_EXPORT_KEYING_MATERIAL_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_ssl_ocsp.c b/src/ngx_http_lua_ssl_ocsp.c index d1805b23dd..73e1b9c7f5 100644 --- a/src/ngx_http_lua_ssl_ocsp.c +++ b/src/ngx_http_lua_ssl_ocsp.c @@ -19,8 +19,9 @@ #ifdef NGX_HTTP_LUA_USE_OCSP static int ngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn, void *data); -#endif +static long ngx_http_lua_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time); +#endif int ngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain( @@ -262,7 +263,7 @@ ngx_http_lua_ffi_ssl_create_ocsp_request(const char *chain_data, int ngx_http_lua_ffi_ssl_validate_ocsp_response(const u_char *resp, size_t resp_len, const char *chain_data, size_t chain_len, - u_char *errbuf, size_t *errbuf_size) + u_char *errbuf, size_t *errbuf_size, long *valid) { #ifndef NGX_HTTP_LUA_USE_OCSP @@ -279,7 +280,7 @@ ngx_http_lua_ffi_ssl_validate_ocsp_response(const u_char *resp, OCSP_RESPONSE *ocsp = NULL; OCSP_BASICRESP *basic = NULL; STACK_OF(X509) *chain = NULL; - ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; + ASN1_GENERALIZEDTIME *thisupdate = NULL, *nextupdate = NULL; ocsp = d2i_OCSP_RESPONSE(NULL, &resp, resp_len); if (ocsp == NULL) { @@ -383,6 +384,16 @@ ngx_http_lua_ffi_ssl_validate_ocsp_response(const u_char *resp, goto error; } + if (nextupdate) { + *valid = ngx_http_lua_ssl_stapling_time(nextupdate); + if (*valid == NGX_ERROR) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "invalid nextUpdate time " + "in certificate status") - errbuf; + goto error; + } + } + sk_X509_free(chain); X509_free(cert); X509_free(issuer); @@ -437,6 +448,40 @@ ngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) { return SSL_TLSEXT_ERR_OK; } + + +static long +ngx_http_lua_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time) +{ + BIO *bio; + char *value; + size_t len; + time_t time; + + /* + * OpenSSL doesn't provide a way to convert ASN1_GENERALIZEDTIME + * into long. To do this, we use ASN1_GENERALIZEDTIME_print(), + * which uses the "MMM DD HH:MM:SS YYYY [GMT]" format (e.g., + * "Feb 3 00:55:52 2015 GMT"), and parse the result. + */ + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + return NGX_ERROR; + } + + /* fake weekday prepended to match C asctime() format */ + + BIO_write(bio, "Tue ", sizeof("Tue ") - 1); + ASN1_GENERALIZEDTIME_print(bio, asn1time); + len = BIO_get_mem_data(bio, &value); + + time = ngx_parse_http_time((u_char *) value, len); + + BIO_free(bio); + + return time; +} #endif diff --git a/src/ngx_http_lua_ssl_session_fetchby.c b/src/ngx_http_lua_ssl_session_fetchby.c index f69cabb198..fee039fca8 100644 --- a/src/ngx_http_lua_ssl_session_fetchby.c +++ b/src/ngx_http_lua_ssl_session_fetchby.c @@ -66,7 +66,7 @@ ngx_http_lua_ssl_sess_fetch_handler_inline(ngx_http_request_t *r, lscf->srv.ssl_sess_fetch_src.len, &lscf->srv.ssl_sess_fetch_src_ref, lscf->srv.ssl_sess_fetch_src_key, - "=ssl_session_fetch_by_lua_block"); + (const char *) lscf->srv.ssl_sess_fetch_chunkname); if (rc != NGX_OK) { return rc; } @@ -102,6 +102,8 @@ char * ngx_http_lua_ssl_sess_fetch_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + size_t chunkname_len; + u_char *chunkname; u_char *cache_key = NULL; u_char *name; ngx_str_t *value; @@ -153,8 +155,15 @@ ngx_http_lua_ssl_sess_fetch_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_ERROR; } + chunkname = ngx_http_lua_gen_chunk_name(cf, "ssl_session_fetch_by_lua", + sizeof("ssl_session_fetch_by_lua") - 1, &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + /* Don't eval nginx variables for inline lua code */ lscf->srv.ssl_sess_fetch_src = value[1]; + lscf->srv.ssl_sess_fetch_chunkname = chunkname; } lscf->srv.ssl_sess_fetch_src_key = cache_key; @@ -407,7 +416,7 @@ ngx_http_lua_ssl_sess_fetch_aborted(void *data) { ngx_http_lua_ssl_ctx_t *cctx = data; - dd("lua ssl sess_fetch done"); + dd("lua ssl sess_fetch aborted"); if (cctx->done) { /* completed successfully already */ @@ -442,15 +451,18 @@ ngx_http_lua_log_ssl_sess_fetch_error(ngx_log_t *log, u_char *buf, size_t len) c = log->data; - if (c->addr_text.len) { - p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); - len -= p - buf; - buf = p; - } + if (c != NULL) { + if (c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } - if (c && c->listening && c->listening->addr_text.len) { - p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); - buf = p; + if (c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", + &c->listening->addr_text); + buf = p; + } } return buf; @@ -465,7 +477,7 @@ ngx_http_lua_ssl_sess_fetch_by_chunk(lua_State *L, ngx_http_request_t *r) ngx_int_t rc; lua_State *co; ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); @@ -519,7 +531,7 @@ ngx_http_lua_ssl_sess_fetch_by_chunk(lua_State *L, ngx_http_request_t *r) /* register request cleanup hooks */ if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { rc = NGX_ERROR; ngx_http_lua_finalize_request(r, rc); diff --git a/src/ngx_http_lua_ssl_session_storeby.c b/src/ngx_http_lua_ssl_session_storeby.c index 10803522a9..b25cbcc2c4 100644 --- a/src/ngx_http_lua_ssl_session_storeby.c +++ b/src/ngx_http_lua_ssl_session_storeby.c @@ -64,7 +64,7 @@ ngx_http_lua_ssl_sess_store_handler_inline(ngx_http_request_t *r, lscf->srv.ssl_sess_store_src.len, &lscf->srv.ssl_sess_store_src_ref, lscf->srv.ssl_sess_store_src_key, - "=ssl_session_store_by_lua_block"); + (const char *) lscf->srv.ssl_sess_store_chunkname); if (rc != NGX_OK) { return rc; } @@ -100,6 +100,8 @@ char * ngx_http_lua_ssl_sess_store_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + size_t chunkname_len; + u_char *chunkname; u_char *cache_key = NULL; u_char *name; ngx_str_t *value; @@ -151,8 +153,15 @@ ngx_http_lua_ssl_sess_store_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_ERROR; } + chunkname = ngx_http_lua_gen_chunk_name(cf, "ssl_session_store_by_lua", + sizeof("ssl_session_store_by_lua") - 1, &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + /* Don't eval nginx variables for inline lua code */ lscf->srv.ssl_sess_store_src = value[1]; + lscf->srv.ssl_sess_store_chunkname = chunkname; } lscf->srv.ssl_sess_store_src_key = cache_key; @@ -328,15 +337,18 @@ ngx_http_lua_log_ssl_sess_store_error(ngx_log_t *log, u_char *buf, size_t len) c = log->data; - if (c->addr_text.len) { - p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); - len -= p - buf; - buf = p; - } + if (c != NULL) { + if (c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } - if (c && c->listening && c->listening->addr_text.len) { - p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); - buf = p; + if (c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", + &c->listening->addr_text); + buf = p; + } } return buf; diff --git a/src/ngx_http_lua_string.c b/src/ngx_http_lua_string.c index 4c755f67f3..3028483609 100644 --- a/src/ngx_http_lua_string.c +++ b/src/ngx_http_lua_string.c @@ -424,6 +424,26 @@ ngx_http_lua_ffi_decode_base64(const u_char *src, size_t slen, u_char *dst, } +int +ngx_http_lua_ffi_decode_base64mime(const u_char *src, size_t slen, u_char *dst, + size_t *dlen) +{ + ngx_int_t rc; + ngx_str_t in, out; + + in.data = (u_char *) src; + in.len = slen; + + out.data = dst; + + rc = ngx_http_lua_decode_base64mime(&out, &in); + + *dlen = out.len; + + return rc == NGX_OK; +} + + size_t ngx_http_lua_ffi_unescape_uri(const u_char *src, size_t len, u_char *dst) { diff --git a/src/ngx_http_lua_subrequest.c b/src/ngx_http_lua_subrequest.c index a798f9bdab..c9a5c08622 100644 --- a/src/ngx_http_lua_subrequest.c +++ b/src/ngx_http_lua_subrequest.c @@ -52,6 +52,8 @@ ngx_str_t ngx_http_lua_patch_method = ngx_str_t ngx_http_lua_trace_method = ngx_http_lua_method_name("TRACE"); +ngx_str_t host_header = ngx_string("host"); + static ngx_str_t ngx_http_lua_content_length_header_key = ngx_string("Content-Length"); @@ -60,10 +62,10 @@ static ngx_str_t ngx_http_lua_content_length_header_key = static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, int forward_body, ngx_http_request_body_t *body, unsigned vars_action, - ngx_array_t *extra_vars); + ngx_array_t *extra_vars, ngx_array_t *extra_headers); static int ngx_http_lua_ngx_location_capture(lua_State *L); static int ngx_http_lua_ngx_location_capture_multi(lua_State *L); -static void ngx_http_lua_process_vars_option(ngx_http_request_t *r, +static void ngx_http_lua_process_keyval_option(ngx_http_request_t *r, lua_State *L, int table, ngx_array_t **varsp); static ngx_int_t ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *r, ngx_array_t *extra_vars); @@ -77,7 +79,7 @@ static void ngx_http_lua_cancel_subreq(ngx_http_request_t *r); static ngx_int_t ngx_http_post_request_to_head(ngx_http_request_t *r); static ngx_int_t ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r); static ngx_int_t ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, - ngx_http_request_t *pr, int pr_not_chunked); + ngx_http_request_t *pr, int pr_not_chunked, ngx_array_t *extra_headers); enum { @@ -125,6 +127,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) ngx_http_lua_ctx_t *sr_ctx; ngx_http_lua_ctx_t *ctx; ngx_array_t *extra_vars; + ngx_array_t *extra_headers; ngx_str_t uri; ngx_str_t args; ngx_str_t extra_args; @@ -170,12 +173,6 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) return luaL_error(L, "no request object found"); } -#if (NGX_HTTP_V2) - if (r->main->stream) { - return luaL_error(L, "http2 requests not supported yet"); - } -#endif - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); @@ -222,6 +219,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) coctx->pending_subreqs = 0; extra_vars = NULL; + extra_headers = NULL; for (index = 0; index < nsubreqs; index++) { coctx->pending_subreqs++; @@ -261,6 +259,11 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) extra_vars->nelts = 0; } + if (extra_headers != NULL) { + /* flush out existing elements in the array */ + extra_headers->nelts = 0; + } + vars_action = 0; custom_ctx = 0; @@ -316,7 +319,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) switch (lua_type(L, -1)) { case LUA_TTABLE: - ngx_http_lua_process_vars_option(r, L, -1, &extra_vars); + ngx_http_lua_process_keyval_option(r, L, -1, &extra_vars); dd("post process vars top: %d", lua_gettop(L)); break; @@ -333,6 +336,29 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) dd("queries query uri opts: %d", lua_gettop(L)); + /* check the headers option */ + + lua_getfield(L, 4, "headers"); + + switch (lua_type(L, -1)) { + case LUA_TTABLE: + ngx_http_lua_process_keyval_option(r, L, -1, &extra_headers); + + dd("post process vars top: %d", lua_gettop(L)); + break; + + case LUA_TNIL: + /* do nothing */ + break; + + default: + return luaL_error(L, "Bad headers option value"); + } + + lua_pop(L, 1); /* pop the headers */ + + dd("queries query uri opts: %d", lua_gettop(L)); + /* check the share_all_vars option */ lua_getfield(L, 4, "share_all_vars"); @@ -593,7 +619,8 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module); rc = ngx_http_lua_adjust_subrequest(sr, method, always_forward_body, - body, vars_action, extra_vars); + body, vars_action, extra_vars, + extra_headers); if (rc != NGX_OK) { ngx_http_lua_cancel_subreq(sr); @@ -629,7 +656,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, int always_forward_body, ngx_http_request_body_t *body, - unsigned vars_action, ngx_array_t *extra_vars) + unsigned vars_action, ngx_array_t *extra_vars, ngx_array_t *extra_headers) { ngx_http_request_t *r; ngx_http_core_main_conf_t *cmcf; @@ -665,7 +692,9 @@ ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, } } - if (ngx_http_lua_copy_request_headers(sr, r, pr_not_chunked) != NGX_OK) { + if (ngx_http_lua_copy_request_headers(sr, r, pr_not_chunked, extra_headers) + != NGX_OK) + { return NGX_ERROR; } @@ -734,7 +763,7 @@ ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, default: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "unsupported HTTP method: %u", (unsigned) method); + "unsupported HTTP method: %ui", method); return NGX_ERROR; } @@ -880,7 +909,7 @@ ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *sr, static void -ngx_http_lua_process_vars_option(ngx_http_request_t *r, lua_State *L, +ngx_http_lua_process_keyval_option(ngx_http_request_t *r, lua_State *L, int table, ngx_array_t **varsp) { ngx_array_t *vars; @@ -1344,7 +1373,9 @@ ngx_http_lua_subrequest(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr, ngx_http_post_subrequest_t *ps, ngx_uint_t flags) { +#if !defined freenginx ngx_time_t *tp; +#endif ngx_connection_t *c; ngx_http_request_t *sr; ngx_http_core_srv_conf_t *cscf; @@ -1472,9 +1503,13 @@ ngx_http_lua_subrequest(ngx_http_request_t *r, sr->subrequests = r->subrequests - 1; #endif +#if (defined freenginx && nginx_version >= 1029000) + sr->start_time = ngx_current_msec; +#else tp = ngx_timeofday(); sr->start_sec = tp->sec; sr->start_msec = tp->msec; +#endif r->main->count++; @@ -1633,10 +1668,11 @@ ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r) static ngx_int_t ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, - ngx_http_request_t *pr, int pr_not_chunked) + ngx_http_request_t *pr, int pr_not_chunked, ngx_array_t *extra_headers) { ngx_table_elt_t *clh, *header; ngx_list_part_t *part; + ngx_keyval_t *header_keyval; ngx_chain_t *in; ngx_uint_t i; u_char *p; @@ -1667,6 +1703,9 @@ ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, clh->hash = ngx_http_lua_content_length_hash; clh->key = ngx_http_lua_content_length_header_key; +#if defined(nginx_version) && nginx_version >= 1023000 + clh->next = NULL; +#endif clh->lowcase_key = ngx_pnalloc(sr->pool, clh->key.len); if (clh->lowcase_key == NULL) { return NGX_ERROR; @@ -1696,6 +1735,17 @@ ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, part = &pr->headers_in.headers.part; header = part->elts; +#if (NGX_HTTP_V3) + if (pr->headers_in.server.data != NULL) { + if (ngx_http_lua_set_input_header(sr, host_header, + pr->headers_in.server, 0) + == NGX_ERROR) + { + return NGX_ERROR; + } + } +#endif + for (i = 0; /* void */; i++) { if (i >= part->nelts) { @@ -1726,6 +1776,20 @@ ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, } } + if (extra_headers && extra_headers->nelts > 0) { + + header_keyval = extra_headers->elts; + + for (i = 0; i < extra_headers->nelts; i++, header_keyval++) { + + if (ngx_http_lua_set_input_header(sr, header_keyval->key, + header_keyval->value, 1) == NGX_ERROR) + { + return NGX_ERROR; + } + } + } + dd("after: parent req headers count: %d", (int) pr->headers_in.headers.part.nelts); diff --git a/src/ngx_http_lua_time.c b/src/ngx_http_lua_time.c index 61324f37a2..fa63115c4c 100644 --- a/src/ngx_http_lua_time.c +++ b/src/ngx_http_lua_time.c @@ -28,7 +28,21 @@ ngx_http_lua_ffi_now(void) double ngx_http_lua_ffi_req_start_time(ngx_http_request_t *r) { +#if (defined freenginx && nginx_version >= 1029000) + ngx_time_t *tp; + + tp = ngx_timeofday(); + tp->sec -= (ngx_current_msec - r->start_time) / 1000; + tp->msec -= (ngx_current_msec - r->start_time) % 1000; + if (tp->msec > NGX_MAX_INT_T_VALUE) { + tp->msec += 1000; + tp->sec -= 1; + } + + return tp->sec + tp->msec / 1000.0; +#else return r->start_sec + r->start_msec / 1000.0; +#endif } @@ -39,6 +53,13 @@ ngx_http_lua_ffi_time(void) } +long +ngx_http_lua_ffi_monotonic_msec(void) +{ + return (long) ngx_current_msec; +} + + void ngx_http_lua_ffi_update_time(void) { diff --git a/src/ngx_http_lua_timer.c b/src/ngx_http_lua_timer.c index 71886761a4..11d29349a5 100644 --- a/src/ngx_http_lua_timer.c +++ b/src/ngx_http_lua_timer.c @@ -173,6 +173,16 @@ ngx_http_lua_ngx_timer_helper(lua_State *L, int every) } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + ngx_http_lua_assert(ctx != NULL); + + /* + * Since nginx has been confirmed that all timers have been cleaned up when + * exit worker is executed, all timers will no longer be executed in exit + * worker phase. + * Reference https://github.com/nginx/nginx/blob/f02e2a734ef472f0dcf83ab2 + * e8ce96d1acead8a5/src/os/unix/ngx_process_cycle.c#L715 + */ + ngx_http_lua_check_context(L, ctx, ~NGX_HTTP_LUA_CONTEXT_EXIT_WORKER); if (ngx_exiting && delay > 0) { lua_pushnil(L); @@ -314,7 +324,7 @@ ngx_http_lua_ngx_timer_helper(lua_State *L, int every) tctx->client_addr_text.data = NULL; } - if (ctx && ctx->vm_state) { + if (ctx->vm_state) { tctx->vm_state = ctx->vm_state; tctx->vm_state->count++; @@ -509,7 +519,7 @@ ngx_http_lua_timer_handler(ngx_event_t *ev) ngx_connection_t *c = NULL; ngx_http_request_t *r = NULL; ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; + ngx_pool_cleanup_t *cln; ngx_pool_cleanup_t *pcln; ngx_http_lua_timer_ctx_t tctx; @@ -555,6 +565,8 @@ ngx_http_lua_timer_handler(ngx_event_t *ev) c = ngx_http_lua_create_fake_connection(tctx.pool); if (c == NULL) { errmsg = "could not create fake connection"; + /* tctx.pool is freed in ngx_http_lua_create_fake_connection */ + tctx.pool = NULL; goto failed; } @@ -608,7 +620,7 @@ ngx_http_lua_timer_handler(ngx_event_t *ev) L = ngx_http_lua_get_lua_vm(r, ctx); - cln = ngx_http_cleanup_add(r, 0); + cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { errmsg = "could not add request cleanup"; goto failed; @@ -733,16 +745,19 @@ ngx_http_lua_log_timer_error(ngx_log_t *log, u_char *buf, size_t len) len -= p - buf; buf = p; - if (c->addr_text.len) { - p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); - len -= p - buf; - buf = p; - } + if (c != NULL) { + if (c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } - if (c && c->listening && c->listening->addr_text.len) { - p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); - /* len -= p - buf; */ - buf = p; + if (c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", + &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } } return buf; @@ -808,7 +823,7 @@ ngx_http_lua_abort_pending_timers(ngx_event_t *ev) prev = NULL; events = ngx_pcalloc(ngx_cycle->pool, - lmcf->pending_timers * sizeof(ngx_event_t)); + lmcf->pending_timers * sizeof(ngx_event_t *)); if (events == NULL) { return; } @@ -862,7 +877,7 @@ ngx_http_lua_abort_pending_timers(ngx_event_t *ev) next = cur->parent; } else { - /* not reacheable */ + /* not reachable */ next = NULL; } diff --git a/src/ngx_http_lua_uri.c b/src/ngx_http_lua_uri.c index 0f88846fef..c4d65f2905 100644 --- a/src/ngx_http_lua_uri.c +++ b/src/ngx_http_lua_uri.c @@ -92,12 +92,14 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) return luaL_error(L, "no ctx found"); } - dd("rewrite: %d, access: %d, content: %d", + dd("server_rewrite: %d, rewrite: %d, access: %d, content: %d", + (int) ctx->entered_server_rewrite_phase, (int) ctx->entered_rewrite_phase, (int) ctx->entered_access_phase, (int) ctx->entered_content_phase); - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE); + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua set uri jump to \"%*s\"", len, p); diff --git a/src/ngx_http_lua_uthread.c b/src/ngx_http_lua_uthread.c index 5ed534c7f1..2208b2a819 100644 --- a/src/ngx_http_lua_uthread.c +++ b/src/ngx_http_lua_uthread.c @@ -125,6 +125,9 @@ ngx_http_lua_uthread_wait(lua_State *L) coctx = ctx->cur_co_ctx; nargs = lua_gettop(L); + if (nargs == 0) { + return luaL_error(L, "at least one coroutine should be specified"); + } for (i = 1; i <= nargs; i++) { sub_co = lua_tothread(L, i); @@ -268,7 +271,7 @@ ngx_http_lua_uthread_kill(lua_State *L) return 1; } - /* not reacheable */ + /* not reachable */ } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 36d6aa4cbd..96e9ae5b20 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -46,6 +46,13 @@ #include "ngx_http_lua_ssl_certby.h" #include "ngx_http_lua_ssl.h" #include "ngx_http_lua_log_ringbuf.h" +#if (NGX_THREADS) +#include "ngx_http_lua_worker_thread.h" +#endif +#if (NGX_HTTP_V3) +#include +#include +#endif #if 1 @@ -116,7 +123,6 @@ static int ngx_http_lua_thread_traceback(lua_State *L, lua_State *co, static void ngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log); static void ngx_http_lua_inject_arg_api(lua_State *L); -static int ngx_http_lua_param_get(lua_State *L); static int ngx_http_lua_param_set(lua_State *L); static ngx_int_t ngx_http_lua_output_filter(ngx_http_request_t *r, ngx_chain_t *in); @@ -278,7 +284,6 @@ ngx_http_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle, lua_pushliteral(L, LUA_DEFAULT_CPATH ";"); /* package default */ lua_getfield(L, -2, "cpath"); /* package default old */ - old_cpath = lua_tolstring(L, -1, &old_cpath_len); lua_concat(L, 2); /* package new */ lua_setfield(L, -2, "cpath"); /* package */ #endif @@ -373,8 +378,10 @@ ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *L, int *ref) if (!lua_isnil(L, -1) && !lua_isnil(L, -2)) { n++; } + lua_pop(L, 1); } + lua_pop(L, 1); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -383,10 +390,9 @@ ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *L, int *ref) } #endif - } else { -#else - { + } else #endif + { base = lua_gettop(L); lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( @@ -547,6 +553,10 @@ ngx_http_lua_send_header_if_needed(ngx_http_request_t *r, if (!ctx->buffering) { dd("sending headers"); rc = ngx_http_send_header(r); + if (r->filter_finalize) { + ngx_http_set_ctx(r, ctx, ngx_http_lua_module); + } + ctx->header_sent = 1; return rc; } @@ -597,6 +607,12 @@ ngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, if (r->header_only) { ctx->eof = 1; + if (!r->request_body && r == r->main) { + if (ngx_http_discard_request_body(r) != NGX_OK) { + return NGX_ERROR; + } + } + if (ctx->buffering) { return ngx_http_lua_send_http10_headers(r, ctx); } @@ -715,6 +731,10 @@ ngx_http_lua_output_filter(ngx_http_request_t *r, ngx_chain_t *in) ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return rc; + } + ngx_chain_update_chains(r->pool, &ctx->free_bufs, &ctx->busy_bufs, &in, (ngx_buf_tag_t) &ngx_http_lua_module); @@ -748,10 +768,6 @@ ngx_http_lua_send_http10_headers(ngx_http_request_t *r, } r->headers_out.content_length_n = size; - - if (r->headers_out.content_length) { - r->headers_out.content_length->hash = 0; - } } send: @@ -776,10 +792,13 @@ ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log) lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ - /* create the registry entry for the Lua request ctx data table */ - lua_pushliteral(L, ngx_http_lua_ctx_tables_key); - lua_createtable(L, 0, 32 /* nrec */); - lua_rawset(L, LUA_REGISTRYINDEX); + /* + * the the Lua request ctx data table will create in resty.core.ctx, + * just equivalent to the following code: + * lua_pushliteral(L, ngx_http_lua_ctx_tables_key); + * lua_createtable(L, 0, 0); + * lua_rawset(L, LUA_REGISTRYINDEX); + */ /* create the registry entry for the Lua socket connection pool table */ lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( @@ -816,7 +835,7 @@ static void ngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log) { - lua_createtable(L, 0 /* narr */, 113 /* nrec */); /* ngx.* */ + lua_createtable(L, 0 /* narr */, 115 /* nrec */); /* ngx.* */ lua_pushcfunction(L, ngx_http_lua_get_raw_phase_context); lua_setfield(L, -2, "_phase_ctx"); @@ -842,6 +861,9 @@ ngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf, ngx_http_lua_inject_uthread_api(log, L); ngx_http_lua_inject_timer_api(L); ngx_http_lua_inject_config_api(L); +#if (NGX_THREADS) + ngx_http_lua_inject_worker_thread_api(log, L); +#endif lua_getglobal(L, "package"); /* ngx package */ lua_getfield(L, -1, "loaded"); /* ngx package loaded */ @@ -991,6 +1013,7 @@ ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, ctx->entry_co_ctx.co_ref = LUA_NOREF; + ctx->entered_server_rewrite_phase = 0; ctx->entered_rewrite_phase = 0; ctx->entered_access_phase = 0; ctx->entered_content_phase = 0; @@ -1017,10 +1040,14 @@ ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r) ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - ctx->read_body_done = 1; - r->main->count--; + if (ctx == NULL) { + return; + } + + ctx->read_body_done = 1; + if (ctx->waiting_more_body) { ctx->waiting_more_body = 0; ngx_http_core_run_phases(r); @@ -1471,8 +1498,9 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, break; case LUA_ERRMEM: - err = "memory allocation error"; - ngx_quit = 1; + err = "[lua] memory allocation error"; + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, err); + abort(); break; case LUA_ERRERR: @@ -1655,6 +1683,12 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, done: +#ifdef HAVE_PROXY_SSL_PATCH + if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { + return NGX_OK; + } +#endif + if (ctx->entered_content_phase && r->connection->fd != (ngx_socket_t) -1) { @@ -2193,136 +2227,76 @@ ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) } +static int +ngx_http_lua_util_hex2int(char xdigit) +{ + if (isdigit(xdigit)) { + return xdigit - '0'; + } + + xdigit = tolower(xdigit); + if (xdigit <= 'f' && xdigit >= 'a') { + return xdigit - 'a' + 10; + } + + return -1; +} + + /* XXX we also decode '+' to ' ' */ void ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type) { - u_char *d, *s, ch, c, decoded; - enum { - sw_usual = 0, - sw_quoted, - sw_quoted_second, - } state; - - d = *dst; - s = *src; - - state = 0; - decoded = 0; + u_char *d = *dst, *s = *src, *de = (*dst + size); + int isuri = type & NGX_UNESCAPE_URI; + int isredirect = type & NGX_UNESCAPE_REDIRECT; while (size--) { + u_char curr = *s++; - ch = *s++; - - switch (state) { - case sw_usual: - if (ch == '?' - && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT))) - { - *d++ = ch; - goto done; - } - - if (ch == '%') { - state = sw_quoted; - break; - } - - if (ch == '+') { - *d++ = ' '; - break; - } - - *d++ = ch; + if (curr == '?' && + (type & (NGX_UNESCAPE_URI | NGX_UNESCAPE_REDIRECT))) + { + *d++ = '?'; break; - case sw_quoted: - - if (ch >= '0' && ch <= '9') { - decoded = (u_char) (ch - '0'); - state = sw_quoted_second; - break; - } - - c = (u_char) (ch | 0x20); - if (c >= 'a' && c <= 'f') { - decoded = (u_char) (c - 'a' + 10); - state = sw_quoted_second; - break; + } else if (curr == '%') { + u_char ch; + if (size < 2 || !(isxdigit(s[0]) && isxdigit(s[1]))) { + *d++ = '%'; + continue; } + /* we can be sure here they must be hex digits */ + ch = ngx_http_lua_util_hex2int(s[0]) * 16 + + ngx_http_lua_util_hex2int(s[1]); - /* the invalid quoted character */ - - state = sw_usual; - - *d++ = ch; - - break; - - case sw_quoted_second: - - state = sw_usual; - - if (ch >= '0' && ch <= '9') { - ch = (u_char) ((decoded << 4) + ch - '0'); - - if (type & NGX_UNESCAPE_REDIRECT) { - if (ch > '%' && ch < 0x7f) { - *d++ = ch; - break; - } - - *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); - break; - } - + if ((isuri || isredirect) && ch == '?') { *d++ = ch; - break; - } - - c = (u_char) (ch | 0x20); - if (c >= 'a' && c <= 'f') { - ch = (u_char) ((decoded << 4) + c - 'a' + 10); - - if (type & NGX_UNESCAPE_URI) { - if (ch == '?') { - *d++ = ch; - goto done; - } - - *d++ = ch; - break; - } - - if (type & NGX_UNESCAPE_REDIRECT) { - if (ch == '?') { - *d++ = ch; - goto done; - } - if (ch > '%' && ch < 0x7f) { - *d++ = ch; - break; - } - - *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); - break; - } - - *d++ = ch; - - break; + } else if (isredirect && (ch <= '%' || ch >= 0x7f)) { + *d++ = '%'; + continue; } - /* the invalid quoted character */ + *d++ = ch; + s += 2; + size -= 2; - break; + } else if (curr == '+') { + *d++ = ' '; + continue; + + } else { + *d++ = curr; } } -done: + /* a safe guard if dst need to be null-terminated */ + if (d != de) { + *d = '\0'; + } *dst = d; *src = s; @@ -2470,6 +2444,12 @@ ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, return ctx->exit_code; } +#ifdef HAVE_PROXY_SSL_PATCH + if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { + return ctx->exit_code; + } +#endif + #if 1 if (!r->header_sent && !ctx->header_sent @@ -3141,9 +3121,6 @@ ngx_http_lua_inject_arg_api(lua_State *L) lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* the metatable */ - lua_pushcfunction(L, ngx_http_lua_param_get); - lua_setfield(L, -2, "__index"); - lua_pushcfunction(L, ngx_http_lua_param_set); lua_setfield(L, -2, "__newindex"); @@ -3155,35 +3132,6 @@ ngx_http_lua_inject_arg_api(lua_State *L) } -static int -ngx_http_lua_param_get(lua_State *L) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_request_t *r; - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return 0; - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return luaL_error(L, "ctx not found"); - } - - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_SET - | NGX_HTTP_LUA_CONTEXT_BODY_FILTER); - - if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SET)) { - return ngx_http_lua_setby_param_get(L, r); - } - - /* ctx->context & (NGX_HTTP_LUA_CONTEXT_BODY_FILTER) */ - - return ngx_http_lua_body_filter_param_get(L, r); -} - - static int ngx_http_lua_param_set(lua_State *L) { @@ -3737,12 +3685,46 @@ void ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_http_lua_ctx_t *ctx; +#if (NGX_HTTP_SSL) +#ifdef HAVE_PROXY_SSL_PATCH + ngx_http_upstream_t *u; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx; +#endif +#endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx && ctx->cur_co_ctx) { ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); } +#if (NGX_HTTP_SSL) +#ifdef HAVE_PROXY_SSL_PATCH + u = r->upstream; + if (u) { + c = u->peer.connection; + if (c && c->ssl) { + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + if (cctx && cctx->pool) { + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + cctx->exit_code = 0; + } + + if (r->main->count > cctx->original_request_count) { + r->main->count--; + return; + } + + ngx_destroy_pool(cctx->pool); + cctx->pool = NULL; + + return; + } + } + } +#endif +#endif + if (r->connection->fd != (ngx_socket_t) -1) { ngx_http_finalize_request(r, rc); return; @@ -3894,6 +3876,18 @@ ngx_http_lua_close_fake_connection(ngx_connection_t *c) c->read->closed = 1; c->write->closed = 1; + /* When destroying the pool, the registered clean callbacks will be + * executed. If the ngx_connection_t is freed before these callbacks are + * run, and a new ngx_connection_t is created within a clean callback, + * it is possible for the freed ngx_connection_t to be reused again. + * If this reused ngx_connection_t is destroyed again within the clean + * callback logic, it may result in other clean callbacks holding a + * ngx_connection_t that has already been destroyed. + */ + if (pool) { + ngx_destroy_pool(pool); + } + /* we temporarily use a valid fd (0) to make ngx_free_connection happy */ c->fd = 0; @@ -3909,10 +3903,6 @@ ngx_http_lua_close_fake_connection(ngx_connection_t *c) if (ngx_cycle->files) { ngx_cycle->files[0] = saved_c; } - - if (pool) { - ngx_destroy_pool(pool); - } } @@ -4454,7 +4444,7 @@ ngx_http_lua_copy_escaped_header(ngx_http_request_t *r, escape = ngx_http_lua_escape_uri(NULL, data, len, type); if (escape > 0) { /* - * we allocate space for the trailling '\0' char here because nginx + * we allocate space for the trailing '\0' char here because nginx * header values must be null-terminated */ dst->data = ngx_palloc(r->pool, len + 2 * escape + 1); @@ -4470,4 +4460,222 @@ ngx_http_lua_copy_escaped_header(ngx_http_request_t *r, return NGX_OK; } + +ngx_int_t +ngx_http_lua_decode_base64mime(ngx_str_t *dst, ngx_str_t *src) +{ + size_t i; + u_char *d, *s, ch; + size_t data_len = 0; + u_char buf[4]; + size_t buf_len = 0; + static u_char basis[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + for (i = 0; i < src->len; i++) { + ch = src->data[i]; + if (ch == '=') { + break; + } + + if (basis[ch] == 77) { + continue; + } + + data_len++; + } + + if (data_len % 4 == 1) { + return NGX_ERROR; + } + + s = src->data; + d = dst->data; + + for (i = 0; i < src->len; i++) { + if (s[i] == '=') { + break; + } + + if (basis[s[i]] == 77) { + continue; + } + + buf[buf_len++] = s[i]; + if (buf_len == 4) { + *d++ = (u_char) (basis[buf[0]] << 2 | basis[buf[1]] >> 4); + *d++ = (u_char) (basis[buf[1]] << 4 | basis[buf[2]] >> 2); + *d++ = (u_char) (basis[buf[2]] << 6 | basis[buf[3]]); + buf_len = 0; + } + } + + if (buf_len > 1) { + *d++ = (u_char) (basis[buf[0]] << 2 | basis[buf[1]] >> 4); + } + + if (buf_len > 2) { + *d++ = (u_char) (basis[buf[1]] << 4 | basis[buf[2]] >> 2); + } + + dst->len = d - dst->data; + + return NGX_OK; +} + + +ngx_addr_t * +ngx_http_lua_parse_addr(lua_State *L, u_char *text, size_t len) +{ + ngx_addr_t *addr; + size_t socklen; + in_addr_t inaddr; + ngx_uint_t family; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct in6_addr inaddr6; + struct sockaddr_in6 *sin6; + + /* + * prevent MSVC8 warning: + * potentially uninitialized local variable 'inaddr6' used + */ + ngx_memzero(&inaddr6, sizeof(struct in6_addr)); +#endif + + inaddr = ngx_inet_addr(text, len); + + if (inaddr != INADDR_NONE) { + family = AF_INET; + socklen = sizeof(struct sockaddr_in); + +#if (NGX_HAVE_INET6) + + } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) { + family = AF_INET6; + socklen = sizeof(struct sockaddr_in6); +#endif + + } else { + return NULL; + } + + addr = lua_newuserdata(L, sizeof(ngx_addr_t) + socklen + len); + if (addr == NULL) { + luaL_error(L, "no memory"); + return NULL; + } + + addr->sockaddr = (struct sockaddr *) ((u_char *) addr + sizeof(ngx_addr_t)); + + ngx_memzero(addr->sockaddr, socklen); + + addr->sockaddr->sa_family = (u_char) family; + addr->socklen = socklen; + + switch (family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr->sockaddr; + ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr->sockaddr; + sin->sin_addr.s_addr = inaddr; + break; + } + + addr->name.data = (u_char *) addr->sockaddr + socklen; + addr->name.len = len; + ngx_memcpy(addr->name.data, text, len); + + return addr; +} + + +void +ngx_http_lua_ffi_bypass_if_checks(ngx_http_request_t *r) +{ + r->disable_not_modified = 1; +} + + +#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3) +void +ngx_http_lua_resume_quic_ssl_handshake(ngx_connection_t *c) +{ + ngx_int_t rc, sslerr; + ngx_ssl_conn_t *ssl_conn; + + if (c == NULL || c->ssl == NULL || c->ssl->connection == NULL) { + return; + } + + if (!c->udp || c->read == NULL + || c->read->handler != ngx_quic_input_handler) + { + return; + } + + ssl_conn = c->ssl->connection; + + /* Openresty ssl lua scripts are triggered by registering callbacks into + * openssl. If a lua script calls a yield api during execution, openssl's + * SSL_do_handshake will return a specific error code. The application + * should not treat these codes as fatal. The lua script will resume on + * yield-related events until it finishes. After completion, + * SSL_do_handshake should be called again to advance openssl's state + * machine. + * + * Note that nginx quic and openresty ssl lua scripts are independent. When + * openresty ssl lua runs, the client is waiting for a server response, so + * nginx quic does not affect the execution of the lua script. After the lua + * script finishes, SSL_do_handshake is called again, and nginx quic's + * registered callback continues the handshake. + */ + rc = SSL_do_handshake(ssl_conn); + sslerr = SSL_get_error(ssl_conn, rc); + + if (rc <= 0 && sslerr != SSL_ERROR_WANT_READ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + && sslerr != SSL_ERROR_WANT_X509_LOOKUP +#endif +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + && sslerr != SSL_ERROR_WANT_CLIENT_HELLO_CB +#endif + ) { + /* If a fatal error occurs or lua script exits with error during quic + * handshake, the quic connection will be closed immediately. + */ + ngx_quic_close_connection(c, NGX_ERROR); + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua resuming quic ssl handshake: rc %d, err %d, will " + "continue driving handshake or next lua script phase", + rc, sslerr); + } +} +#endif + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index 1768e5e28e..9d7a0bd1a3 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -32,21 +32,68 @@ #define NGX_HTTP_LUA_ESCAPE_HEADER_VALUE 8 +#ifdef HAVE_PROXY_SSL_PATCH + #define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE \ - | NGX_HTTP_LUA_CONTEXT_ACCESS \ - | NGX_HTTP_LUA_CONTEXT_CONTENT \ - | NGX_HTTP_LUA_CONTEXT_TIMER \ - | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE \ + | NGX_HTTP_LUA_CONTEXT_ACCESS \ + | NGX_HTTP_LUA_CONTEXT_CONTENT \ + | NGX_HTTP_LUA_CONTEXT_TIMER \ + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY \ + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO \ + | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) + +#else + +#define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE \ + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE \ + | NGX_HTTP_LUA_CONTEXT_ACCESS \ + | NGX_HTTP_LUA_CONTEXT_CONTENT \ + | NGX_HTTP_LUA_CONTEXT_TIMER \ + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO \ + | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) + +#endif /* HAVE_PROXY_SSL_PATCH */ /* key in Lua vm registry for all the "ngx.ctx" tables */ #define ngx_http_lua_ctx_tables_key "ngx_lua_ctx_tables" +#ifdef HAVE_PROXY_SSL_PATCH + +#define ngx_http_lua_context_name(c) \ + ((c) == NGX_HTTP_LUA_CONTEXT_SET ? "set_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? "rewrite_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE ? "server_rewrite_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_ACCESS ? "access_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_CONTENT ? "content_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_LOG ? "log_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_HEADER_FILTER ? "header_filter_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_BODY_FILTER ? "body_filter_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_TIMER ? "ngx.timer" \ + : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? "exit_worker_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY ? \ + "proxy_ssl_verify_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ + "ssl_client_hello_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE ? \ + "ssl_session_store_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH ? \ + "ssl_session_fetch_by_lua*" \ + : "(unknown)") + +#else + #define ngx_http_lua_context_name(c) \ ((c) == NGX_HTTP_LUA_CONTEXT_SET ? "set_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? "rewrite_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE ? "server_rewrite_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_ACCESS ? "access_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_CONTENT ? "content_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_LOG ? "log_by_lua*" \ @@ -56,6 +103,8 @@ : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? "exit_worker_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ + "ssl_client_hello_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE ? \ "ssl_session_store_by_lua*" \ @@ -63,6 +112,8 @@ "ssl_session_fetch_by_lua*" \ : "(unknown)") +#endif /* HAVE_PROXY_SSL_PATCH */ + #define ngx_http_lua_check_context(L, ctx, flags) \ if (!((ctx)->context & (flags))) { \ @@ -162,7 +213,7 @@ void ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, void ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r); -void ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int foricible); +void ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int forcible); void ngx_http_lua_request_cleanup_handler(void *data); @@ -256,9 +307,16 @@ void ngx_http_lua_cleanup_free(ngx_http_request_t *r, #if (NGX_HTTP_LUA_HAVE_SA_RESTART) void ngx_http_lua_set_sa_restart(ngx_log_t *log); #endif +ngx_int_t ngx_http_lua_decode_base64mime(ngx_str_t *dst, ngx_str_t *src); + +ngx_addr_t *ngx_http_lua_parse_addr(lua_State *L, u_char *text, size_t len); size_t ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size); +#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3) +void ngx_http_lua_resume_quic_ssl_handshake(ngx_connection_t *c); +#endif + static ngx_inline void ngx_http_lua_init_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) @@ -561,7 +619,7 @@ ngx_http_lua_free_thread(ngx_http_request_t *r, lua_State *L, int co_ref, { #ifdef HAVE_LUA_RESETTHREAD ngx_queue_t *q; - ngx_http_lua_thread_ref_t *tref ; + ngx_http_lua_thread_ref_t *tref; ngx_http_lua_ctx_t *ctx; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, @@ -645,10 +703,9 @@ ngx_http_lua_new_cached_thread(lua_State *L, lua_State **out_co, lua_rawget(L, LUA_REGISTRYINDEX); lua_rawgeti(L, -1, co_ref); - } else { -#else - { + } else #endif + { lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( coroutines_key)); lua_rawget(L, LUA_REGISTRYINDEX); @@ -683,6 +740,43 @@ ngx_http_lua_new_cached_thread(lua_State *L, lua_State **out_co, } +static ngx_inline void * +ngx_http_lua_hash_find_lc(ngx_hash_t *hash, ngx_uint_t key, u_char *name, + size_t len) +{ + ngx_uint_t i; + ngx_hash_elt_t *elt; + + elt = hash->buckets[key % hash->size]; + + if (elt == NULL) { + return NULL; + } + + while (elt->value) { + if (len != (size_t) elt->len) { + goto next; + } + + for (i = 0; i < len; i++) { + if (ngx_tolower(name[i]) != elt->name[i]) { + goto next; + } + } + + return elt->value; + + next: + + elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len, + sizeof(void *)); + continue; + } + + return NULL; +} + + extern ngx_uint_t ngx_http_lua_location_hash; extern ngx_uint_t ngx_http_lua_content_length_hash; diff --git a/src/ngx_http_lua_variable.c b/src/ngx_http_lua_variable.c index 573629ad21..2c6c6233bd 100644 --- a/src/ngx_http_lua_variable.c +++ b/src/ngx_http_lua_variable.c @@ -70,6 +70,18 @@ ngx_http_lua_ffi_var_get(ngx_http_request_t *r, u_char *name_data, } #endif +#if (NGX_HTTP_V3) + if (name_len == 9 + && r->http_version == NGX_HTTP_VERSION_30 + && ngx_strncasecmp(name_data, (u_char *) "http_host", 9) == 0 + && r->headers_in.server.data != NULL) + { + *value = r->headers_in.server.data; + *value_len = r->headers_in.server.len; + return NGX_OK; + } +#endif + hash = ngx_hash_strlow(lowcase_buf, name_data, name_len); name.data = lowcase_buf; diff --git a/src/ngx_http_lua_worker.c b/src/ngx_http_lua_worker.c index 8389dbeaec..5fdf0b4760 100644 --- a/src/ngx_http_lua_worker.c +++ b/src/ngx_http_lua_worker.c @@ -9,6 +9,10 @@ #endif #include "ddebug.h" +#if !(NGX_WIN32) +#include +#endif + #define NGX_PROCESS_PRIVILEGED_AGENT 99 @@ -20,6 +24,40 @@ ngx_http_lua_ffi_worker_pid(void) } +#if !(NGX_WIN32) +int +ngx_http_lua_ffi_worker_pids(int *pids, size_t *pids_len) +{ + size_t n; + ngx_int_t i; + + n = 0; + for (i = 0; n < *pids_len && i < NGX_MAX_PROCESSES; i++) { + if (i != ngx_process_slot && ngx_processes[i].pid == 0) { + break; + } + + /* The current process */ + if (i == ngx_process_slot) { + pids[n++] = ngx_pid; + } + + if (ngx_processes[i].channel[0] > 0 && ngx_processes[i].pid > 0) { + pids[n++] = ngx_processes[i].pid; + } + } + + if (n == 0) { + return NGX_ERROR; + } + + *pids_len = n; + + return NGX_OK; +} +#endif + + int ngx_http_lua_ffi_worker_id(void) { @@ -96,9 +134,13 @@ ngx_http_lua_ffi_get_process_type(void) return ngx_process; } - +#if defined(nginx_version) && nginx_version >= 1019003 +int +ngx_http_lua_ffi_enable_privileged_agent(char **err, unsigned int connections) +#else int ngx_http_lua_ffi_enable_privileged_agent(char **err) +#endif { #ifdef HAVE_PRIVILEGED_PROCESS_PATCH ngx_core_conf_t *ccf; @@ -107,6 +149,9 @@ ngx_http_lua_ffi_enable_privileged_agent(char **err) ngx_core_module); ccf->privileged_agent = 1; +#if defined(nginx_version) && nginx_version >= 1019003 + ccf->privileged_agent_connections = connections; +#endif return NGX_OK; diff --git a/src/ngx_http_lua_worker_thread.c b/src/ngx_http_lua_worker_thread.c new file mode 100644 index 0000000000..ad747e8104 --- /dev/null +++ b/src/ngx_http_lua_worker_thread.c @@ -0,0 +1,643 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + * Copyright (C) Jinhua Luo (kingluo) + * I hereby assign copyright in this code to the lua-nginx-module project, + * to be licensed under the same terms as the rest of the code. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_lua_worker_thread.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_lua_string.h" +#include "ngx_http_lua_config.h" +#include "ngx_http_lua_shdict.h" + +#ifndef STRINGIFY +#define TOSTRING(x) #x +#define STRINGIFY(x) TOSTRING(x) +#endif + +#if (NGX_THREADS) + + +#include +#include + +#define LUA_COPY_MAX_DEPTH 100 + +typedef struct ngx_http_lua_task_ctx_s { + lua_State *vm; + struct ngx_http_lua_task_ctx_s *next; +} ngx_http_lua_task_ctx_t; + + +typedef struct { + ngx_http_lua_task_ctx_t *ctx; + ngx_http_lua_co_ctx_t *wait_co_ctx; + int n_args; + int rc; + ngx_uint_t is_abort:1; +} ngx_http_lua_worker_thread_ctx_t; + + +static ngx_http_lua_task_ctx_t dummy_ctx; +static ngx_http_lua_task_ctx_t *ctxpool = &dummy_ctx; +static ngx_uint_t worker_thread_vm_count; + + +void +ngx_http_lua_thread_exit_process(void) +{ + ngx_http_lua_task_ctx_t *ctx; + + while (ctxpool->next != NULL) { + ctx = ctxpool->next; + ctxpool->next = ctx->next; + lua_close(ctx->vm); + ngx_free(ctx); + } +} + + +/* + * Re-implement ngx_thread_task_alloc to avoid alloc from request pool + * since the request may exit before worker thread finish. + * And we may implement a memory pool for this allocation in the future + * to avoid memory fragmentation. + */ +static ngx_thread_task_t * +ngx_http_lua_thread_task_alloc(size_t size) +{ + ngx_thread_task_t *task; + + task = ngx_calloc(sizeof(ngx_thread_task_t) + size, ngx_cycle->log); + if (task == NULL) { + return NULL; + } + + task->ctx = task + 1; + + return task; +} + + +static void +ngx_http_lua_thread_task_free(void *ctx) +{ + ngx_thread_task_t *task = ctx; + ngx_free(task - 1); +} + + +static ngx_http_lua_task_ctx_t * +ngx_http_lua_get_task_ctx(lua_State *L, ngx_http_request_t *r) +{ + ngx_http_lua_task_ctx_t *ctx = NULL; + + size_t path_len; + const char *path; + size_t cpath_len; + const char *cpath; + lua_State *vm; + + ngx_http_lua_main_conf_t *lmcf; + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + + if (ctxpool->next == NULL) { + if (worker_thread_vm_count >= lmcf->worker_thread_vm_pool_size) { + return NULL; + } + + ctx = ngx_calloc(sizeof(ngx_http_lua_task_ctx_t), ngx_cycle->log); + if (ctx == NULL) { + return NULL; + } + + vm = luaL_newstate(); + + if (vm == NULL) { + ngx_free(ctx); + return NULL; + } + + worker_thread_vm_count++; + + ctx->vm = vm; + + luaL_openlibs(vm); + + /* copy package.path and package.cpath */ + lua_getglobal(L, "package"); + lua_getfield(L, -1, "path"); + path = lua_tolstring(L, -1, &path_len); + lua_getfield(L, -2, "cpath"); + cpath = lua_tolstring(L, -1, &cpath_len); + + lua_getglobal(vm, "package"); + lua_pushlstring(vm, path, path_len); + lua_setfield(vm, -2, "path"); + lua_pushlstring(vm, cpath, cpath_len); + lua_setfield(vm, -2, "cpath"); + lua_pop(vm, 1); + + /* pop path, cpath and "package" table from L */ + lua_pop(L, 3); + + /* inject API from C */ + lua_newtable(vm); /* ngx.* */ + ngx_http_lua_inject_string_api(vm); + ngx_http_lua_inject_config_api(vm); + ngx_http_lua_inject_shdict_api(lmcf, vm); + lua_setglobal(vm, "ngx"); + + lua_getglobal(vm, "require"); + lua_pushstring(vm, "resty.core.hash"); + if (lua_pcall(vm, 1, 0, 0) != 0) { + lua_close(vm); + ngx_free(ctx); + return NULL; + } + + lua_getglobal(vm, "require"); + lua_pushstring(vm, "resty.core.base64"); + if (lua_pcall(vm, 1, 0, 0) != 0) { + lua_close(vm); + ngx_free(ctx); + return NULL; + } + + lua_getglobal(vm, "require"); + lua_pushstring(vm, "resty.core.shdict"); + if (lua_pcall(vm, 1, 0, 0) != 0) { + lua_close(vm); + ngx_free(ctx); + return NULL; + } + + } else { + ctx = ctxpool->next; + ctxpool->next = ctx->next; + ctx->next = NULL; + } + + return ctx; +} + + +static void +ngx_http_lua_free_task_ctx(ngx_http_lua_task_ctx_t *ctx) +{ + lua_State *vm; + + ctx->next = ctxpool->next; + ctxpool->next = ctx; + + /* clean Lua stack */ + vm = ctx->vm; + + /* call collectgarbage("collect") */ + lua_settop(vm, 0); + lua_getglobal(vm, "collectgarbage"); + lua_pushstring(vm, "collect"); + lua_pcall(vm, 1, 1, 0); + + lua_settop(vm, 0); +} + + +static int +ngx_http_lua_xcopy(lua_State *from, lua_State *to, int idx, + const int allow_nil, const int depth, const char **err) +{ + size_t len = 0; + const char *str; + int typ; + int top_from, top_to; + + typ = lua_type(from, idx); + switch (typ) { + case LUA_TBOOLEAN: + lua_pushboolean(to, lua_toboolean(from, idx)); + return LUA_TBOOLEAN; + + case LUA_TLIGHTUSERDATA: + lua_pushlightuserdata(to, lua_touserdata(from, idx)); + return LUA_TLIGHTUSERDATA; + + case LUA_TNUMBER: + lua_pushnumber(to, lua_tonumber(from, idx)); + return LUA_TNUMBER; + + case LUA_TSTRING: + str = lua_tolstring(from, idx, &len); + lua_pushlstring(to, str, len); + return LUA_TSTRING; + + case LUA_TTABLE: + if (depth >= LUA_COPY_MAX_DEPTH) { + *err = "suspicious circular references, " + "table depth exceed max depth: " + STRINGIFY(LUA_COPY_MAX_DEPTH); + return LUA_TNONE; + } + + top_from = lua_gettop(from); + top_to = lua_gettop(to); + + lua_newtable(to); + + /* to positive number */ + if (idx < 0) { + idx = lua_gettop(from) + idx + 1; + } + + lua_pushnil(from); + + while (lua_next(from, idx) != 0) { + if (ngx_http_lua_xcopy(from, to, -2, 0, depth + 1, err) != LUA_TNONE + && ngx_http_lua_xcopy(from, to, -1, 0, + depth + 1, err) != LUA_TNONE) + { + lua_rawset(to, -3); + + } else { + lua_settop(from, top_from); + lua_settop(to, top_to); + return LUA_TNONE; + } + + lua_pop(from, 1); + } + + return LUA_TTABLE; + + case LUA_TNIL: + if (allow_nil) { + lua_pushnil(to); + return LUA_TNIL; + } + + *err = "unsupported Lua type: LUA_TNIL"; + return LUA_TNONE; + + case LUA_TFUNCTION: + *err = "unsupported Lua type: LUA_TFUNCTION"; + return LUA_TNONE; + + case LUA_TUSERDATA: + *err = "unsupported Lua type: LUA_TUSERDATA"; + return LUA_TNONE; + + case LUA_TTHREAD: + *err = "unsupported Lua type: LUA_TTHREAD"; + return LUA_TNONE; + + default: + *err = "unsupported Lua type"; + return LUA_TNONE; + } +} + + +/* executed in a separate thread */ +static void +ngx_http_lua_worker_thread_handler(void *data, ngx_log_t *log) +{ + ngx_http_lua_worker_thread_ctx_t *ctx = data; + lua_State *vm = ctx->ctx->vm; + + /* function + args in the lua stack */ + ngx_http_lua_assert(lua_gettop(vm) == ctx->n_args + 1); + + ctx->rc = lua_pcall(vm, ctx->n_args, LUA_MULTRET, 0); +} + + +static ngx_int_t +ngx_http_lua_worker_thread_resume(ngx_http_request_t *r) +{ + lua_State *vm; + ngx_connection_t *c; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_http_lua_wev_handler; + + c = r->connection; + vm = ngx_http_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_http_lua_run_thread(vm, r, ctx, + ctx->cur_co_ctx->nresults_from_worker_thread); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +/* executed in nginx event loop */ +static void +ngx_http_lua_worker_thread_event_handler(ngx_event_t *ev) +{ + ngx_http_lua_worker_thread_ctx_t *worker_thread_ctx; + lua_State *L; + ngx_http_request_t *r; + ngx_connection_t *c; + int nresults; + size_t len; + const char *str; + int i; + ngx_http_lua_ctx_t *ctx; + lua_State *vm; + int saved_top; + const char *err; + + worker_thread_ctx = ev->data; + + if (worker_thread_ctx->is_abort) { + goto failed; + } + + L = worker_thread_ctx->wait_co_ctx->co; + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + goto failed; + } + + c = r->connection; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + goto failed; + } + + vm = worker_thread_ctx->ctx->vm; + + if (worker_thread_ctx->rc != 0) { + str = lua_tolstring(vm, 1, &len); + lua_pushboolean(L, 0); + lua_pushlstring(L, str, len); + nresults = 2; + + } else { + /* copying return values */ + saved_top = lua_gettop(L); + lua_pushboolean(L, 1); + nresults = lua_gettop(vm) + 1; + for (i = 1; i < nresults; i++) { + err = NULL; + if (ngx_http_lua_xcopy(vm, L, i, 1, 1, &err) == LUA_TNONE) { + lua_settop(L, saved_top); + lua_pushboolean(L, 0); + lua_pushfstring(L, "%s in the return value", + err != NULL ? err : "unsupoorted Lua type"); + nresults = 2; + break; + } + } + } + + ctx->cur_co_ctx = worker_thread_ctx->wait_co_ctx; + ctx->cur_co_ctx->nresults_from_worker_thread = nresults; + ctx->cur_co_ctx->cleanup = NULL; + + ngx_http_lua_free_task_ctx(worker_thread_ctx->ctx); + ngx_http_lua_thread_task_free(worker_thread_ctx); + + /* resume the caller coroutine */ + + if (ctx->entered_content_phase) { + (void) ngx_http_lua_worker_thread_resume(r); + + } else { + ctx->resume_handler = ngx_http_lua_worker_thread_resume; + ngx_http_core_run_phases(r); + } + + ngx_http_run_posted_requests(c); + + return; + +failed: + + ngx_http_lua_free_task_ctx(worker_thread_ctx->ctx); + ngx_http_lua_thread_task_free(worker_thread_ctx); + return; +} + + +static void +ngx_http_lua_worker_thread_cleanup(void *data) +{ + ngx_http_lua_co_ctx_t *ctx = data; + ngx_http_lua_worker_thread_ctx_t *worker_thread_ctx = ctx->data; + worker_thread_ctx->is_abort = 1; +} + + +/* It's not easy to use pure ffi here, using the Lua C API for now. */ +static int +ngx_http_lua_run_worker_thread(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + int n_args; + ngx_str_t thread_pool_name; + ngx_thread_pool_t *thread_pool; + ngx_http_lua_task_ctx_t *tctx; + lua_State *vm; + size_t len; + const char *mod_name; + const char *func_name; + int rc; + const char *err; + int i; + ngx_thread_task_t *task; + ngx_http_lua_worker_thread_ctx_t *worker_thread_ctx; + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + + n_args = lua_gettop(L); + + if (n_args < 3) { + lua_pushboolean(L, 0); + lua_pushstring(L, "expecting at least 3 arguments"); + return 2; + } + + thread_pool_name.data = (u_char *) + lua_tolstring(L, 1, &thread_pool_name.len); + + if (thread_pool_name.data == NULL) { + lua_pushboolean(L, 0); + lua_pushstring(L, "threadpool should be a string"); + return 2; + } + + thread_pool = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, + &thread_pool_name); + + if (thread_pool == NULL) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "thread pool %s not found", thread_pool_name.data); + return 2; + } + + mod_name = lua_tolstring(L, 2, &len); + if (mod_name == NULL) { + lua_pushboolean(L, 0); + lua_pushstring(L, "module name should be a string"); + return 2; + } + + func_name = lua_tolstring(L, 3, NULL); + if (func_name == NULL) { + lua_pushboolean(L, 0); + lua_pushstring(L, "function name should be a string"); + return 2; + } + + /* get vm */ + tctx = ngx_http_lua_get_task_ctx(L, r); + if (tctx == NULL) { + lua_pushboolean(L, 0); + lua_pushstring(L, "no available Lua vm"); + return 2; + } + + vm = tctx->vm; + + ngx_http_lua_assert(lua_gettop(vm) == 0); + + /* push function from module require */ + lua_getfield(vm, LUA_GLOBALSINDEX, "require"); + lua_pushlstring(vm, mod_name, len); + rc = lua_pcall(vm, 1, 1, 0); + + if (rc != 0) { + err = lua_tolstring(vm, 1, &len); + lua_pushboolean(L, 0); + lua_pushlstring(L, err, len); + ngx_http_lua_free_task_ctx(tctx); + return 2; + } + + if (!lua_istable(vm, -1)) { + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid lua module"); + ngx_http_lua_free_task_ctx(tctx); + return 2; + } + + lua_getfield(vm, -1, func_name); + if (!lua_isfunction(vm, -1)) { + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid function"); + ngx_http_lua_free_task_ctx(tctx); + return 2; + } + + /* remove the table returned by require */ + lua_remove(vm, 1); + + /* copying passed arguments */ + for (i = 4; i <= n_args; i++) { + err = NULL; + if (ngx_http_lua_xcopy(L, vm, i, 1, 1, &err) == LUA_TNONE) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "%s in the argument", + err != NULL ? err : "unsupoorted Lua type"); + ngx_http_lua_free_task_ctx(tctx); + return 2; + } + } + + /* post task */ + task = ngx_http_lua_thread_task_alloc( + sizeof(ngx_http_lua_worker_thread_ctx_t)); + + if (task == NULL) { + ngx_http_lua_free_task_ctx(tctx); + lua_pushboolean(L, 0); + lua_pushstring(L, "no memory"); + return 2; + } + + worker_thread_ctx = task->ctx; + + worker_thread_ctx->ctx = tctx; + worker_thread_ctx->wait_co_ctx = ctx->cur_co_ctx; + + ctx->cur_co_ctx->cleanup = ngx_http_lua_worker_thread_cleanup; + ctx->cur_co_ctx->data = worker_thread_ctx; + + worker_thread_ctx->n_args = n_args - 3; + worker_thread_ctx->rc = 0; + worker_thread_ctx->is_abort = 0; + + task->handler = ngx_http_lua_worker_thread_handler; + task->event.handler = ngx_http_lua_worker_thread_event_handler; + task->event.data = worker_thread_ctx; + + if (ngx_thread_task_post(thread_pool, task) != NGX_OK) { + ngx_http_lua_free_task_ctx(tctx); + ngx_http_lua_thread_task_free(task); + lua_pushboolean(L, 0); + lua_pushstring(L, "ngx_thread_task_post failed"); + return 2; + } + + return lua_yield(L, 0); +} + + +void +ngx_http_lua_inject_worker_thread_api(ngx_log_t *log, lua_State *L) +{ + lua_pushcfunction(L, ngx_http_lua_run_worker_thread); + lua_setfield(L, -2, "run_worker_thread"); +} + +#endif + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_worker_thread.h b/src/ngx_http_lua_worker_thread.h new file mode 100644 index 0000000000..2daa520a59 --- /dev/null +++ b/src/ngx_http_lua_worker_thread.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + * Copyright (C) Jinhua Luo (kingluo) + * I hereby assign copyright in this code to the lua-nginx-module project, + * to be licensed under the same terms as the rest of the code. + */ + +#ifndef _NGX_HTTP_LUA_WORKER_THREAD_H_INCLUDED_ +#define _NGX_HTTP_LUA_WORKER_THREAD_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +void ngx_http_lua_inject_worker_thread_api(ngx_log_t *log, lua_State *L); +void ngx_http_lua_thread_exit_process(void); + + +#endif /* _NGX_HTTP_LUA_WORKER_THREAD_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/t/000--init.t b/t/000--init.t index 0dd08fe188..0016e14486 100644 --- a/t/000--init.t +++ b/t/000--init.t @@ -11,7 +11,7 @@ $ENV{TEST_NGINX_MYSQL_PORT} ||= 3306; our $http_config = <<'_EOC_'; # lua-resty-string is required for lua-resty-mysql - lua_package_path "../lua-resty-mysql/lib/?.lua;../lua-resty-string/lib/?.lua;;"; + lua_package_path "../lua-resty-rsa/lib/?.lua;../lua-resty-mysql/lib/?.lua;../lua-resty-string/lib/?.lua;;"; _EOC_ no_shuffle(); diff --git a/t/001-set.t b/t/001-set.t index 62dec1a7f9..009159e484 100644 --- a/t/001-set.t +++ b/t/001-set.t @@ -389,8 +389,8 @@ GET /lua GET /lua --- response_body_like: 500 Internal Server Error --- error_code: 500 ---- error_log -API disabled in the context of set_by_lua* +--- error_log eval +qr/(?:API disabled in the context of set_by_lua\*|http3 requests are not supported without content-length header)/ms @@ -404,8 +404,16 @@ API disabled in the context of set_by_lua* GET /lua --- response_body_like: 500 Internal Server Error --- error_code: 500 ---- error_log -API disabled in the context of set_by_lua* +--- error_log eval +my $err_log; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $err_log = "http v3 not supported yet"; +} else { + $err_log = "API disabled in the context of set_by_lua*"; +} + +$err_log; @@ -573,7 +581,7 @@ GET /lua --- response_body_like: 500 Internal Server Error --- error_code: 500 --- error_log -failed to run set_by_lua*: set_by_lua:1: Bad +failed to run set_by_lua*: set_by_lua(nginx.conf:40):1: Bad diff --git a/t/002-content.t b/t/002-content.t index d6a3600444..26070f5287 100644 --- a/t/002-content.t +++ b/t/002-content.t @@ -10,7 +10,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); #repeat_each(1); -plan tests => repeat_each() * (blocks() * 2 + 24); +plan tests => repeat_each() * (blocks() * 2 + 32); #no_diff(); #no_long_string(); @@ -849,4 +849,275 @@ GET /lua --- response_body_like: 500 Internal Server Error --- error_code: 500 --- error_log eval -qr/failed to load inlined Lua code: / +qr/failed to load inlined Lua code: content_by_lua\(nginx.conf:40\)/ + + + +=== TEST 43: syntax error in content_by_lua_block +--- config + location /lua { + + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log eval +qr/failed to load inlined Lua code: content_by_lua\(nginx.conf:41\)/ + + + +=== TEST 44: syntax error in second content_by_lua_block +--- config + location /foo { + content_by_lua_block { + 'for end'; + } + } + + location /lua { + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log eval +qr/failed to load inlined Lua code: content_by_lua\(nginx.conf:46\)/ + + + +=== TEST 45: syntax error in thrid content_by_lua_block +--- config + location /foo { + content_by_lua_block { + 'for end'; + } + } + + location /bar { + content_by_lua_block { + 'for end'; + } + } + + location /lua { + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log eval +qr/failed to load inlined Lua code: content_by_lua\(nginx.conf:52\)/ + + + +=== TEST 46: syntax error in included file +--- config + location /foo { + content_by_lua_block { + 'for end'; + } + } + + location /bar { + content_by_lua_block { + 'for end'; + } + } + + include ../html/lua.conf; +--- user_files +>>> lua.conf + location /lua { + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log +failed to load inlined Lua code: content_by_lua(../html/lua.conf:2):2: unexpected symbol near ''for end'' + + + +=== TEST 47: syntax error with very long filename +--- config + location /foo { + content_by_lua_block { + 'for end'; + } + } + + location /bar { + content_by_lua_block { + 'for end'; + } + } + + include ../html/1234567890123456789012345678901234.conf; +--- user_files +>>> 1234567890123456789012345678901234.conf + location /lua { + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log +failed to load inlined Lua code: content_by_lua(...234567890123456789012345678901234.conf:2) + + + +=== TEST 48: syntax error in /tmp/lua.conf +--- config + location /foo { + content_by_lua_block { + 'for end'; + } + } + + location /bar { + content_by_lua_block { + 'for end'; + } + } + + include /tmp/lua.conf; +--- user_files +>>> /tmp/lua.conf + location /lua { + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log +failed to load inlined Lua code: content_by_lua(/tmp/lua.conf:2) + + + +=== TEST 49: syntax error in /tmp/12345678901234567890123456789012345.conf +--- config + location /foo { + content_by_lua_block { + 'for end'; + } + } + + location /bar { + content_by_lua_block { + 'for end'; + } + } + + include /tmp/12345678901234567890123456789012345.conf; + +--- user_files +>>> /tmp/12345678901234567890123456789012345.conf + location /lua { + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log +failed to load inlined Lua code: content_by_lua(...345678901234567890123456789012345.conf:2) + + + +=== TEST 50: the error line number greater than 9 +--- config + location /foo { + content_by_lua_block { + 'for end'; + } + } + + location /bar { + content_by_lua_block { + 'for end'; + } + } + + include /tmp/12345678901234567890123456789012345.conf; + +--- user_files +>>> /tmp/12345678901234567890123456789012345.conf + location /lua { + + + + + + + + + + + + + content_by_lua_block { + 'for end'; + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log +failed to load inlined Lua code: content_by_lua(...45678901234567890123456789012345.conf:14) + + + +=== TEST 51: Lua file permission denied +--- config + location /lua { + content_by_lua_file /etc/shadow; + } +--- request +GET /lua +--- response_body_like: 503 Service Temporarily Unavailable +--- error_code: 503 + + + +=== TEST 52: send_header trigger filter finalize does not clear the ctx +--- config + location /lua { + content_by_lua_block { + ngx.header["Last-Modified"] = ngx.http_time(ngx.time()) + ngx.send_headers() + local phase = ngx.get_phase() + } + header_filter_by_lua_block { + ngx.header["X-Hello-World"] = "Hello World" + } + } +--- request +GET /lua +--- more_headers +If-Unmodified-Since: Wed, 01 Jan 2020 07:28:00 GMT +--- error_code: 412 +--- no_error_log +unknown phase: 0 +--- skip_eval: 2:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) diff --git a/t/005-exit.t b/t/005-exit.t index ef4e0f3031..7b82a81e4c 100644 --- a/t/005-exit.t +++ b/t/005-exit.t @@ -65,7 +65,7 @@ GET /lua --- error_log attempt to set status 404 via ngx.exit after sending out the response status 200 --- no_error_log -alert +[alert] --- response_body hi @@ -123,6 +123,8 @@ GET /api?user=agentz === TEST 6: working with ngx_auth_request (simplest form, w/o ngx_memc) +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -195,6 +197,8 @@ Logged in 56 === TEST 7: working with ngx_auth_request (simplest form) +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -267,6 +271,8 @@ Logged in 56 === TEST 8: working with ngx_auth_request +--- no_http2 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -653,6 +659,7 @@ GET /t --- response_body_like: 403 Forbidden --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -671,6 +678,7 @@ GET /t --- response_body --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -707,6 +715,7 @@ F(ngx_http_lua_header_filter_by_chunk).return { --- no_error_log [error] [alert] +--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -724,3 +733,105 @@ GET /t --- response_body --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) + + + +=== TEST 25: 501 Method Not Implemented +--- config + location /lua { + content_by_lua ' + ngx.exit(ngx.HTTP_NOT_IMPLEMENTED) + '; + } +--- request +GET /lua +--- error_code: 501 +--- response_body_like: 501 (?:Method )?Not Implemented +--- no_error_log +[error] + + + +=== TEST 26: accepts NGX_OK +--- config + location = /t { + content_by_lua_block { + ngx.exit(ngx.OK) + } + } +--- request +GET /t +--- response_body +--- no_error_log +[error] + + + +=== TEST 27: accepts NGX_ERROR +--- no_http2 +--- config + location = /t { + content_by_lua_block { + ngx.exit(ngx.ERROR) + } + } +--- request +GET /t +--- error_code: +--- response_body +--- no_error_log +[error] +--- curl_error +curl: (95) HTTP/3 stream 0 reset by server + + + +=== TEST 28: accepts NGX_DECLINED +--- no_http2 +--- config + location = /t { + content_by_lua_block { + ngx.exit(ngx.DECLINED) + } + } +--- request +GET /t +--- error_code: +--- response_body +--- no_error_log +[error] +--- curl_error +curl: (95) HTTP/3 stream 0 reset by server + + + +=== TEST 29: refuses NGX_AGAIN +--- config + location = /t { + content_by_lua_block { + ngx.exit(ngx.AGAIN) + } + } +--- request +GET /t +--- error_code: 500 +--- response_body_like: 500 Internal Server Error +--- error_log eval +qr/\[error\] .*? bad argument to 'ngx.exit': does not accept NGX_AGAIN or NGX_DONE/ + + + +=== TEST 30: refuses NGX_DONE +--- config + location = /t { + content_by_lua_block { + ngx.exit(ngx.DONE) + } + } +--- request +GET /t +--- error_code: 500 +--- response_body_like: 500 Internal Server Error +--- error_log eval +qr/\[error\] .*? bad argument to 'ngx.exit': does not accept NGX_AGAIN or NGX_DONE/ diff --git a/t/006-escape.t b/t/006-escape.t index aadf167552..28714ab893 100644 --- a/t/006-escape.t +++ b/t/006-escape.t @@ -294,3 +294,37 @@ GET /lua --- error_code: 500 --- error_log eval qr/\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: "type" is not a number/ + + + +=== TEST 20: invalid unescape sequences +--- config + location /lua { + content_by_lua_block { + ngx.say(ngx.unescape_uri("%ua%%20%au")) + } + } +--- request +GET /lua +--- response_body +%ua% %au + + + +=== TEST 21: invalid unescape sequences where remain length less than 2 +--- config + location /lua { + content_by_lua_block { + ngx.say(ngx.unescape_uri("%a")) -- first character is good + ngx.say(ngx.unescape_uri("%u")) -- first character is bad + ngx.say(ngx.unescape_uri("%")) + ngx.say(ngx.unescape_uri("good%20job%")) + } + } +--- request +GET /lua +--- response_body +%a +%u +% +good job% diff --git a/t/009-log.t b/t/009-log.t index c4597698ef..4446c71277 100644 --- a/t/009-log.t +++ b/t/009-log.t @@ -243,7 +243,7 @@ GET /log --- response_body 32 --- error_log eval -qr/\[error\] \S+: \S+ \[lua\] set_by_lua:2: HELLO,/ +qr/\[error\] \S+: \S+ \[lua\] set_by_lua\(nginx.conf:43\):2: HELLO,/ @@ -261,7 +261,7 @@ GET /log --- response_body 32 --- error_log eval -qr/\[error\] \S+: \S+ \[lua\] set_by_lua:2: truefalsenil,/ +qr/\[error\] \S+: \S+ \[lua\] set_by_lua\(nginx.conf:43\):2: truefalsenil,/ @@ -368,7 +368,7 @@ GET /log --- response_headers foo: 32 --- error_log eval -qr/\[notice\] .*? \[lua\] header_filter_by_lua:2: hello world/ +qr/\[notice\] .*? \[lua\] header_filter_by_lua\(nginx.conf:43\):2: hello world/ --- response_body hi @@ -390,7 +390,7 @@ foo: 32 --- response_body hi --- error_log eval -qr/\[error\] .*? \[lua\] header_filter_by_lua:2: howdy, lua!/ +qr/\[error\] .*? \[lua\] header_filter_by_lua\(nginx.conf:43\):2: howdy, lua!/ diff --git a/t/010-request_body.t b/t/010-request_body.t index e669d9480e..c47f9fc13d 100644 --- a/t/010-request_body.t +++ b/t/010-request_body.t @@ -270,3 +270,22 @@ Expect: 100-Continue http finalize request: 500, "/echo_body?" a:1, c:2 http finalize request: 500, "/echo_body?" a:1, c:0 --- log_level: debug +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} + + + +=== TEST 13: test reading the first n bytes of request body +--- config + location /echo_body { + lua_need_request_body on; + content_by_lua_block { + local data = ngx.req.get_body_data(1) + ngx.say(data) + } + } +--- request +POST /echo_body +hello +--- response_body +h +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/014-bugs.t b/t/014-bugs.t index dad084ec4d..b1c7d442ae 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -8,7 +8,8 @@ log_level('debug'); repeat_each(3); -plan tests => repeat_each() * (blocks() * 2 + 30); +# NB: the shutdown_error_log block is independent from repeat times +plan tests => repeat_each() * (blocks() * 2 + 41); our $HtmlDir = html_dir; #warn $html_dir; @@ -193,6 +194,14 @@ Hi" === TEST 8: github issue 37: header bug https://github.com/chaoslawful/lua-nginx-module/issues/37 + +https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2 + Just as in HTTP/1.x, header field names are strings of ASCII + characters that are compared in a case-insensitive fashion. However, + header field names MUST be converted to lowercase prior to their + encoding in HTTP/2. A request or response containing uppercase + header field names MUST be treated as malformed +--- no_http2 --- config location /sub { content_by_lua ' @@ -219,8 +228,17 @@ https://github.com/chaoslawful/lua-nginx-module/issues/37 --- request GET /lua --- raw_response_headers_like eval -".*Set-Cookie: TestCookie1=foo\r +my $headers; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $headers = ".*set-cookie: TestCookie1=foo\r +set-cookie: TestCookie2=bar.*" +} else { + $headers = ".*Set-Cookie: TestCookie1=foo\r Set-Cookie: TestCookie2=bar.*" +} + +$headers; @@ -713,6 +731,7 @@ Content-Type: application/json; charset=utf-8 === TEST 32: hang on upstream_next (from kindy) +--- no_http2 --- no_check_leak --- http_config upstream xx { @@ -769,6 +788,10 @@ eof found in body stream === TEST 34: testing a segfault when using ngx_poll_module + ngx_resolver See more details here: http://mailman.nginx.org/pipermail/nginx-devel/2013-January/003275.html + +http3 may cache the dns result. +so need to skip for http3 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} --- config location /t { set $myserver nginx.org; @@ -785,6 +808,8 @@ See more details here: http://mailman.nginx.org/pipermail/nginx-devel/2013-Janua [alert] --- error_log eval qr/(?:send|recv)\(\) failed \(\d+: Connection refused\) while resolving/ +--- curl_error eval +qr/curl: \(28\) Operation timed out after \d+ milliseconds with 0 bytes received/ @@ -803,7 +828,7 @@ qr/(?:send|recv)\(\) failed \(\d+: Connection refused\) while resolving/ rewrite ^/myproxy/(.*) /$1 break; resolver_timeout 3s; #resolver 172.16.0.23; # AWS DNS resolver address is the same in all regions - 172.16.0.23 - resolver 8.8.8.8; + resolver $TEST_NGINX_RESOLVER; proxy_read_timeout 1s; proxy_send_timeout 1s; proxy_connect_timeout 1s; @@ -860,6 +885,7 @@ GET /t --- no_error_log [error] --- timeout: 10 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -867,7 +893,7 @@ GET /t --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; server { - listen 12354; + listen \$TEST_NGINX_RAND_PORT_1; location = /t { echo 'args: \$args'; @@ -877,7 +903,7 @@ GET /t --- config location = /t { set $args "foo=1&bar=2"; - proxy_pass http://127.0.0.1:12354; + proxy_pass http://127.0.0.1:$TEST_NGINX_RAND_PORT_1; } --- request @@ -1019,3 +1045,413 @@ write timer set: 1 --- no_error_log [error] [alert] + + + +=== TEST 42: tcp: nginx crash when resolve an not exist domain in ngx.thread.spawn +https://github.com/openresty/lua-nginx-module/issues/1915 +--- config + resolver $TEST_NGINX_RESOLVER ipv6=off; + location = /t { + content_by_lua_block { + local function tcp(host, port) + local sock = ngx.socket.tcp() + local ok,err = sock:connect(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "nonexistent.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(tcp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + } + +--- request +GET /t +--- response_body +res: false +--- error_log +nonexistent.openresty.org could not be resolved + + + +=== TEST 43: domain exists with tcp socket +https://github.com/openresty/lua-nginx-module/issues/1915 +--- config + resolver $TEST_NGINX_RESOLVER ipv6=off; + location = /t { + content_by_lua_block { + local function tcp(host, port) + local sock = ngx.socket.tcp() + local ok,err = sock:connect(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "www.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(tcp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + } + +--- request +GET /t +--- response_body +res: true + + + +=== TEST 44: domain exists with udp socket +https://github.com/openresty/lua-nginx-module/issues/1915 +--- config + resolver $TEST_NGINX_RESOLVER ipv6=off; + location = /t { + content_by_lua_block { + local function udp(host, port) + local sock = ngx.socket.udp() + local ok,err = sock:setpeername(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "nonexistent.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(udp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + } + +--- request +GET /t +--- response_body +res: false +--- error_log +nonexistent.openresty.org could not be resolved + + + +=== TEST 45: udp: nginx crash when resolve an not exist domain in ngx.thread.spawn +https://github.com/openresty/lua-nginx-module/issues/1915 +--- config + resolver $TEST_NGINX_RESOLVER ipv6=off; + location = /t { + content_by_lua_block { + local function udp(host, port) + local sock = ngx.socket.udp() + local ok,err = sock:setpeername(host, port) + if not ok then + ngx.log(ngx.WARN, "failed: ", err) + sock:close() + return false + end + + sock:close() + return true + end + + local host = "www.openresty.org" + local port = 80 + + local threads = {} + for i = 1, 3 do + threads[i] = ngx.thread.spawn(udp, host, port) + end + + local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3]) + if not ok then + ngx.say("failed to wait thread") + return + end + + ngx.say("res: ", res) + + for i = 1, 3 do + ngx.thread.kill(threads[i]) + end + } + } + +--- request +GET /t +--- response_body +res: true + + + +=== TEST 46: nginx crash when parsing a word or a single configuration item that is too long +https://github.com/openresty/lua-nginx-module/issues/1938 +--- http_config + init_worker_by_lua ' + err_big_str = 'A NA' + '; +--- config + location /t { + content_by_lua ' + ngx.say("hello world") + '; + } +--- request +GET /t +--- response_body +res: true +--- no_error_log +[error] +--- must_die +--- error_log eval +qr/\[emerg\] \d+#\d+: unexpected "A" in/ + + + +=== TEST 47: cosocket does not exit on worker_shutdown_timeout +This test must enable master process +--- SKIP +--- main_config +worker_shutdown_timeout 1; +--- config +location /t { + content_by_lua_block { + local function thread_func(port) + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", port) + local bytes, err = sock:send("hello") + if bytes ~= 5 then + sock:close() + return ngx.exit(500) + end + + local data, err = sock:receive(20) + local line, err, partial = sock:receive() + if not line then + ngx.log(ngx.ERR, "failed to read a line: ", err) + return + end + + ngx.log(ngx.ERR, "successfully read a line: ", line) + end + + local function timer_func(port) + ngx.thread.spawn(thread_func, port) + end + + ngx.timer.at(1, timer_func, ngx.var.server_port) + ngx.say("Hello world") + } +} +--- request + GET /t +--- response_body +Hello world +--- shutdown_error_log eval +my $expr; + +if ($ENV{TEST_NGINX_USE_HTTP3}) { + $expr = qr|lua close the global Lua VM| +} else { + $expr = qr/failed to read a line: closed|attempt to send data on a closed socket/ +} + +$expr; +--- timeout: 1.2 + + + +=== TEST 48: nginx crashes when encountering an illegal http if header +crash with ngx.send_headers() +--- main_config +--- config +error_page 412 /my_error_handler_412; + +location /t { + rewrite_by_lua_block { + ngx.send_headers() + -- ngx.print() -- this also triggers the bug + } +} +location = /my_error_handler_412 { + return 412 "hello"; +} +--- request + GET /t +--- more_headers +If-Match: 1 +--- error_code: 412 +--- response_body eval +qr/\Ahello\z/ + + + +=== TEST 49: nginx crashes when encountering an illegal http if header +crash with ngx.print() +--- main_config +--- config +error_page 412 /my_error_handler_412; + +location /t { + rewrite_by_lua_block { + ngx.print() + } +} +location = /my_error_handler_412 { + return 412 "hello"; +} +--- request + GET /t +--- more_headers +If-Match: 1 +--- error_code: 412 +--- response_body eval +qr/\Ahello\z/ + + + +=== TEST 50: nginx crashes when encountering an illegal http if header +crash with ngx.print() +--- main_config +--- config +error_page 412 /my_error_handler_412; + +location /t { + access_by_lua_block { + local ngx_resp = require "ngx.resp" + ngx_resp.bypass_if_checks() + ngx.print("hello") + ngx.exit(200) + } +} +location = /my_error_handler_412 { + content_by_lua_block { + ngx.sleep(0.002) + ngx.header["Content-Type"] = "text/plain" + } +} +--- request + GET /t +--- more_headers +If-Match: 1 +--- error_code: 200 +--- response_body eval +qr/\Ahello\z/ + + + +=== TEST 51: subrequest cycle problem in rewrite_by_lua_file +--- http_config + lua_code_cache off; +--- config + set $main "foo"; + set $sub "bar"; + location = /main { + rewrite_by_lua_file html/main.lua; + echo $main; + } + + location = /sub { + rewrite_by_lua_file html/sub.lua; + echo $sub; + } +--- user_files +>>> main.lua +local res = ngx.location.capture("/sub") +ngx.var.main = "main " .. res.body +>>> sub.lua +ngx.var.sub = "sub" + +--- pipelined_requests eval +["GET /sub", "GET /main"] +--- response_body eval +["sub\n", "main sub\n\n"] +--- no_error_log +[error] + + + +=== TEST 52: subrequest cycle problem in content_by_lua_file +--- http_config + lua_code_cache off; +--- config + location = /main { + content_by_lua_file html/main.lua; + } + + location = /sub { + content_by_lua_file html/sub.lua; + } +--- user_files +>>> main.lua +local res = ngx.location.capture("/sub") +ngx.print("main " .. res.body) +>>> sub.lua +ngx.print("sub") + +--- pipelined_requests eval +["GET /sub", "GET /main"] +--- response_body eval +["sub", "main sub"] +--- no_error_log +[error] diff --git a/t/015-status.t b/t/015-status.t index 666dae727c..d3f6a3b4cf 100644 --- a/t/015-status.t +++ b/t/015-status.t @@ -9,7 +9,7 @@ log_level('warn'); #repeat_each(120); repeat_each(2); -plan tests => repeat_each() * (blocks() * 2 + 9); +plan tests => repeat_each() * (blocks() * 3 + 2); #no_diff(); #no_long_string(); @@ -29,6 +29,8 @@ __DATA__ GET /nil --- response_body nil +--- no_error_log +[error] @@ -43,6 +45,8 @@ nil GET /nil --- response_body not nil +--- no_error_log +[error] @@ -57,6 +61,8 @@ not nil GET /nil --- response_body 0 +--- no_error_log +[error] @@ -73,6 +79,8 @@ GET /nil --- response_body blah 200 +--- no_error_log +[error] @@ -89,6 +97,8 @@ GET /201 --- response_body created --- error_code: 201 +--- no_error_log +[error] @@ -105,6 +115,8 @@ GET /201 --- response_body created --- error_code: 201 +--- no_error_log +[error] @@ -121,6 +133,8 @@ GET /201 --- response_body created --- error_code: 201 +--- no_error_log +[error] @@ -136,6 +150,8 @@ created GET /201 --- response_body_like: 500 Internal Server Error --- error_code: 500 +--- no_error_log +[crit] @@ -233,6 +249,8 @@ GET /t --- error_code: 101 --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 @@ -246,6 +264,8 @@ GET /t --- http09 --- response_body status = 9 +--- no_error_log +[error] @@ -272,7 +292,7 @@ ngx.status = 502 -=== TEST 16: ngx.status assignmnt should clear r->err_status +=== TEST 16: ngx.status assignment should clear r->err_status --- config location = /t { return 502; @@ -291,3 +311,221 @@ ngx.status: 654 --- no_error_log [error] --- error_code: 654 + + + +=== TEST 17: set status and reason +--- config +location = /upstream { + content_by_lua_block { + local resp = require "ngx.resp" + resp.set_status(500, "user defined reason") + ngx.say("set_status_and_reason") + } +} + +location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = ngx.var.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 req = "GET /upstream HTTP/1.0\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 found = false + while true do + local line, err, part = sock:receive() + if line then + if ngx.re.find(line, "HTTP/1.1 500 user defined reason") then + ngx.say("match") + end + else + break + end + end + + sock:close() + } +} +--- request +GET /t +--- response_body +match +--- no_error_log +[error] + + + +=== TEST 18: set ngx.status in server_rewrite_by_lua_block +don't proxy_pass to upstream +--- config + server_rewrite_by_lua_block { + if ngx.var.uri == "/t" then + ngx.status = 403 + ngx.say("Hello World") + end + } + + location /t { + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u; + } + + location /u { + content_by_lua_block { + ngx.say("From upstream") + } + } +--- request +GET /t HTTP/1.0 +--- response_body +Hello World +--- error_code: 403 +--- no_error_log +[error] + + + +=== TEST 19: set ngx.status in rewrite_by_lua_block +don't proxy_pass to upstream +--- config + location /t { + rewrite_by_lua_block { + ngx.status = 403 + ngx.say("Hello World") + } + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u; + } + + location /u { + content_by_lua_block { + ngx.say("From upstream") + } + } +--- request +GET /t HTTP/1.0 +--- response_body +Hello World +--- error_code: 403 +--- no_error_log +[error] + + + +=== TEST 20: set ngx.status in access_by_lua_block +don't proxy_pass to upstream +--- config + location /t { + access_by_lua_block { + ngx.status = 403 + ngx.say("Hello World") + } + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u; + } + + location /u { + content_by_lua_block { + ngx.say("From upstream") + } + } +--- request +GET /t HTTP/1.0 +--- response_body +Hello World +--- error_code: 403 +--- no_error_log +[error] + + + +=== TEST 21: set ngx.status in server_rewrite_by_lua_block with sleep +don't proxy_pass to upstream +--- config + server_rewrite_by_lua_block { + if ngx.var.uri == "/t" then + ngx.sleep(0.001) + ngx.status = 403 + ngx.say("Hello World") + end + } + + location /t { + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u; + } + + location /u { + content_by_lua_block { + ngx.say("From upstream") + } + } +--- request +GET /t HTTP/1.0 +--- response_body +Hello World +--- error_code: 403 +--- no_error_log +[error] + + + +=== TEST 22: set ngx.status in rewrite_by_lua_block with sleep +don't proxy_pass to upstream +--- config + location /t { + rewrite_by_lua_block { + ngx.sleep(0.001) + ngx.status = 403 + ngx.say("Hello World") + } + + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u; + } + + location /u { + content_by_lua_block { + ngx.say("From upstream") + } + } +--- request +GET /t HTTP/1.0 +--- response_body +Hello World +--- error_code: 403 +--- no_error_log +[error] + + + +=== TEST 23: set ngx.status in access_by_lua_block +don't proxy_pass to upstream +--- config + location /t { + access_by_lua_block { + ngx.sleep(0.001) + ngx.status = 403 + ngx.say("Hello World") + } + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u; + } + + location /u { + content_by_lua_block { + ngx.say("From upstream") + } + } +--- request +GET /t HTTP/1.0 +--- response_body +Hello World +--- error_code: 403 +--- no_error_log +[error] diff --git a/t/016-resp-header.t b/t/016-resp-header.t index c7b5addee4..f5c58fa855 100644 --- a/t/016-resp-header.t +++ b/t/016-resp-header.t @@ -65,6 +65,7 @@ GET /read Content-Length: 3 --- response_body chop Hel +--- skip_eval: 3:defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2}) @@ -113,8 +114,16 @@ Hello } --- request GET /read ---- raw_response_headers_like chomp -X-Foo: a\r\n.*?X-Foo: bc\r\n +--- raw_response_headers_like eval +my $headers; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + $headers = qr/x-foo: a\r\n.*?x-foo: bc\r\n/ +} else { + $headers = qr/X-Foo: a\r\n.*?X-Foo: bc\r\n/ +} + +$headers; --- response_body Hello @@ -184,8 +193,16 @@ Hello } --- request GET /read ---- raw_response_headers_like chomp -X-Foo: a\r\n.*?X-Foo: abc\r\n +--- raw_response_headers_like eval +my $headers; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + $headers = "x-foo: a\r\n.*?x-foo: abc\r\n" +} else { + $headers = "X-Foo: a\r\n.*?X-Foo: abc\r\n" +} + +$headers; --- response_body Hello @@ -203,8 +220,17 @@ Hello --- request GET /lua --- raw_response_headers_like eval -".*Foo: a\r -Foo: b.*" +my $headers; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + $headers = ".*foo: a\r +foo: b.*"; +} else { + $headers = ".*Foo: a\r +Foo: b.*"; +} + +$headers; --- response_body @@ -222,8 +248,17 @@ Foo: b.*" --- request GET /lua --- raw_response_headers_like eval -".*Foo: a\r -Foo: b.*" +my $headers; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + $headers = ".*foo: a\r +foo: b.*"; +} else { + $headers = ".*Foo: a\r +Foo: b.*"; +} + +$headers; --- response_body @@ -263,6 +298,7 @@ Fooy: cony1, cony2 === TEST 15: set header after ngx.print +--- no_http2 --- config location /lua { default_type "text/plain"; @@ -278,7 +314,7 @@ hello --- error_log attempt to set ngx.header.HEADER after sending out response headers --- no_error_log eval -["alert", "warn"] +["[alert]", "[warn]"] @@ -931,7 +967,7 @@ Hello local results = {} results.something = "hello" results.content_type = "anything" - results.somehing_else = "hi" + results.something_else = "hi" local arr = {} for k in pairs(results) do table.insert(arr, k) end @@ -948,8 +984,8 @@ Content-Type: text/my-plain --- response_body content_type: anything -somehing_else: hi something: hello +something_else: hi --- no_error_log [error] @@ -1071,8 +1107,8 @@ GET /t --- more_headers Foo: bar Bah: baz ---- response_headers -Location: http://localhost:$ServerPort/t/ +--- response_headers_like +Location: https?://localhost:\d+/t/ --- response_body_like: 301 Moved Permanently --- error_code: 301 --- no_error_log @@ -1094,8 +1130,8 @@ GET /t Foo: bar Bah: baz --- response_body_like: 301 Moved Permanently ---- response_headers -Location: http://localhost:$ServerPort/t/ +--- response_headers_like +Location: https?://localhost:\d+/t/ --- error_code: 301 --- no_error_log [error] @@ -1115,8 +1151,8 @@ GET /t --- more_headers Foo: bar Bah: baz ---- response_headers -Location: http://localhost:$ServerPort/t/ +--- response_headers_like +Location: https?://localhost:\d+/t/ Foo: /t/ --- response_body_like: 301 Moved Permanently --- error_code: 301 @@ -1139,8 +1175,8 @@ GET /t --- more_headers Foo: bar Bah: baz ---- response_headers -Location: http://localhost:$ServerPort/t/ +--- response_headers_like +Location: https?://localhost:\d+/t/ Foo: /t/ --- response_body_like: 301 Moved Permanently --- error_code: 301 @@ -1159,8 +1195,16 @@ Foo: /t/ } --- request GET /lua ---- raw_response_headers_like chop -cache-Control: private +--- raw_response_headers_like eval +my $headers; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + $headers = "cache-control: private" +} else { + $headers = "cache-Control: private" +} + +$headers; --- response_body Cache-Control: private @@ -1509,6 +1553,7 @@ hi --- error_log my Transfer-Encoding: chunked my transfer-encoding: chunked +--- skip_eval: 6:defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2}) @@ -2138,6 +2183,91 @@ foo: foo%0Axx:bar\r\nfoo: bar%0Dxxx:foo\r\n hi --- no_error_log [alert] ---- error_log -my Content-Length: 8589934591 -upstream prematurely closed connection while sending to client +--- error_log eval +[ "my Content-Length: 8589934591", +qr/upstream prematurely closed connection while sending to client|upstream prematurely closed connection while reading upstream/] + + + +=== TEST 95: Expose the 'Last-Modified' response header as ngx.header["Last-Modified"] +--- config + location /a.txt { + header_filter_by_lua_block { + local last_modified = ngx.header["Last-Modified"] + if last_modified == nil then + ngx.log(ngx.ERR, "can not get lasted modified") + ngx.exit(500) + return + end + + local last_mod = ngx.parse_http_time(last_modified) + local age = ngx.time() - last_mod + ngx.header["Age"] = age + } + } +--- user_files +>>> a.txt +Foo +--- request +GET /a.txt +--- raw_response_headers_like eval +qr/^(a|A)ge: \d\r\n/ms +--- no_error_log +[error] + + + +=== TEST 96: 'Last-Modified' from upstream +--- config + location /test/ { + proxy_pass http://127.0.0.1:$server_port/; + + header_filter_by_lua_block { + local last_modified = ngx.header["Last-Modified"] + if last_modified == nil then + ngx.log(ngx.ERR, "can not get lasted modified") + ngx.exit(500) + return + end + + local last_mod = ngx.parse_http_time(last_modified) + local age = ngx.time() - last_mod + ngx.header["Age"] = age + } + } + +--- user_files +>>> a.txt +Foo +--- request +GET /test/a.txt +--- raw_response_headers_like eval +qr/^(a|A)ge: \d\r\n/ms +--- no_error_log +[error] + + + +=== TEST 97: 'Last-Modified' does not exist +--- config + location /test { + header_filter_by_lua_block { + local last_modified = ngx.header["Last-Modified"] + if last_modified == nil then + ngx.log(ngx.INFO, "Last-Modified is nil as expected") + return + end + + ngx.log(ngx.ERR, "Last-Modified expected to be nil, but got ", last_modified) + } + + content_by_lua_block { + ngx.say("Hello World") + } + } +--- request +GET /test +--- response_body +Hello World +--- no_error_log +[error] diff --git a/t/020-subrequest.t b/t/020-subrequest.t index 18befbd761..e91f3f6253 100644 --- a/t/020-subrequest.t +++ b/t/020-subrequest.t @@ -1,6 +1,7 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: use Test::Nginx::Socket::Lua; +use Test::Nginx::Util 'is_tcp_port_used'; #master_on(); #workers(1); @@ -16,6 +17,16 @@ plan tests => repeat_each() * (blocks() * 3 + 23); $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; $ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +# NB: tcp_listen_port needs to be greater than 10000, +# because the test cases expect it to be a 5-digit number +my $tcp_listen_port = 19113; +while (++$tcp_listen_port < 65535) { + if (!is_tcp_port_used $tcp_listen_port) { + last; + } +} +$ENV{TEST_NGINX_TCP_LISTEN_PORT} = $tcp_listen_port; + #no_diff(); no_long_string(); #no_shuffle(); @@ -1187,6 +1198,7 @@ body: === TEST 43: subrequests with an output body filter returning NGX_ERROR +--- no_http2 --- config location /sub { echo hello world; @@ -1227,6 +1239,8 @@ F(ngx_http_finalize_request) { --- error_code --- no_error_log [error] +--- curl_error eval +qr{(\Qcurl: (52) Empty reply from server\E|\Qcurl: (95) HTTP/3 stream 0 reset by server\E)}ms @@ -1238,7 +1252,7 @@ F(ngx_http_finalize_request) { set $memc_key 'foo'; #set $memc_exptime 300; - memc_pass 127.0.0.1:19112; #$TEST_NGINX_MEMCACHED_PORT; + memc_pass 127.0.0.1:$TEST_NGINX_RAND_PORT_1; #$TEST_NGINX_MEMCACHED_PORT; } location /main { @@ -1251,7 +1265,7 @@ F(ngx_http_finalize_request) { } --- request GET /main ---- tcp_listen: 19112 +--- tcp_listen: $TEST_NGINX_RAND_PORT_1 --- tcp_query_len: 9 --- tcp_reply eval "VALUE foo 0 1024\r\nhello world" @@ -1308,7 +1322,7 @@ upstream prematurely closed connection set $memc_key 'foo'; #set $memc_exptime 300; - memc_pass 127.0.0.1:19112; #$TEST_NGINX_MEMCACHED_PORT; + memc_pass 127.0.0.1:$TEST_NGINX_RAND_PORT_1; #$TEST_NGINX_MEMCACHED_PORT; } location /main { @@ -1321,7 +1335,7 @@ upstream prematurely closed connection } --- request GET /main ---- tcp_listen: 19112 +--- tcp_listen: $TEST_NGINX_RAND_PORT_1 --- tcp_no_close --- tcp_reply eval "VALUE foo 0 1024\r\nhello world" @@ -1381,7 +1395,7 @@ upstream timed out #proxy_read_timeout 100ms; proxy_buffering on; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1394,7 +1408,7 @@ upstream timed out } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_query_len: 65 --- tcp_reply eval "HTTP/1.0 200 OK\r\nContent-Length: 1024\r\n\r\nhello world" @@ -1441,7 +1455,7 @@ upstream prematurely closed connection proxy_read_timeout 100ms; proxy_buffering on; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1454,7 +1468,7 @@ upstream prematurely closed connection } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_no_close --- tcp_reply eval "HTTP/1.0 200 OK\r\nContent-Length: 1024\r\n\r\nhello world" @@ -1503,7 +1517,7 @@ upstream timed out #proxy_read_timeout 100ms; proxy_buffering on; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1516,7 +1530,7 @@ upstream timed out } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_query_len: 65 --- tcp_reply eval "HTTP/1.0 200 OK\r\n\r\nhello world" @@ -1563,7 +1577,7 @@ truncated: false proxy_read_timeout 100ms; proxy_buffering on; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1576,7 +1590,7 @@ truncated: false } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_no_close --- tcp_reply eval "HTTP/1.0 200 OK\r\n\r\nhello world" @@ -1626,7 +1640,7 @@ upstream timed out #proxy_read_timeout 100ms; proxy_buffering off; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1639,7 +1653,7 @@ upstream timed out } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_query_len: 65 --- tcp_reply eval "HTTP/1.0 200 OK\r\n\r\nhello world" @@ -1686,7 +1700,7 @@ truncated: false proxy_read_timeout 500ms; proxy_buffering off; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1699,7 +1713,7 @@ truncated: false } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_no_close --- tcp_reply eval "HTTP/1.0 200 OK\r\n\r\nhello world" @@ -1912,7 +1926,7 @@ a client request body is buffered to a temporary file #proxy_read_timeout 100ms; proxy_http_version 1.1; proxy_buffering on; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1925,7 +1939,7 @@ a client request body is buffered to a temporary file } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_query_len: 65 --- tcp_reply eval "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nhello world\r" @@ -1975,7 +1989,7 @@ upstream prematurely closed connection #proxy_read_timeout 100ms; proxy_http_version 1.1; proxy_buffering off; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -1988,7 +2002,7 @@ upstream prematurely closed connection } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_query_len: 65 --- tcp_reply eval "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nhello world\r" @@ -2036,7 +2050,7 @@ upstream prematurely closed connection proxy_read_timeout 100ms; proxy_buffering on; proxy_http_version 1.1; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -2049,7 +2063,7 @@ upstream prematurely closed connection } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_no_close --- tcp_reply eval "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nhello world\r" @@ -2098,7 +2112,7 @@ upstream timed out #proxy_read_timeout 100ms; proxy_buffering on; proxy_http_version 1.1; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -2111,7 +2125,7 @@ upstream timed out } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_no_close --- tcp_reply eval "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n" @@ -2156,7 +2170,7 @@ truncated: false #proxy_read_timeout 100ms; proxy_buffering off; proxy_http_version 1.1; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -2169,7 +2183,7 @@ truncated: false } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_no_close --- tcp_reply eval "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n" @@ -2215,7 +2229,7 @@ truncated: false #proxy_read_timeout 100ms; proxy_buffering off; - proxy_pass http://127.0.0.1:19113; + proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT; } location /main { @@ -2228,7 +2242,7 @@ truncated: false } --- request GET /main ---- tcp_listen: 19113 +--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT --- tcp_query_len: 65 --- tcp_reply eval "HTTP/1.0 200 OK\r\nContent-Length: 1024\r\n\r\nhello world" @@ -2338,6 +2352,7 @@ hello world nil --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -2622,6 +2637,7 @@ pr: Host: localhost --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -2655,6 +2671,7 @@ pr: Host: localhost --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -3000,6 +3017,8 @@ method: GET, uri: /foo, X: GET /bar HTTP/1.0 0 --- no_error_log [error] +--- skip_nginx +3: >= 1.21.1 @@ -3128,6 +3147,8 @@ method: POST, uri: /foo 0 --- no_error_log [error] +--- skip_nginx +3: >= 1.21.1 @@ -3259,6 +3280,8 @@ method: POST, uri: /foo 0 --- no_error_log [error] +--- skip_nginx +3: >= 1.21.1 @@ -3391,3 +3414,208 @@ method: POST, uri: /foo 0 --- no_error_log [error] +--- skip_nginx +3: >= 1.21.1 + + + +=== TEST 81: bad HTTP method +--- config + location /other { } + + location /lua { + content_by_lua_block { + local res = ngx.location.capture("/other", + { method = 10240 }); + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log +unsupported HTTP method: 10240 + + + +=== TEST 82: bad requests with both Content-Length and Transfer-Encoding (nginx >= 1.21.1) +--- http_config + upstream backend { + server unix:$TEST_NGINX_HTML_DIR/nginx.sock; + keepalive 32; + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + location / { + content_by_lua_block { + ngx.say("method: ", ngx.var.request_method, + ", uri: ", ngx.var.uri, + ", X: ", ngx.var.http_x) + } + } + } +--- config + location /proxy { + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://backend/foo; + } + + location /capture { + server_tokens off; + more_clear_headers Date; + + content_by_lua_block { + local res = ngx.location.capture("/proxy") + ngx.print(res.body) + } + } + + location /t { + content_by_lua_block { + local req = [[ +GET /capture HTTP/1.1 +Host: test.com +Content-Length: 37 +Transfer-Encoding: chunked + +0 + +GET /capture HTTP/1.1 +Host: test.com +X: GET /bar HTTP/1.0 + +]] + + local sock = ngx.socket.tcp() + sock:settimeout(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 + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send req: ", err) + return + end + + ngx.say("req bytes: ", bytes) + + local n_resp = 0 + + local reader = sock:receiveuntil("\r\n") + while true do + local line, err = reader() + if line then + ngx.say(line) + if line == "0" then + n_resp = n_resp + 1 + end + + if n_resp >= 2 then + break + end + + else + ngx.say("err: ", err) + break + end + end + + sock:close() + } + } +--- request +GET /t +--- response_body_like +req bytes: 146 +HTTP/1.1 400 Bad Request +--- no_error_log +[error] +--- skip_nginx +3: < 1.21.1 + + + +=== TEST 83: avoid request smuggling of HEAD req +--- config + location /capture { + server_tokens off; + more_clear_headers Date; + + content_by_lua_block { + ngx.say("Hello") + } + } + + location /t { + content_by_lua_block { + local req = [[ +HEAD /capture HTTP/1.1 +Host: test.com +Content-Length: 63 + +GET /capture HTTP/1.1 +Host: test.com +X: GET /bar HTTP/1.0 + +]] + + local sock = ngx.socket.tcp() + sock:settimeout(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 + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send req: ", err) + return + end + + ngx.say("req bytes: ", bytes) + + local n_resp = 0 + + local reader = sock:receiveuntil("\r\n") + while true do + local line, err = reader() + if line then + ngx.say(line) + if line == "0" then + n_resp = n_resp + 1 + end + + if n_resp >= 2 then + break + end + + else + ngx.say("err: ", err) + break + end + end + + sock:close() + } + } +--- request +GET /t +--- response_body +req bytes: 117 +HTTP/1.1 200 OK +Server: nginx +Content-Type: text/plain +Connection: keep-alive + +err: timeout +--- error_log +lua tcp socket read timed out diff --git a/t/022-redirect.t b/t/022-redirect.t index ef80174ca5..197c76e1d7 100644 --- a/t/022-redirect.t +++ b/t/022-redirect.t @@ -122,7 +122,16 @@ GET /read } --- request GET /read ---- raw_response_headers_like: Location: /echo\r\n +--- raw_response_headers_like eval +my $headers; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $headers = "location: /echo\r\n" +} else { + $headers = "Location: /echo\r\n" +} + +$headers; --- response_body_like: 302 Found --- error_code: 302 diff --git a/t/023-rewrite/client-abort.t b/t/023-rewrite/client-abort.t index 0d6f2b39b3..61ada3a1b0 100644 --- a/t/023-rewrite/client-abort.t +++ b/t/023-rewrite/client-abort.t @@ -19,7 +19,13 @@ our $StapScript = $t::StapThread::StapScript; repeat_each(2); -plan tests => repeat_each() * (blocks() * 3 - 1); +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + plan(skip_all => "HTTP3 does not support client abort"); +} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) { + plan(skip_all => "HTTP2 does not support client abort"); +} else { + plan tests => repeat_each() * (blocks() * 3 - 1); +} $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; $ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; diff --git a/t/023-rewrite/exit.t b/t/023-rewrite/exit.t index 39ea5cbd62..7f01717eb6 100644 --- a/t/023-rewrite/exit.t +++ b/t/023-rewrite/exit.t @@ -120,6 +120,7 @@ GET /api?user=agentz === TEST 6: working with ngx_auth_request (simplest form, w/o ngx_memc) +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -187,10 +188,12 @@ ngx.var.uid = res[1].uid; GET /api?uid=32 --- response_body Logged in 56 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} === TEST 7: working with ngx_auth_request (simplest form) +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -258,10 +261,12 @@ ngx.var.uid = res[1].uid; GET /api?uid=32 --- response_body Logged in 56 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} === TEST 8: working with ngx_auth_request +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -342,6 +347,7 @@ GET /api?uid=32 Logged in 56 --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/023-rewrite/on-abort.t b/t/023-rewrite/on-abort.t index 336b7ce4b5..0535732a0b 100644 --- a/t/023-rewrite/on-abort.t +++ b/t/023-rewrite/on-abort.t @@ -19,7 +19,13 @@ our $StapScript = $t::StapThread::StapScript; repeat_each(2); -plan tests => repeat_each() * (blocks() * 4 + 15); +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + plan(skip_all => "HTTP3 does not support on_abort"); +} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) { + plan(skip_all => "HTTP2 does not support on_abort"); +} else { + plan tests => repeat_each() * (blocks() * 4 + 15); +} $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; $ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; @@ -419,7 +425,7 @@ main handler done -=== TEST 9: regsiter on_abort callback but no client abortion +=== TEST 9: register on_abort callback but no client abortion --- config location /t { lua_check_client_abort on; @@ -560,7 +566,7 @@ on abort called -=== TEST 12: regsiter on_abort callback but no client abortion (uthread) +=== TEST 12: register on_abort callback but no client abortion (uthread) --- config location /t { lua_check_client_abort on; @@ -607,7 +613,7 @@ main handler done -=== TEST 13: regsiter on_abort callback multiple times +=== TEST 13: register on_abort callback multiple times --- config location /t { lua_check_client_abort on; diff --git a/t/023-rewrite/redirect.t b/t/023-rewrite/redirect.t index 8fa9aa2e79..2b3dccb0c0 100644 --- a/t/023-rewrite/redirect.t +++ b/t/023-rewrite/redirect.t @@ -119,7 +119,16 @@ GET /read } --- request GET /read ---- raw_response_headers_like: Location: /foo\r\n +--- raw_response_headers_like eval +my $headers; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + $headers = "location: /foo\r\n" +} else { + $headers = "Location: /foo\r\n" +} + +$headers; --- response_body_like: 302 Found --- error_code: 302 diff --git a/t/023-rewrite/req-body.t b/t/023-rewrite/req-body.t index 13bdcb2511..0c1857384e 100644 --- a/t/023-rewrite/req-body.t +++ b/t/023-rewrite/req-body.t @@ -125,6 +125,7 @@ Expect: 100-Continue [alert] [error] http finalize request: 500, "/test?" a:1, c:0 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/023-rewrite/req-socket.t b/t/023-rewrite/req-socket.t index 9292385265..280001578d 100644 --- a/t/023-rewrite/req-socket.t +++ b/t/023-rewrite/req-socket.t @@ -1,6 +1,14 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support ngx.req.socket"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); repeat_each(2); @@ -532,3 +540,4 @@ Expect: 100-Continue \breceived: hello\b.*?\breceived: worl\b --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) diff --git a/t/023-rewrite/sleep.t b/t/023-rewrite/sleep.t index 8d4c2da2ea..0135d5e440 100644 --- a/t/023-rewrite/sleep.t +++ b/t/023-rewrite/sleep.t @@ -110,6 +110,7 @@ bad argument #1 to 'sleep' === TEST 5: sleep 0.5 - multi-times +--- quic_max_idle_timeout: 1.0 --- config location /test { rewrite_by_lua ' @@ -135,6 +136,7 @@ lua sleep timer expired: "/test?" === TEST 6: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep +--- quic_max_idle_timeout: 2.05 --- config location /test { rewrite_by_lua ' @@ -163,6 +165,7 @@ lua sleep timer expired: "/test?" === TEST 7: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep +--- quic_max_idle_timeout: 0.85 --- config location /test { rewrite_by_lua ' diff --git a/t/023-rewrite/socket-keepalive.t b/t/023-rewrite/socket-keepalive.t index 8b532ead6b..9ce8d5e290 100644 --- a/t/023-rewrite/socket-keepalive.t +++ b/t/023-rewrite/socket-keepalive.t @@ -176,6 +176,7 @@ received: OK === TEST 3: upstream sockets close prematurely +--- no_http3 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -253,6 +254,7 @@ done === TEST 4: http keepalive +--- no_http3 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -330,6 +332,7 @@ done === TEST 5: lua_socket_keepalive_timeout +--- quic_max_idle_timeout: 1.1 --- config server_tokens off; location /t { @@ -409,6 +412,7 @@ qr/lua tcp socket connection pool size: 30\b/] === TEST 6: lua_socket_pool_size +--- quic_max_idle_timeout: 1.1 --- config server_tokens off; location /t { @@ -489,6 +493,7 @@ qr/lua tcp socket connection pool size: 1\b/] === TEST 7: "lua_socket_keepalive_timeout 0" means unlimited +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -571,6 +576,7 @@ lua tcp socket keepalive timeout: unlimited === TEST 8: setkeepalive(timeout) overrides lua_socket_keepalive_timeout +--- quic_max_idle_timeout: 1.1 --- config server_tokens off; location /t { @@ -650,6 +656,7 @@ qr/lua tcp socket connection pool size: 30\b/] === TEST 9: sock:setkeepalive(timeout, size) overrides lua_socket_pool_size +--- quic_max_idle_timeout: 1.1 --- config server_tokens off; location /t { @@ -730,6 +737,7 @@ qr/lua tcp socket connection pool size: 25\b/] === TEST 10: sock:keepalive_timeout(0) means unlimited +--- quic_max_idle_timeout: 1.1 --- config server_tokens off; location /t { @@ -903,7 +911,7 @@ lua tcp socket get keepalive peer: using connection -=== TEST 12: github issue #108: ngx.locaiton.capture + redis.set_keepalive +=== TEST 12: github issue #108: ngx.location.capture + redis.set_keepalive --- http_config eval qq{ lua_package_path "$::HtmlDir/?.lua;;"; diff --git a/t/023-rewrite/tcp-socket-timeout.t b/t/023-rewrite/tcp-socket-timeout.t index e713bb5755..9be8081321 100644 --- a/t/023-rewrite/tcp-socket-timeout.t +++ b/t/023-rewrite/tcp-socket-timeout.t @@ -16,6 +16,7 @@ BEGIN { $ENV{MOCKEAGAIN} = 'w'; } + delete($ENV{TEST_NGINX_USE_HTTP2}); $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'get helloworld'; } diff --git a/t/023-rewrite/tcp-socket.t b/t/023-rewrite/tcp-socket.t index 5258487eba..966365f906 100644 --- a/t/023-rewrite/tcp-socket.t +++ b/t/023-rewrite/tcp-socket.t @@ -86,6 +86,7 @@ failed to receive a line: closed [] close: 1 nil --- no_error_log [error] +--- no_http2 @@ -155,6 +156,7 @@ failed to receive a line: closed [foo] closed --- no_error_log [error] +--- no_http2 @@ -197,6 +199,7 @@ connected: nil failed to send request: closed --- error_log attempt to send data on a closed socket: +--- no_http2 @@ -521,6 +524,7 @@ failed to receive a line: closed close: 1 nil --- no_error_log [error] +--- no_http2 @@ -591,6 +595,7 @@ close: 1 nil " --- no_error_log [error] +--- no_http2 @@ -672,6 +677,7 @@ close: 1 nil " --- no_error_log [error] +--- no_http2 @@ -749,6 +755,7 @@ close: 1 nil " --- no_error_log [error] +--- no_http2 @@ -827,6 +834,7 @@ close: 1 nil " --- no_error_log [error] +--- no_http2 @@ -898,6 +906,7 @@ failed to receive a line: closed [] close: 1 nil --- no_error_log [error] +--- no_http2 @@ -967,6 +976,7 @@ failed to receive a line: closed [] close: 1 nil --- no_error_log [error] +--- no_http2 @@ -1077,6 +1087,7 @@ close: 1 nil " --- no_error_log [error] +--- no_http2 @@ -1521,6 +1532,7 @@ GET /t 2: close: 1 nil --- no_error_log [error] +--- no_http2 @@ -1593,6 +1605,7 @@ failed to receive a line: closed [] close: 1 nil --- no_error_log [error] +--- no_http2 @@ -1654,6 +1667,9 @@ GET /t --- ignore_response --- error_log bad argument #1 to 'send' (bad data type nil found) +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# +--- no_http2 @@ -1715,6 +1731,9 @@ GET /t --- ignore_response --- error_log bad argument #1 to 'send' (bad data type boolean found) +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# +--- no_http2 @@ -1776,6 +1795,9 @@ GET /t --- ignore_response --- error_log bad argument #1 to 'send' (bad data type userdata found) +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# +--- no_http2 @@ -1845,6 +1867,7 @@ subrequest: 200, OK\r " --- no_error_log [error] +--- no_http2 @@ -1916,6 +1939,7 @@ close: 1 nil --- no_error_log [error] --- SKIP +--- no_http2 @@ -1976,6 +2000,7 @@ receive(0): [] close: 1 nil --- no_error_log [error] +--- no_http2 @@ -2036,6 +2061,7 @@ send(""): 0 close: 1 nil --- no_error_log [error] +--- no_http2 @@ -2092,6 +2118,7 @@ qr/runtime error: rewrite_by_lua\(nginx\.conf:\d+\):7: bad request/ --- no_error_log [alert] +--- no_http2 @@ -2151,6 +2178,7 @@ qr/runtime error: rewrite_by_lua\(nginx\.conf:\d+\):14: bad request/ --- no_error_log [alert] +--- no_http2 diff --git a/t/023-rewrite/uthread-exit.t b/t/023-rewrite/uthread-exit.t index e25f7f8b0c..87f850ce8b 100644 --- a/t/023-rewrite/uthread-exit.t +++ b/t/023-rewrite/uthread-exit.t @@ -987,6 +987,7 @@ hello in thread after --- no_error_log [error] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} @@ -1072,6 +1073,7 @@ hello in thread after --- no_error_log [error] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/023-rewrite/uthread-spawn.t b/t/023-rewrite/uthread-spawn.t index dccef87dac..62d837cff7 100644 --- a/t/023-rewrite/uthread-spawn.t +++ b/t/023-rewrite/uthread-spawn.t @@ -1120,6 +1120,7 @@ body: hello world)$ --- no_error_log [error] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/024-access/client-abort.t b/t/024-access/client-abort.t index 83bf0a4df1..e4abb4ebd9 100644 --- a/t/024-access/client-abort.t +++ b/t/024-access/client-abort.t @@ -1,6 +1,16 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support ngx.req.socket and lua_check_client_abort"; + } elsif ($ENV{TEST_NGINX_USE_HTTP2}) { + $SkipReason = "http2 does not support ngx.req.socket and lua_check_client_abort"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); use t::StapThread; our $GCScript = <<_EOC_; diff --git a/t/024-access/exit.t b/t/024-access/exit.t index d6d66b8cfe..661ee2d8bc 100644 --- a/t/024-access/exit.t +++ b/t/024-access/exit.t @@ -114,6 +114,7 @@ GET /api?user=agentz === TEST 6: working with ngx_auth_request (simplest form, w/o ngx_memc) +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -177,10 +178,12 @@ GET /api?uid=32 --- response_body Logged in 56 --- timeout: 3 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} === TEST 7: working with ngx_auth_request (simplest form) +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -243,10 +246,12 @@ ngx.var.uid = res[1].uid; GET /api?uid=32 --- response_body Logged in 56 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} === TEST 8: working with ngx_auth_request +--- no_http2 --- http_config eval " lua_package_cpath '$::LuaCpath'; @@ -320,6 +325,7 @@ ngx.var.uid = res[1].uid; GET /api?uid=32 --- response_body Logged in 56 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/024-access/on-abort.t b/t/024-access/on-abort.t index 5bb948bb68..70637ba862 100644 --- a/t/024-access/on-abort.t +++ b/t/024-access/on-abort.t @@ -19,7 +19,13 @@ our $StapScript = $t::StapThread::StapScript; repeat_each(2); -plan tests => repeat_each() * (blocks() * 4 + 15); +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + plan(skip_all => "HTTP3 does not support on_abort"); +} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) { + plan(skip_all => "HTTP2 does not support on_abort"); +} else { + plan tests => repeat_each() * (blocks() * 4 + 15); +} $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; $ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; @@ -417,7 +423,7 @@ main handler done -=== TEST 9: regsiter on_abort callback but no client abortion +=== TEST 9: register on_abort callback but no client abortion --- config location /t { lua_check_client_abort on; @@ -558,7 +564,7 @@ on abort called -=== TEST 12: regsiter on_abort callback but no client abortion (uthread) +=== TEST 12: register on_abort callback but no client abortion (uthread) --- config location /t { lua_check_client_abort on; @@ -603,7 +609,7 @@ main handler done -=== TEST 13: regsiter on_abort callback multiple times +=== TEST 13: register on_abort callback multiple times --- config location /t { lua_check_client_abort on; diff --git a/t/024-access/redirect.t b/t/024-access/redirect.t index b45fac718b..e8609af060 100644 --- a/t/024-access/redirect.t +++ b/t/024-access/redirect.t @@ -119,6 +119,15 @@ GET /read } --- request GET /read ---- raw_response_headers_like: Location: /foo\r\n +--- raw_response_headers_like eval +my $headers; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + $headers = "location: /foo\r\n" +} else { + $headers = "Location: /foo\r\n" +} + +$headers; --- response_body_like: 302 Found --- error_code: 302 diff --git a/t/024-access/req-body.t b/t/024-access/req-body.t index 70db85c1c2..48caeb9001 100644 --- a/t/024-access/req-body.t +++ b/t/024-access/req-body.t @@ -122,6 +122,7 @@ Expect: 100-Continue [alert] [error] http finalize request: 500, "/test?" a:1, c:0 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/024-access/sleep.t b/t/024-access/sleep.t index fc1fc024b5..c7aa0b65a4 100644 --- a/t/024-access/sleep.t +++ b/t/024-access/sleep.t @@ -110,6 +110,7 @@ bad argument #1 to 'sleep' === TEST 5: sleep 0.5 - multi-times +--- quic_max_idle_timeout: 1.0 --- config location /test { access_by_lua ' @@ -135,6 +136,7 @@ lua sleep timer expired: "/test?" === TEST 6: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep +--- quic_max_idle_timeout: 2.2 --- config location /test { access_by_lua ' @@ -163,6 +165,7 @@ lua sleep timer expired: "/test?" === TEST 7: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep +--- quic_max_idle_timeout: 0.85 --- config location /test { access_by_lua ' diff --git a/t/024-access/uthread-exit.t b/t/024-access/uthread-exit.t index 27572370d5..bd165ae708 100644 --- a/t/024-access/uthread-exit.t +++ b/t/024-access/uthread-exit.t @@ -880,6 +880,7 @@ after === TEST 11: exit in user thread (entry thread is still pending on reqsock:receive) +--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3} --- config location /lua { access_by_lua ' @@ -973,6 +974,7 @@ after === TEST 12: exit in user thread (entry thread is still pending on ngx.req.read_body) +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} --- config location /lua { client_body_timeout 12000ms; diff --git a/t/024-access/uthread-spawn.t b/t/024-access/uthread-spawn.t index bc92ccd32c..b31edbcb1e 100644 --- a/t/024-access/uthread-spawn.t +++ b/t/024-access/uthread-spawn.t @@ -1120,3 +1120,4 @@ body: hello world)$ --- no_error_log [error] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/025-codecache.t b/t/025-codecache.t index 3e01bb88cf..6e3e335097 100644 --- a/t/025-codecache.t +++ b/t/025-codecache.t @@ -855,6 +855,7 @@ qr/\[alert\] \S+ lua_code_cache is off; this will hurt performance/, "decrementing the reference count for Lua VM: 2", "decrementing the reference count for Lua VM: 1", ] +--- skip_eval: 11:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -966,6 +967,7 @@ qr/\[alert\] \S+ lua_code_cache is off; this will hurt performance/, === TEST 27: GC issue with the on_abort thread object +curl: (52) Empty reply from server --- config lua_code_cache off; location = /t { @@ -991,6 +993,8 @@ decrementing the reference count for Lua VM: 3 qr/\[alert\] \S+ lua_code_cache is off; this will hurt performance/, "lua close the global Lua VM", ] +--- curl_error eval +qr/curl: \(\d+\) Empty reply from server|curl: \(28\) Operation timed out after \d+ milliseconds with 0 bytes received/ @@ -1096,7 +1100,7 @@ function go(port) ngx.say("failed to receive a line: ", err, " [", part, "]") end - local ok, err = sock:setkeepalive(1) + local ok, err = sock:setkeepalive(10) if not ok then ngx.say("failed to set reusable: ", err) end @@ -1117,6 +1121,7 @@ qq{lua tcp socket keepalive create connection pool for key "127.0.0.1:$ENV{TEST_ qr/\[alert\] \S+ lua_code_cache is off; this will hurt performance/, qr/\blua tcp socket keepalive: free connection pool [0-9A-F]+ for "127.0.0.1:/, ] +--- skip_eval: 7:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -1720,6 +1725,7 @@ grep me: b } --- config lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location = /proxy { proxy_pass http://backend; @@ -1862,18 +1868,19 @@ code cache hit (key='log_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=12) "] --- error_log eval [ -qr/balancer_by_lua:\d+: hello/, -qr/ssl_session_fetch_by_lua_block:\d+: hello/, -qr/ssl_certificate_by_lua:\d+: hello/, -qr/ssl_session_store_by_lua_block:\d+: hello/, -qr/set_by_lua:\d+: hello/, +qr/balancer_by_lua\(nginx\.conf:\d+\):\d+: hello/, +qr/ssl_session_fetch_by_lua\(nginx\.conf:\d+\):\d+: hello/, +qr/ssl_certificate_by_lua\(nginx\.conf:\d+\):\d+: hello/, +qr/ssl_session_store_by_lua\(nginx\.conf:\d+\):\d+: hello/, +qr/set_by_lua\(nginx\.conf:\d+\):\d+: hello/, qr/rewrite_by_lua\(nginx\.conf:\d+\):\d+: hello/, qr/access_by_lua\(nginx\.conf:\d+\):\d+: hello/, qr/content_by_lua\(nginx\.conf:\d+\):\d+: hello/, -qr/header_filter_by_lua:\d+: hello/, -qr/body_filter_by_lua:\d+: hello/, -qr/log_by_lua\(nginx\.conf:\d+\):\d+: hello/, +qr/header_filter_by_lua\(nginx\.conf:\d+\):\d+: hello/, +qr/body_filter_by_lua\(nginx\.conf:\d+\):\d+: hello/, +qr/log_by_lua\(nginx.conf:\d+\):\d+: hello/, ] --- log_level: debug --- no_error_log [error] +--- skip_eval: 14:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/026-mysql.t b/t/026-mysql.t index 569f96fbc9..e7ab170600 100644 --- a/t/026-mysql.t +++ b/t/026-mysql.t @@ -16,6 +16,7 @@ run_tests(); __DATA__ === TEST 1: when mysql query timed out, kill that query by Lua +--- no_http2 --- http_config upstream backend { drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql @@ -69,6 +70,7 @@ kill status = 200 kill body = \{"errcode":0\}$ --- error_log eval qr{upstream timed out \(\d+: Connection timed out\) while sending query to drizzle upstream} +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/027-multi-capture.t b/t/027-multi-capture.t index 9299561687..b3cdbaff09 100644 --- a/t/027-multi-capture.t +++ b/t/027-multi-capture.t @@ -752,3 +752,75 @@ proxy_cache_path conf/cache levels=1:2 keys_zone=STATIC:10m inactive=10m max_siz GET /foo --- response_body ok + + + +=== TEST 14: capture multi with headers +--- config + location /foo { + content_by_lua_block { + local res1, res2, res3 = ngx.location.capture_multi{ + {"/test", { headers = { ["X-Test-Header"] = "aa"} } }, + {"/test", { headers = { ["X-Test-Header"] = "bb"} } }, + {"/test"}, + } + ngx.say("res1.status = " .. res1.status) + ngx.say("res1.body = " .. res1.body) + ngx.say("res2.status = " .. res2.status) + ngx.say("res2.body = " .. res2.body) + ngx.say("res3.status = " .. res3.status) + ngx.say("res3.body = " .. res3.body) + } + } + + location = /test { + content_by_lua_block { + ngx.print(ngx.var.http_x_test_header) + } + } +--- request + GET /foo +--- response_body +res1.status = 200 +res1.body = aa +res2.status = 200 +res2.body = bb +res3.status = 200 +res3.body = nil + + + +=== TEST 15: capture multi with headers override +--- config + location /foo { + content_by_lua_block { + local res1, res2, res3 = ngx.location.capture_multi{ + {"/test", { headers = { ["X-Test-Header"] = "aa"} } }, + {"/test", { headers = { ["X-Test-Header"] = "bb"} } }, + {"/test"}, + } + ngx.say("res1.status = " .. res1.status) + ngx.say("res1.body = " .. res1.body) + ngx.say("res2.status = " .. res2.status) + ngx.say("res2.body = " .. res2.body) + ngx.say("res3.status = " .. res3.status) + ngx.say("res3.body = " .. res3.body) + } + } + + location = /test { + content_by_lua_block { + ngx.print(ngx.var.http_x_test_header) + } + } +--- request + GET /foo +--- more_headers +X-Test-Header: cc +--- response_body +res1.status = 200 +res1.body = aa +res2.status = 200 +res2.body = bb +res3.status = 200 +res3.body = cc diff --git a/t/028-req-header.t b/t/028-req-header.t index c2c58eb52e..21da3fc1ad 100644 --- a/t/028-req-header.t +++ b/t/028-req-header.t @@ -59,8 +59,13 @@ lua exceeding request header limit for k, v in pairs(headers) do h[k] = v end - ngx.say("Foo: ", h["Foo"] or "nil") - ngx.say("Bar: ", h["Bar"] or "nil") + if (ngx.req.http_version() == 3 or ngx.req.http_version() == 2) then + ngx.say("Foo: ", h["foo"] or "nil") + ngx.say("Bar: ", h["bar"] or "nil") + else + ngx.say("Foo: ", h["Foo"] or "nil") + ngx.say("Bar: ", h["Bar"] or "nil") + end '; } --- request @@ -127,6 +132,8 @@ Foo: --- response_body eval "a" x 2048 --- timeout: 15 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 @@ -145,6 +152,8 @@ Foo: --- response_body eval "a" x 2048 --- timeout: 15 +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 @@ -273,8 +282,13 @@ Content-Type: } --- request GET /bar ---- response_body -Foo: a +--- response_body eval +# Since nginx version 1.23.0, nginx combines same $http_* variable together +$Test::Nginx::Util::NginxVersion >= 1.023000 ? + +"Foo: a, b\n" +: +"Foo: a\n" @@ -480,6 +494,8 @@ for my $k (@k) { --- timeout: 4 --- error_log lua exceeding request header limit 101 > 100 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 @@ -518,20 +534,30 @@ while ($i <= 98) { $s --- response_body eval my @k; + +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + push @k, "host: localhost\n"; +} my $i = 1; while ($i <= 98) { push @k, "x-$i"; $i++; } -push @k, "connection: close\n"; -push @k, "host: localhost\n"; + +my $found_headers = "found 99 headers\n"; +if (!defined($ENV{TEST_NGINX_USE_HTTP3}) && !defined($ENV{TEST_NGINX_USE_HTTP2})) { + push @k, "connection: close\n"; + push @k, "host: localhost\n"; + $found_headers = "found 100 headers\n"; +} @k = sort @k; for my $k (@k) { if ($k =~ /\d+/) { $k .= ": $&\n"; } } -CORE::join("", @k) . "found 100 headers\n"; + +CORE::join("", @k) . $found_headers; --- timeout: 4 --- no_error_log [error] @@ -539,7 +565,7 @@ lua exceeding request header limit -=== TEST 21: execeeding custom max 102 header limit +=== TEST 21: exceeding custom max 102 header limit --- config location /lua { content_by_lua ' @@ -571,6 +597,9 @@ while ($i <= 101) { $s --- response_body eval my @k; +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + push @k, "host: localhost\n"; +} my $i = 1; while ($i <= 100) { push @k, "x-$i"; @@ -588,10 +617,12 @@ for my $k (@k) { --- timeout: 4 --- error_log lua exceeding request header limit 103 > 102 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 -=== TEST 22: NOT execeeding custom max 102 header limit +=== TEST 22: NOT exceeding custom max 102 header limit --- config location /lua { content_by_lua ' @@ -623,13 +654,20 @@ while ($i <= 100) { $s --- response_body eval my @k; +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + push @k, "host: localhost\n"; +} my $i = 1; while ($i <= 100) { push @k, "x-$i"; $i++; } -push @k, "connection: close\n"; -push @k, "host: localhost\n"; + +if (!defined($ENV{TEST_NGINX_USE_HTTP3}) && !defined($ENV{TEST_NGINX_USE_HTTP2})) { + push @k, "connection: close\n"; + push @k, "host: localhost\n"; +} + @k = sort @k; for my $k (@k) { if ($k =~ /\d+/) { @@ -676,13 +714,18 @@ while ($i <= 105) { $s --- response_body eval my @k; +if (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) { + push @k, "host: localhost\n"; +} my $i = 1; while ($i <= 105) { push @k, "x-$i"; $i++; } -push @k, "connection: close\n"; -push @k, "host: localhost\n"; +if (!defined($ENV{TEST_NGINX_USE_HTTP3}) && !defined($ENV{TEST_NGINX_USE_HTTP2})) { + push @k, "connection: close\n"; + push @k, "host: localhost\n"; +} @k = sort @k; for my $k (@k) { if ($k =~ /\d+/) { @@ -825,10 +868,21 @@ hello world Content-Type: application/ocsp-request Test-Header: 1 --- response_body_like eval -qr/Connection: close\r +my $body; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $body = qr/Connection: close\r +test-header: 1\r +\r +$/; +} else { + $body = qr/Connection: close\r Test-Header: 1\r \r -$/ +$/; +} + +$body; --- no_error_log [error] @@ -876,7 +930,36 @@ Foo20: foo20 Foo21: foo21 Foo22: foo22 --- response_body_like eval -qr/Bah: bah\r +my $headers; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $headers = qr/bah: bah\r +test-header: 1\r +foo1: foo1\r +foo2: foo2\r +foo3: foo3\r +foo4: foo4\r +foo5: foo5\r +foo6: foo6\r +foo7: foo7\r +foo8: foo8\r +foo9: foo9\r +foo10: foo10\r +foo11: foo11\r +foo12: foo12\r +foo13: foo13\r +foo14: foo14\r +foo15: foo15\r +foo16: foo16\r +foo17: foo17\r +foo18: foo18\r +foo19: foo19\r +foo20: foo20\r +foo21: foo21\r +foo22: foo22\r +/; +} else { + $headers = qr/Bah: bah\r Test-Header: 1\r Foo1: foo1\r Foo2: foo2\r @@ -900,7 +983,10 @@ Foo19: foo19\r Foo20: foo20\r Foo21: foo21\r Foo22: foo22\r -/ +/; +} + +$headers; @@ -931,11 +1017,21 @@ GET /t --- more_headers My-Foo: bar Bar: baz ---- response_body -Bar: baz +--- response_body eval +my $body; +if ($ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_HTTP2}) { + $body = "bar: baz +host: localhost +my-foo: bar +"; +} else { + $body = "Bar: baz Connection: close Host: localhost My-Foo: bar +"; +} +$body; @@ -1005,29 +1101,23 @@ for my $i ('a' .. 'r') { } $s --- response_body eval -"GET /back HTTP/1.0\r +my $s = "GET /back HTTP/1.0\r Host: foo\r -Connection: close\r -User-Agent: curl\r -A: a\r -B: b\r -C: c\r -D: d\r -E: e\r -F: f\r -G: g\r -H: h\r -I: i\r -J: j\r -K: k\r -L: l\r -M: m\r -N: n\r -O: o\r -P: p\r -Q: q\r -\r -" +Connection: close\r\n"; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $s .= "user-agent: curl\r\n"; + for my $i ('a' .. 'q') { + $s .= $i . ": " . "$i\r\n" + } +} else { + $s .= "User-Agent: curl\r\n"; + for my $i ('a' .. 'q') { + $s .= uc($i) . ": " . "$i\r\n" + } +} + +$s . "\r\n"; @@ -1058,7 +1148,55 @@ for my $i ('a' .. 'r') { } $s --- response_body eval -"GET /back HTTP/1.0\r +my $body; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $body = "GET /back HTTP/1.0\r +Host: foo\r +Connection: close\r +user-agent: curl\r +a: a\r +b: b\r +c: c\r +d: d\r +e: e\r +f: f\r +g: g\r +h: h\r +i: i\r +j: j\r +k: k\r +l: l\r +m: m\r +n: n\r +o: o\r +p: p\r +q: q\r +foo-1: 1\r +foo-2: 2\r +foo-3: 3\r +foo-4: 4\r +foo-5: 5\r +foo-6: 6\r +foo-7: 7\r +foo-8: 8\r +foo-9: 9\r +foo-10: 10\r +foo-11: 11\r +foo-12: 12\r +foo-13: 13\r +foo-14: 14\r +foo-15: 15\r +foo-16: 16\r +foo-17: 17\r +foo-18: 18\r +foo-19: 19\r +foo-20: 20\r +foo-21: 21\r +\r +"; +} else { + $body = "GET /back HTTP/1.0\r Host: foo\r Connection: close\r User-Agent: curl\r @@ -1101,7 +1239,10 @@ foo-19: 19\r foo-20: 20\r foo-21: 21\r \r -" +"; +} + +$body; @@ -1130,7 +1271,34 @@ for my $i ('a' .. 'r') { } $s --- response_body eval -"GET /back HTTP/1.0\r +my $body; + +if ($ENV{TEST_NGINX_USE_HTTP3}) { + $body = "GET /back HTTP/1.0\r +Host: foo\r +Connection: close\r +user-agent: curl\r +bah: bah\r +a: a\r +b: b\r +c: c\r +d: d\r +e: e\r +f: f\r +g: g\r +h: h\r +i: i\r +j: j\r +k: k\r +l: l\r +m: m\r +n: n\r +o: o\r +p: p\r +\r +" +} else { +$body = "GET /back HTTP/1.0\r Host: foo\r Connection: close\r User-Agent: curl\r @@ -1153,6 +1321,9 @@ O: o\r P: p\r \r " +} + +$body; @@ -1184,7 +1355,55 @@ for my $i ('a' .. 'r') { } $s --- response_body eval -"GET /back HTTP/1.0\r +my $body; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $body = "GET /back HTTP/1.0\r +Host: foo\r +Connection: close\r +user-agent: curl\r +bah: bah\r +a: a\r +b: b\r +c: c\r +d: d\r +e: e\r +f: f\r +g: g\r +h: h\r +i: i\r +j: j\r +k: k\r +l: l\r +m: m\r +n: n\r +o: o\r +p: p\r +foo-1: 1\r +foo-2: 2\r +foo-3: 3\r +foo-4: 4\r +foo-5: 5\r +foo-6: 6\r +foo-7: 7\r +foo-8: 8\r +foo-9: 9\r +foo-10: 10\r +foo-11: 11\r +foo-12: 12\r +foo-13: 13\r +foo-14: 14\r +foo-15: 15\r +foo-16: 16\r +foo-17: 17\r +foo-18: 18\r +foo-19: 19\r +foo-20: 20\r +foo-21: 21\r +\r +"; +} else { + $body = "GET /back HTTP/1.0\r Host: foo\r Connection: close\r User-Agent: curl\r @@ -1228,6 +1447,9 @@ foo-20: 20\r foo-21: 21\r \r " +} + +$body; @@ -1259,11 +1481,23 @@ GET /t --- more_headers My-Foo: bar Bar: baz ---- response_body -Bar: baz +--- response_body eval +my $body; + +if (defined($ENV{TEST_NGINX_USE_HTTP3})|| defined($ENV{TEST_NGINX_USE_HTTP2})) { + $body="bar: baz +host: localhost +my-foo: bar +"; +} else { + $body="Bar: baz Connection: close Host: localhost My-Foo: bar +"; +} + +$body; --- no_error_log [error] @@ -1863,7 +2097,7 @@ ok -=== TEST 57: execeeding custom 3 header limit +=== TEST 57: exceeding custom 3 header limit --- config location /lua { content_by_lua ' @@ -1898,10 +2132,12 @@ found 3 headers. lua exceeding request header limit 4 > 3 --- no_error_log [error] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 -=== TEST 58: NOT execeeding custom 3 header limit +=== TEST 58: NOT exceeding custom 3 header limit --- config location /lua { content_by_lua ' @@ -1934,10 +2170,12 @@ found 3 headers. --- no_error_log lua exceeding request header limit [error] +--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 -=== TEST 59: execeeding custom 3 header limit (raw) +=== TEST 59: exceeding custom 3 header limit (raw) --- config location /lua { content_by_lua ' @@ -1972,10 +2210,12 @@ found 3 headers. lua exceeding request header limit 4 > 3 --- no_error_log [error] +--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3} +--- no_http2 -=== TEST 60: NOT execeeding custom 3 header limit (raw) +=== TEST 60: NOT exceeding custom 3 header limit (raw) --- config location /lua { content_by_lua ' @@ -2002,12 +2242,22 @@ while ($i <= 1) { $i++; } $s ---- response_body -found 3 headers. +--- response_body eval +my $body; +if (!defined $ENV{TEST_NGINX_USE_HTTP2}) { + $body = "found 3 headers. +"; +} else { + $body = "found 2 headers. +"; +} + +$body; --- timeout: 4 --- no_error_log lua exceeding request header limit [error] +--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/030-uri-args.t b/t/030-uri-args.t index 4391b919f6..0633476c8d 100644 --- a/t/030-uri-args.t +++ b/t/030-uri-args.t @@ -1690,7 +1690,7 @@ bad argument #1 to 'set_uri_args' (string, number, or table expected, but got us === TEST 64: set_uri binary option with unsafe uri -explict specify binary option to true +explicit specify binary option to true --- config location /t { rewrite_by_lua_block { @@ -1717,7 +1717,7 @@ explict specify binary option to true === TEST 65: set_uri binary option with unsafe uri -explict specify binary option to false +explicit specify binary option to false --- config location /t { rewrite_by_lua_block { @@ -1743,7 +1743,7 @@ qr{\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: rewrite_by_ === TEST 66: set_uri binary option with safe uri -explict specify binary option to false +explicit specify binary option to false --- config location /t { rewrite_by_lua_block { diff --git a/t/031-post-args.t b/t/031-post-args.t index 4659f059fc..78805d3723 100644 --- a/t/031-post-args.t +++ b/t/031-post-args.t @@ -108,6 +108,7 @@ a=3&b=4&c --- request POST /lua --- response_body +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/033-ctx.t b/t/033-ctx.t index 8fc50aa7a0..77bd15f8c1 100644 --- a/t/033-ctx.t +++ b/t/033-ctx.t @@ -278,6 +278,8 @@ GET /t [error] --- error_log ngx.ctx = 32 +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ diff --git a/t/034-match.t b/t/034-match.t index 1bf45bce1a..fd4d9439e2 100644 --- a/t/034-match.t +++ b/t/034-match.t @@ -361,8 +361,11 @@ he } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -648,8 +651,12 @@ regex: (?:>[\w\s]*) } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "([0-9]+" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile\(\) failed: missing closing parenthesis in \"\([0-9]+\"\n" +: +"error: pcre_compile\(\) failed: missing \) in \"\([0-9]+\"\n" + --- no_error_log [error] @@ -939,8 +946,11 @@ nil } --- request GET /t ---- response_body_like chop -^error: pcre_exec\(\) failed: -10$ +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre_exec\(\) failed: -4\n" +: +"error: pcre_exec\(\) failed: -10\n" --- no_error_log [error] @@ -1050,8 +1060,14 @@ end --- request GET /re ---- response_body -error: pcre_exec() failed: -8 +--- response_body eval +# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE, +# but PCRE2 replaces this with pcre2_set_match_limit interface, +# which has different effects. +$Test::Nginx::Util::PcreVersion == 2 ? +"failed to match\n" +: +"error: pcre_exec() failed: -8\n" diff --git a/t/035-gmatch.t b/t/035-gmatch.t index 0dfeaeaf39..0a4efd2b25 100644 --- a/t/035-gmatch.t +++ b/t/035-gmatch.t @@ -698,8 +698,11 @@ not matched! } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -735,8 +738,11 @@ error: pcre_compile() failed: missing ) in "(abc" } --- request GET /t ---- response_body_like chop -error: pcre_exec\(\) failed: -10 +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre_exec\(\) failed: -4\n" +: +"error: pcre_exec\(\) failed: -10\n" --- no_error_log [error] @@ -854,8 +860,14 @@ end --- request GET /re ---- response_body -error: pcre_exec() failed: -8 +--- response_body eval +# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE, +# but PCRE2 replaces this with pcre2_set_match_limit interface, +# which has different effects. +$Test::Nginx::Util::PcreVersion == 2 ? +"failed to match\n" +: +"error: pcre_exec() failed: -8\n" diff --git a/t/036-sub.t b/t/036-sub.t index 3e88eba5a0..0657ebb2ca 100644 --- a/t/036-sub.t +++ b/t/036-sub.t @@ -480,8 +480,11 @@ a [b c] [b] [c] [] [] d } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -506,8 +509,11 @@ error: pcre_compile() failed: missing ) in "(abc" } --- request GET /t ---- response_body_like chop -error: pcre_exec\(\) failed: -10 +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre_exec\(\) failed: -4\n" +: +"error: pcre_exec\(\) failed: -10\n" --- no_error_log [error] @@ -610,8 +616,14 @@ ngx.say("sub: ", cnt) --- request GET /re ---- response_body -error: pcre_exec() failed: -8 +--- response_body eval +# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE, +# but PCRE2 replaces this with pcre2_set_match_limit interface, +# which has different effects. +$Test::Nginx::Util::PcreVersion == 2 ? +"sub: 0\n" +: +"error: pcre_exec() failed: -8\n" diff --git a/t/037-gsub.t b/t/037-gsub.t index 41f86ac013..a6fe579bd4 100644 --- a/t/037-gsub.t +++ b/t/037-gsub.t @@ -423,8 +423,11 @@ n: 1 } --- request GET /t ---- response_body_like chop -error: pcre_exec\(\) failed: -10 +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre_exec\(\) failed: -4\n" +: +"error: pcre_exec\(\) failed: -10\n" --- no_error_log [error] @@ -531,8 +534,14 @@ ngx.say("gsub: ", cnt) --- request GET /re ---- response_body -error: pcre_exec() failed: -8 +--- response_body eval +# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE, +# but PCRE2 replaces this with pcre2_set_match_limit interface, +# which has different effects. +$Test::Nginx::Util::PcreVersion == 2 ? +"gsub: 0\n" +: +"error: pcre_exec() failed: -8\n" diff --git a/t/038-match-o.t b/t/038-match-o.t index f6ae0103fe..cc80244e82 100644 --- a/t/038-match-o.t +++ b/t/038-match-o.t @@ -336,8 +336,11 @@ he } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] diff --git a/t/041-header-filter.t b/t/041-header-filter.t index 0adc699c6c..23730815da 100644 --- a/t/041-header-filter.t +++ b/t/041-header-filter.t @@ -124,6 +124,8 @@ Hi GET /read --- error_code --- response_body +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -447,6 +449,7 @@ GET /lua === TEST 21: lua error (string) +--- no_http2 --- config location /lua { set $foo ''; @@ -462,13 +465,16 @@ GET /lua GET /lua --- ignore_response --- error_log -failed to run header_filter_by_lua*: header_filter_by_lua:2: Something bad +failed to run header_filter_by_lua*: header_filter_by_lua(nginx.conf:47):2: Something bad --- no_error_log [alert] +--- curl_error eval +qr/curl: \(56\) Failure when receiving data from the peer|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(28\) Remote peer returned unexpected data|curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server/ === TEST 22: lua error (nil) +--- no_http2 --- config location /lua { set $foo ''; @@ -487,6 +493,8 @@ GET /lua failed to run header_filter_by_lua*: unknown reason --- no_error_log [alert] +--- curl_error eval +qr/curl: \(56\) Failure when receiving data from the peer|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(28\) Remote peer returned unexpected data|curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -501,6 +509,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -515,6 +525,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -529,6 +541,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -543,6 +557,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -557,6 +573,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -575,6 +593,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -593,6 +613,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -607,6 +629,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -621,6 +645,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -635,6 +661,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -665,8 +693,10 @@ uri: /blah --- request GET /lua --- ignore_response ---- error_log -API disabled in the context of header_filter_by_lua* +--- error_log eval +qr/API disabled in the context of header_filter_by_lua\*|http3 requests are not supported without content-length header/ms +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -679,8 +709,18 @@ API disabled in the context of header_filter_by_lua* --- request GET /lua --- ignore_response ---- error_log -API disabled in the context of header_filter_by_lua* +--- error_log eval +my $err_log; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $err_log = "http v3 not supported yet"; +} else { + $err_log = "API disabled in the context of header_filter_by_lua*"; +} + +$err_log; +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -695,6 +735,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -709,6 +751,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -756,6 +800,8 @@ stack traceback: in function 'error' in function 'bar' in function 'foo' +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -774,6 +820,8 @@ GET /lua?a=1&b=2 --- ignore_response --- error_log eval qr/failed to load external Lua file ".*?test2\.lua": cannot open .*? No such file or directory/ +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -799,3 +847,84 @@ GET /t --- error_code: 302 --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) + + + +=== TEST 42: syntax error in header_filter_by_lua_block +--- config + location /lua { + + header_filter_by_lua_block { + 'for end'; + } + content_by_lua_block { + ngx.say("Hello world") + } + } +--- request +GET /lua +--- ignore_response +--- error_log +failed to load inlined Lua code: header_filter_by_lua(nginx.conf:41):2: unexpected symbol near ''for end'' +--- no_error_log +no_such_error +--- curl_error eval +qr/curl: \(56\) Failure when receiving data from the peer|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly: INTERNAL_ERROR \(err 2\)/ + + + +=== TEST 43: syntax error in second content_by_lua_block +--- config + location /foo { + header_filter_by_lua_block { + 'for end'; + } + content_by_lua_block { + ngx.say("Hello world") + } + } + + location /lua { + header_filter_by_lua_block { + 'for end'; + } + content_by_lua_block { + ngx.say("Hello world") + } + } +--- request +GET /lua +--- ignore_response +--- error_log +failed to load inlined Lua code: header_filter_by_lua(nginx.conf:49):2: unexpected symbol near ''for end'' +--- no_error_log +no_such_error +--- curl_error eval +qr/curl: \(56\) Failure when receiving data from the peer|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly: INTERNAL_ERROR \(err 2\)/ + + + +=== TEST 44: syntax error in /tmp/12345678901234567890123456789012345.conf +--- config + location /lua { + content_by_lua_block { + ngx.say("Hello world") + } + + include /tmp/12345678901234567890123456789012345.conf; + } +--- user_files +>>> /tmp/12345678901234567890123456789012345.conf + header_filter_by_lua_block { + 'for end'; + } +--- request +GET /lua +--- ignore_response +--- error_log +failed to load inlined Lua code: header_filter_by_lua(...901234567890123456789012345.conf:1):2: unexpected symbol near ''for end'' +--- no_error_log +[alert] +--- curl_error eval +qr/curl: \(56\) Failure when receiving data from the peer|curl: \(56\) Failure when receiving data from the peer|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly: INTERNAL_ERROR \(err 2\)/ diff --git a/t/043-shdict.t b/t/043-shdict.t index b0528e5cc7..3c0cc7961a 100644 --- a/t/043-shdict.t +++ b/t/043-shdict.t @@ -158,7 +158,7 @@ hello content_by_lua ' local dogs = ngx.shared.dogs dogs:set("foo", 32, 0.01) - ngx.location.capture("/sleep/0.01") + ngx.location.capture("/sleep/0.011") ngx.say(dogs:get("foo")) '; } @@ -768,7 +768,7 @@ foo = 10502 dogs:set("bar", 32, 0.001) dogs:set("baz", 32, 0.001) dogs:set("foo", 32, 0.001) - ngx.location.capture("/sleep/0.002") + ngx.location.capture("/sleep/0.003") local res, err, forcible = dogs:add("foo", 10502) ngx.say("add: ", res, " ", err, " ", forcible) ngx.say("foo = ", dogs:get("foo")) @@ -797,7 +797,7 @@ foo = 10502 dogs:set("bar", 32, 0.001) dogs:set("baz", 32, 0.001) dogs:set("foo", "hi", 0.001) - ngx.location.capture("/sleep/0.002") + ngx.location.capture("/sleep/0.003") local res, err, forcible = dogs:add("foo", "hello") ngx.say("add: ", res, " ", err, " ", forcible) ngx.say("foo = ", dogs:get("foo")) @@ -1131,7 +1131,7 @@ nil nil content_by_lua ' local dogs = ngx.shared.dogs dogs:set("foo", 32, 0.01, 255) - ngx.location.capture("/sleep/0.01") + ngx.location.capture("/sleep/0.011") local res, flags = dogs:get("foo") ngx.say("res = ", res, ", flags = ", flags) '; @@ -1184,6 +1184,7 @@ nil nil === TEST 45: flush_expires +--- quic_max_idle_timeout: 1.6 --- http_config lua_shared_dict dogs 1m; --- config @@ -1210,6 +1211,7 @@ GET /t === TEST 46: flush_expires with number +--- quic_max_idle_timeout: 1.6 --- http_config lua_shared_dict dogs 1m; --- config @@ -1337,6 +1339,7 @@ GET /t === TEST 51: list all keys in a shdict with expires +--- quic_max_idle_timeout: 1.6 --- http_config lua_shared_dict dogs 1m; --- config @@ -1426,6 +1429,7 @@ GET /t === TEST 55: list all keys in a shdict with all keys expired +--- quic_max_idle_timeout: 1.6 --- http_config lua_shared_dict dogs 1m; --- config diff --git a/t/044-req-body.t b/t/044-req-body.t index e645eeefe5..d72b1eb632 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -7,7 +7,7 @@ log_level('warn'); repeat_each(2); -plan tests => repeat_each() * (blocks() * 4 + 52 ); +plan tests => repeat_each() * (blocks() * 4 + 58); #no_diff(); no_long_string(); @@ -351,9 +351,18 @@ hello, world --- user_files >>> a.txt Will you change this world? ---- raw_response_headers_like -X-Old: \S+/client_body_temp/\d+\r -.*?X-New: \S+/html/a\.txt\r +--- raw_response_headers_like eval +my $headers; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $headers = qr#x-old: \S+/client_body_temp/\d+\r +.*?x-new: \S+/html/a\.txt\r#; +} else { + $headers = qr#X-Old: \S+/client_body_temp/\d+\r +.*?X-New: \S+/html/a\.txt\r#; +} + +$headers; --- response_body Will you change this world? --- no_error_log @@ -390,9 +399,18 @@ hello, world! --- user_files >>> a.txt Will you change this world? ---- raw_response_headers_like -X-Old: \S+/client_body_temp/\d+\r -.*?X-New: \S+/html/a\.txt\r +--- raw_response_headers_like eval +my $headers; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $headers = qr#x-old: \S+/client_body_temp/\d+\r +.*?x-new: \S+/html/a\.txt\r#; +} else { + $headers = qr#X-Old: \S+/client_body_temp/\d+\r +.*?X-New: \S+/html/a\.txt\r#; +} + +$headers; --- response_body Will you change this world? --- no_error_log @@ -440,7 +458,7 @@ b.txt exists: yes -=== TEST 17: read buffered body to memoary and reset it to a new file (auto-clean) +=== TEST 17: read buffered body to memory and reset it to a new file (auto-clean) --- config client_body_in_file_only off; @@ -476,7 +494,7 @@ qr/500 Internal Server Error/] -=== TEST 18: read buffered body to memoary and reset it to a new file (no auto-clean) +=== TEST 18: read buffered body to memory and reset it to a new file (no auto-clean) --- config client_body_in_file_only off; @@ -898,6 +916,7 @@ body: hell --- no_error_log [error] [alert] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} @@ -959,6 +978,7 @@ body file: hello [alert] --- error_log a client request body is buffered to a temporary file +--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3} @@ -985,9 +1005,9 @@ a client request body is buffered to a temporary file --- error_code: 500 --- error_log eval qr/lua entry thread aborted: runtime error: content_by_lua\(nginx\.conf:\d+\):2: request body not read yet/ - --- no_error_log [alert] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} @@ -1017,8 +1037,8 @@ body: hell --- no_error_log [error] [alert] ---- no_error_log a client request body is buffered to a temporary file +--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3} @@ -1241,8 +1261,8 @@ body: hello, my dear friend! --- no_error_log [error] [alert] ---- no_error_log a client request body is buffered to a temporary file +--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3} @@ -1382,8 +1402,8 @@ failed to get req socket: request body already exists --- no_error_log [error] [alert] ---- no_error_log a client request body is buffered to a temporary file +--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3} @@ -1405,6 +1425,7 @@ Expect: 100-Continue [alert] [error] http finalize request: 500, "/test?" a:1, c:0 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -1480,13 +1501,22 @@ probe syscall.fcntl { --- stap_out_unlike fcntl\(O_DIRECT\) ---- raw_response_headers_like -.*?X-New: \S+/html/a\.txt\r +--- raw_response_headers_like eval +my $headers; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $headers = qr#.*?x-new: \S+/html/a\.txt\r#; +} else { + $headers = qr#.*?X-New: \S+/html/a\.txt\r#; +} + +$headers; --- response_body Will you change this world? --- no_error_log [error] [alert] +--- skip_eval: 6:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/045-ngx-var.t b/t/045-ngx-var.t index 81fcef6458..6fe5590d99 100644 --- a/t/045-ngx-var.t +++ b/t/045-ngx-var.t @@ -170,10 +170,22 @@ invalid referer: 1 } --- request GET /t ---- raw_response_headers_like -Proxy-Host: 127.0.0.1\:\d+\r +--- raw_response_headers_like eval +my $headers; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $headers = +qr/proxy-host: 127.0.0.1\:\d+\r +proxy-port: \d+\r +proxy-add-x-forwarded-for: 127.0.0.1\r/; +} else { + $headers = +qr/Proxy-Host: 127.0.0.1\:\d+\r Proxy-Port: \d+\r -Proxy-Add-X-Forwarded-For: 127.0.0.1\r +Proxy-Add-X-Forwarded-For: 127.0.0.1\r/; +} + +$headers; --- response_body hello --- no_error_log @@ -248,19 +260,19 @@ variable "query_string" not changeable } server { # this is the real entry point - listen 8091; + listen $TEST_NGINX_RAND_PORT_1; location / { content_by_lua_block{ - ngx.print("this is backend peer 8091") + ngx.print("this is backend peer $TEST_NGINX_RAND_PORT_1") } } } server { # this is the real entry point - listen 8092; + listen $TEST_NGINX_RAND_PORT_2; location / { content_by_lua_block{ - ngx.print("this is backend peer 8092") + ngx.print("this is backend peer $TEST_NGINX_RAND_PORT_2") } } } @@ -275,6 +287,6 @@ variable "query_string" not changeable proxy_pass http://balancer; } --- pipelined_requests eval -["GET /balancer?port=8091", "GET /balancer?port=8092"] +["GET /balancer?port=\$TEST_NGINX_RAND_PORT_1", "GET /balancer?port=\$TEST_NGINX_RAND_PORT_2"] --- response_body eval -["this is backend peer 8091", "this is backend peer 8092"] +["this is backend peer \$TEST_NGINX_RAND_PORT_1", "this is backend peer \$TEST_NGINX_RAND_PORT_2"] diff --git a/t/047-match-jit.t b/t/047-match-jit.t index f53a4083a2..9b40a889dc 100644 --- a/t/047-match-jit.t +++ b/t/047-match-jit.t @@ -32,8 +32,11 @@ __DATA__ GET /re --- response_body 1234 ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -53,8 +56,11 @@ pcre JIT compiling result: 1 GET /re --- response_body not matched! ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -76,9 +82,15 @@ pcre JIT compiling result: 1 1234 --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -101,9 +113,15 @@ qr/pcre JIT compiling result: \d+/ not matched! --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -128,8 +146,11 @@ qr/pcre JIT compiling result: \d+/ } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -170,8 +191,15 @@ end --- request GET /re ---- response_body -error: pcre_exec() failed: -8 +--- response_body eval +# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE, +# but PCRE2 replaces this with pcre2_set_match_limit interface, +# which has different effects. +$Test::Nginx::Util::PcreVersion == 2 ? +# PCRE2_ERROR_MATCHLIMIT (-47) +"error: pcre_exec() failed: -47\n" +: +"error: pcre_exec() failed: -8\n" diff --git a/t/048-match-dfa.t b/t/048-match-dfa.t index edf3662d4f..023231b5c0 100644 --- a/t/048-match-dfa.t +++ b/t/048-match-dfa.t @@ -207,3 +207,41 @@ exec opts: 0 你 --- no_error_log [error] + + + +=== TEST 9: matched with do +--- config + location /re { + content_by_lua ' + local m = ngx.re.match("hello", "(h)(e)(l)", "jo") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(m[3]) + else + ngx.say("not matched!") + end + local m = ngx.re.match("horld", "(h)(e)?(l)?", "jo") + if m then + ngx.say(m[0]) + ngx.say(m[1]) + ngx.say(m[2]) + ngx.say(m[3]) + else + ngx.say("not matched!") + end + '; + } +--- request + GET /re +--- response_body +hel +h +e +l +h +h +false +false diff --git a/t/049-gmatch-jit.t b/t/049-gmatch-jit.t index 5134d526b5..b76df35e98 100644 --- a/t/049-gmatch-jit.t +++ b/t/049-gmatch-jit.t @@ -34,8 +34,11 @@ __DATA__ --- response_body hello world ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -60,8 +63,11 @@ pcre JIT compiling result: 1 nil nil nil ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -77,8 +83,11 @@ pcre JIT compiling result: 1 GET /re --- response_body done ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -99,8 +108,11 @@ pcre JIT compiling result: 1 GET /re --- response_body hello ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -124,9 +136,15 @@ hello world --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -154,9 +172,15 @@ nil nil --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -175,9 +199,15 @@ qr/pcre JIT compiling result: \d+/ done --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -201,9 +231,15 @@ qr/pcre JIT compiling result: \d+/ hello --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -222,7 +258,10 @@ qr/pcre JIT compiling result: \d+/ } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] diff --git a/t/050-gmatch-dfa.t b/t/050-gmatch-dfa.t index d2ac2bc666..4c5f820e49 100644 --- a/t/050-gmatch-dfa.t +++ b/t/050-gmatch-dfa.t @@ -214,8 +214,11 @@ hello } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] diff --git a/t/051-sub-jit.t b/t/051-sub-jit.t index c8a49c0516..7aec7ccb79 100644 --- a/t/051-sub-jit.t +++ b/t/051-sub-jit.t @@ -32,8 +32,11 @@ __DATA__ GET /re --- response_body hello, world 5678: 1 ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -53,8 +56,11 @@ pcre JIT compiling result: 1 GET /re --- response_body hello, world: 0 ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -76,9 +82,15 @@ pcre JIT compiling result: 1 hello, world 5678: 1 --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -101,9 +113,15 @@ qr/pcre JIT compiling result: \d+/ hello, world: 0 --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -122,8 +140,11 @@ qr/pcre JIT compiling result: \d+/ } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -143,7 +164,10 @@ error: pcre_compile() failed: missing ) in "(abc" } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] diff --git a/t/052-sub-dfa.t b/t/052-sub-dfa.t index 254913b3e0..2329c77aa3 100644 --- a/t/052-sub-dfa.t +++ b/t/052-sub-dfa.t @@ -107,8 +107,11 @@ hello, world: 0 } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -129,8 +132,11 @@ error: pcre_compile() failed: missing ) in "(abc" } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] diff --git a/t/053-gsub-jit.t b/t/053-gsub-jit.t index 3efc0a24a5..c7c87ee29c 100644 --- a/t/053-gsub-jit.t +++ b/t/053-gsub-jit.t @@ -32,8 +32,11 @@ __DATA__ GET /re --- response_body hello, world world: 2 ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -53,8 +56,11 @@ pcre JIT compiling result: 1 GET /re --- response_body hello, world: 0 ---- error_log -pcre JIT compiling result: 1 +--- error_log eval +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully\n" +: +"pcre JIT compiling result: 1\n" @@ -76,9 +82,15 @@ pcre JIT compiling result: 1 hello, world world: 2 --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -101,9 +113,15 @@ qr/pcre JIT compiling result: \d+/ hello, world: 0 --- grep_error_log eval -qr/pcre JIT compiling result: \d+/ +$Test::Nginx::Util::PcreVersion == 2 ? +"pcre2 JIT compiled successfully" +: +"pcre JIT compiling result: 1" --- grep_error_log_out eval +$Test::Nginx::Util::PcreVersion == 2 ? +["pcre2 JIT compiled successfully\n", ""] +: ["pcre JIT compiling result: 1\n", ""] @@ -122,8 +140,11 @@ qr/pcre JIT compiling result: \d+/ } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -143,7 +164,10 @@ error: pcre_compile() failed: missing ) in "(abc" } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] diff --git a/t/054-gsub-dfa.t b/t/054-gsub-dfa.t index 937aa1c421..bd0825d4ba 100644 --- a/t/054-gsub-dfa.t +++ b/t/054-gsub-dfa.t @@ -107,8 +107,11 @@ hello, world: 0 } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" @@ -126,8 +129,11 @@ error: pcre_compile() failed: missing ) in "(abc" } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] diff --git a/t/056-flush.t b/t/056-flush.t index bdd33d4f9a..bb81c1ecf3 100644 --- a/t/056-flush.t +++ b/t/056-flush.t @@ -43,6 +43,7 @@ hiya [error] --- error_log lua reuse free buf chain, but reallocate memory because 5 >= 0 +--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -491,6 +492,7 @@ GET /test === TEST 17: limit_rate +--- quic_max_idle_timeout: 2 --- config location /test { limit_rate 150; @@ -512,11 +514,19 @@ GET /test --- response_body eval "a" x 200 --- error_log eval -[ +my @errlog; +if (defined $ENV{TEST_NGINX_USE_HTTP2}) { + @errlog = [ +qr/lua writes elapsed (?:0\.[7-9]\d+|[12]\.\d+) sec/, +qr/lua flush requires waiting: buffered 0x[0-9a-f]+, delayed:1/, +]; +} else { + @errlog = [ qr/lua writes elapsed [12](?:\.\d+)? sec/, qr/lua flush requires waiting: buffered 0x[0-9a-f]+, delayed:1/, -] - +]; +} +@errlog; --- no_error_log [error] --- timeout: 4 diff --git a/t/057-flush-timeout.t b/t/057-flush-timeout.t index 1f8cfaadf9..8f0b7790a0 100644 --- a/t/057-flush-timeout.t +++ b/t/057-flush-timeout.t @@ -19,9 +19,14 @@ BEGIN { $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'hello, world'; $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1; + delete($ENV{TEST_NGINX_USE_HTTP2}); + + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "HTTP3 does not support mockeagain"; + } } -use Test::Nginx::Socket::Lua; +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); use t::StapThread; our $GCScript = $t::StapThread::GCScript; diff --git a/t/058-tcp-socket.t b/t/058-tcp-socket.t index 593e49461f..45d3073f5e 100644 --- a/t/058-tcp-socket.t +++ b/t/058-tcp-socket.t @@ -1,15 +1,17 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: use Test::Nginx::Socket::Lua; +use Test::Nginx::Socket::Lua::Stream; repeat_each(2); -plan tests => repeat_each() * 231; +plan tests => repeat_each() * (blocks() * 3 + 21); our $HtmlDir = html_dir; $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); #log_level 'warn'; log_level 'debug'; @@ -21,6 +23,7 @@ run_tests(); __DATA__ === TEST 1: sanity +--- no_http2 --- config server_tokens off; location /t { @@ -90,6 +93,7 @@ close: 1 nil === TEST 2: no trailing newline +--- no_http2 --- config server_tokens off; location /t { @@ -253,10 +257,12 @@ second line received: (?:Date|Server): .*? --- no_error_log [error] --- timeout: 10 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} === TEST 5: connection refused (tcp) +--- no_http2 --- config location /test { content_by_lua ' @@ -327,6 +333,7 @@ lua tcp socket connect timed out, when connecting to 127.0.0.2:12345 === TEST 7: not closed manually +--- no_http2 --- config server_tokens off; location /t { @@ -441,6 +448,7 @@ attempt to send data on a closed socket === TEST 10: explicit *l pattern for receive +--- no_http2 --- config server_tokens off; location /t { @@ -509,6 +517,7 @@ close: 1 nil === TEST 11: *a pattern for receive +--- no_http2 --- config server_tokens off; location /t { @@ -577,6 +586,7 @@ close: 1 nil === TEST 12: mixing *a and *l patterns for receive +--- no_http2 --- config server_tokens off; location /t { @@ -656,6 +666,7 @@ close: 1 nil === TEST 13: receive by chunks +--- no_http2 --- timeout: 5 --- config server_tokens off; @@ -731,6 +742,7 @@ close: 1 nil === TEST 14: receive by chunks (very small buffer) +--- no_http2 --- timeout: 5 --- config server_tokens off; @@ -807,6 +819,7 @@ close: 1 nil === TEST 15: line reading (very small buffer) +--- no_http2 --- config server_tokens off; lua_socket_buffer_size 1; @@ -876,6 +889,7 @@ close: 1 nil === TEST 16: ngx.socket.connect (working) +--- no_http2 --- config server_tokens off; location /t { @@ -982,6 +996,7 @@ qr/connect\(\) failed \(\d+: Connection refused\)/ === TEST 18: receive by chunks (stringified size) +--- no_http2 --- config server_tokens off; location /t { @@ -1056,6 +1071,7 @@ close: 1 nil === TEST 19: cannot survive across request boundary (send) +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -1115,6 +1131,7 @@ received: OK|failed to send request: closed)\$" === TEST 20: cannot survive across request boundary (receive) +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -1190,6 +1207,7 @@ received: OK|failed to receive a line: closed \[nil\])$/ === TEST 21: cannot survive across request boundary (close) +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -1259,6 +1277,7 @@ received: OK|failed to close: closed)$/ === TEST 22: cannot survive across request boundary (connect) +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -1335,6 +1354,7 @@ lua reuse socket upstream ctx === TEST 23: connect again immediately +--- no_http2 --- config server_tokens off; location /t { @@ -1403,6 +1423,7 @@ close: 1 nil === TEST 24: two sockets mix together +--- no_http2 --- config server_tokens off; location /t { @@ -1494,6 +1515,7 @@ GET /t === TEST 25: send tables of string fragments (with integers too) +--- no_http2 --- config server_tokens off; location /t { @@ -1564,6 +1586,7 @@ close: 1 nil === TEST 26: send tables of string fragments (bad type "nil") +--- no_http2 --- config server_tokens off; location /t { @@ -1619,10 +1642,13 @@ GET /t --- ignore_response --- error_log bad argument #1 to 'send' (bad data type nil found) +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# === TEST 27: send tables of string fragments (bad type "boolean") +--- no_http2 --- config server_tokens off; location /t { @@ -1678,10 +1704,13 @@ GET /t --- ignore_response --- error_log bad argument #1 to 'send' (bad data type boolean found) +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# === TEST 28: send tables of string fragments (bad type ngx.null) +--- no_http2 --- config server_tokens off; location /t { @@ -1737,10 +1766,13 @@ GET /t --- ignore_response --- error_log bad argument #1 to 'send' (bad data type userdata found) +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# === TEST 29: cosocket before location capture (tcpsock:send did not clear u->waiting) +--- no_http2 --- config server_tokens off; location /t { @@ -1808,6 +1840,7 @@ subrequest: 200, OK\r === TEST 30: CR in a line +--- no_http2 --- config server_tokens off; location /t { @@ -1877,6 +1910,7 @@ close: nil closed === TEST 31: receive(0) +--- no_http2 --- config server_tokens off; location /t { @@ -1935,6 +1969,7 @@ close: 1 nil === TEST 32: send("") +--- no_http2 --- config server_tokens off; location /t { @@ -2176,6 +2211,7 @@ lua tcp socket read timed out === TEST 37: successful reread after a read time out happen (receive -> receive) +--- no_http2 --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -2253,6 +2289,7 @@ lua tcp socket read timed out === TEST 38: successful reread after a read time out happen (receive -> receiveuntil) +--- no_http2 --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -2333,6 +2370,7 @@ lua tcp socket read timed out === TEST 39: successful reread after a read time out happen (receiveuntil -> receiveuntil) +--- no_http2 --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -2415,6 +2453,7 @@ lua tcp socket read timed out === TEST 40: successful reread after a read time out happen (receiveuntil -> receive) +--- no_http2 --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -2495,6 +2534,7 @@ lua tcp socket read timed out === TEST 41: receive(0) +--- no_http2 --- config server_tokens off; location /t { @@ -2542,6 +2582,7 @@ close: 1 nil === TEST 42: empty options table +--- no_http2 --- config server_tokens off; location /t { @@ -2580,6 +2621,7 @@ close: 1 nil === TEST 43: u->coctx left over bug +--- no_http2 --- config server_tokens off; location = /t { @@ -2671,6 +2713,7 @@ lua clean up the timer for pending ngx.sleep === TEST 44: bad request tries to connect +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -2725,6 +2768,7 @@ qr/runtime error: content_by_lua\(nginx\.conf:\d+\):7: bad request/ === TEST 45: bad request tries to receive +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -2782,6 +2826,7 @@ qr/runtime error: content_by_lua\(nginx\.conf:\d+\):14: bad request/ === TEST 46: bad request tries to send +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -2839,6 +2884,7 @@ qr/runtime error: content_by_lua\(nginx\.conf:\d+\):14: bad request/ === TEST 47: bad request tries to close +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -2896,6 +2942,7 @@ qr/runtime error: content_by_lua\(nginx\.conf:\d+\):14: bad request/ === TEST 48: bad request tries to set keepalive +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -2953,6 +3000,7 @@ qr/runtime error: content_by_lua\(nginx\.conf:\d+\):14: bad request/ === TEST 49: bad request tries to receiveuntil +--- no_http2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -3084,6 +3132,7 @@ could not cancel === TEST 52: tcp_nodelay on +--- no_http2 --- config tcp_nodelay on; server_tokens off; @@ -3157,6 +3206,7 @@ lua socket tcp_nodelay === TEST 53: tcp_nodelay off +--- no_http2 --- config tcp_nodelay off; server_tokens off; @@ -3348,6 +3398,7 @@ lua tcp socket connect timeout: 100 === TEST 56: reuse cleanup +--- no_http2 --- config server_tokens off; location /t { @@ -3412,6 +3463,7 @@ lua http cleanup reuse === TEST 57: reuse cleanup in ngx.timer (fake_request) +--- no_http2 --- config server_tokens off; location /t { @@ -3495,6 +3547,7 @@ lua http cleanup reuse === TEST 58: free cleanup in ngx.timer (without sock:close) +--- no_http2 --- config server_tokens off; location /t { @@ -3576,6 +3629,7 @@ total_send_bytes: 114 === TEST 59: reuse cleanup in subrequest +--- no_http2 --- config server_tokens off; location /t { @@ -3644,6 +3698,7 @@ lua http cleanup reuse === TEST 60: setkeepalive on socket already shutdown +--- no_http2 --- config location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -3682,6 +3737,7 @@ failed to setkeepalive: closed === TEST 61: options_table is nil +--- no_http2 --- config location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -3788,6 +3844,7 @@ failed to connect: bad port number: 65536 === TEST 64: send boolean and nil +--- no_http2 --- config location /t { set $port $TEST_NGINX_SERVER_PORT; @@ -3850,6 +3907,7 @@ received: truefalsenil === TEST 65: receiveany method in cosocket +--- no_http2 --- config server_tokens off; location = /t { @@ -3935,6 +3993,7 @@ hello world [error] --- error_log lua tcp socket read any +--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "") @@ -3942,10 +4001,13 @@ lua tcp socket read any --- config server_tokens off; location = /t { + set $port $TEST_NGINX_RAND_PORT_1; + content_by_lua_block { local sock = ngx.socket.tcp() + local port = ngx.var.port sock:settimeout(500) - assert(sock:connect("127.0.0.1", 7658)) + assert(sock:connect("127.0.0.1", port)) while true do local data, err = sock:receiveany(1024) @@ -3972,7 +4034,7 @@ lua tcp socket read any --- request GET /t ---- tcp_listen: 7658 +--- tcp_listen: $TEST_NGINX_RAND_PORT_1 --- tcp_shutdown: 1 --- tcp_query eval: "send data after read side closed" --- tcp_query_len: 32 @@ -3983,6 +4045,7 @@ GET /t === TEST 67: receiveany with limited, max <= 0 +--- no_http2 --- config location = /t { set $port $TEST_NGINX_SERVER_PORT; @@ -4019,6 +4082,7 @@ GET /t === TEST 68: receiveany with limited, max is larger than data +--- no_http2 --- config server_tokens off; location = /t { @@ -4084,10 +4148,12 @@ hello world [error] --- error_log lua tcp socket calling receiveany() method to read at most 128 bytes +--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "") === TEST 69: receiveany with limited, max is smaller than data +--- no_http2 --- config server_tokens off; location = /t { @@ -4158,10 +4224,12 @@ orld [error] --- error_log lua tcp socket calling receiveany() method to read at most 7 bytes +--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "") === TEST 70: send tables of string fragments (with floating point number too) +--- no_http2 --- config server_tokens off; location /t { @@ -4236,6 +4304,7 @@ close: 1 nil === TEST 71: send numbers the maximum number of significant digits is 14 in lua +--- no_http2 --- config server_tokens off; location /t { @@ -4367,3 +4436,209 @@ connect failed: missing the port number finish --- no_error_log [error] + + + +=== TEST 73: reset the buffer pos when keepalive +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + for i = 1, 10 + do + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local req = "GET /hi HTTP/1.1\r\nHost: localhost\r\n\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 not line then + ngx.say("receive err: ", err) + return + end + + data, err = sock:receiveany(4096) + if not data then + ngx.say("receiveany er: ", err) + return + end + + ok, err = sock:setkeepalive(10000, 32) + if not ok then + ngx.say("reused times: ", i, ", setkeepalive err: ", err) + return + end + end + ngx.say("END") + } + } + + location /hi { + keepalive_requests 3; + content_by_lua_block { + ngx.say("Hello") + } + + more_clear_headers Date; + } + +--- request +GET /t +--- response_body +reused times: 3, setkeepalive err: closed +--- no_error_log +[error] +--- skip_eval: 3: $ENV{TEST_NGINX_EVENT_TYPE} && $ENV{TEST_NGINX_EVENT_TYPE} ne 'epoll' + + + +=== TEST 74: setkeepalive with TLSv1.3 +--- skip_openssl: 3: < 1.1.1 +--- stream_server_config + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_protocols TLSv1.3; + + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + local data + while true do + data = assert(sock:receive()) + assert(data == "hello") + end + } +--- config + location /test { + lua_ssl_protocols TLSv1.3; + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local ok, err = sock:sslhandshake(false, nil, false) + if not ok then + ngx.say("failed to sslhandshake: ", err) + return + end + + local ok, err = sock:send("hello\n") + if not ok then + ngx.say("failed to send: ", err) + return + end + + -- sleep a while to make sure the NewSessionTicket message has arrived + ngx.sleep(1) + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to setkeepalive: ", err) + else + ngx.say("setkeepalive: ", ok) + end + } + } +--- request +GET /test +--- response_body +connected: 1 +setkeepalive: 1 +--- no_error_log +[error] + + + +=== TEST 75: getfd() +--- no_http2 +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; +--- config + server_tokens off; + location /t { + #set $port 5000; + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local s, err = sock:getfd() + ngx.say("fd: ", s) + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + content_by_lua 'ngx.say("foo")'; + more_clear_headers Date; + } + +--- request +GET /t +--- response_body eval +qr/fd: \d+ +connected: 1 +request sent: 57 +received: HTTP\/1.1 200 OK +received: Server: nginx +received: Content-Type: text\/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed \[\] +close: 1 nil +/ms +--- no_error_log +[error] diff --git a/t/062-count.t b/t/062-count.t index 807e618e21..3fda3b9547 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -34,7 +34,7 @@ __DATA__ --- request GET /test --- response_body -ngx: 113 +ngx: 117 --- no_error_log [error] @@ -55,7 +55,7 @@ ngx: 113 --- request GET /test --- response_body -113 +117 --- no_error_log [error] @@ -83,7 +83,7 @@ GET /test --- request GET /test --- response_body -n = 113 +n = 117 --- no_error_log [error] @@ -259,9 +259,10 @@ n = 10 POST /test hello world --- response_body -n = 6 +n = 7 --- no_error_log [error] +--- skip_eval: 3: $ENV{TEST_NGINX_USE_HTTP3} @@ -305,7 +306,7 @@ GET /t --- response_body_like: 404 Not Found --- error_code: 404 --- error_log -ngx. entry count: 113 +ngx. entry count: 117 @@ -438,7 +439,7 @@ thread: 3 --- request GET /test --- response_body -worker: 4 +worker: 5 --- no_error_log [error] @@ -459,7 +460,7 @@ worker: 4 --- request GET /test --- response_body -n = 14 +n = 17 --- no_error_log [error] @@ -480,7 +481,7 @@ n = 14 --- request GET /test --- response_body -n = 6 +n = 7 --- no_error_log [error] @@ -512,9 +513,10 @@ n = 6 --- request GET /test --- response_body -n = 7 +n = 8 --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -554,6 +556,7 @@ narr = 2 nrec = 3 --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -589,3 +592,4 @@ narr = 2 nrec = 3 --- no_error_log [error] +--- skip_eval: 3: $ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/065-tcp-socket-timeout.t b/t/065-tcp-socket-timeout.t index 092094a3c9..14563f70c6 100644 --- a/t/065-tcp-socket-timeout.t +++ b/t/065-tcp-socket-timeout.t @@ -16,6 +16,7 @@ BEGIN { $ENV{MOCKEAGAIN} = 'w'; } + delete($ENV{TEST_NGINX_USE_HTTP2}); $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'get helloworld'; } @@ -873,6 +874,7 @@ quitting request now --- no_error_log lua tcp socket write timed out [alert] +--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/066-socket-receiveuntil.t b/t/066-socket-receiveuntil.t index 89d2abf373..43744a3721 100644 --- a/t/066-socket-receiveuntil.t +++ b/t/066-socket-receiveuntil.t @@ -71,6 +71,7 @@ close: 1 nil === TEST 2: http read lines +--- no_http2 --- config server_tokens off; location /t { @@ -140,6 +141,7 @@ close: 1 nil === TEST 3: http read all the headers in a single run +--- no_http2 --- config server_tokens off; location /t { @@ -208,6 +210,7 @@ close: 1 nil === TEST 4: ambiguous boundary patterns (abcabd) +--- no_http2 --- config server_tokens off; location /t { @@ -279,6 +282,7 @@ close: 1 nil === TEST 5: ambiguous boundary patterns (aa) +--- no_http2 --- config server_tokens off; location /t { @@ -350,6 +354,7 @@ close: 1 nil === TEST 6: ambiguous boundary patterns (aaa) +--- no_http2 --- config server_tokens off; location /t { @@ -421,6 +426,7 @@ close: 1 nil === TEST 7: ambiguous boundary patterns (aaaaad) +--- no_http2 --- config server_tokens off; location /t { @@ -492,6 +498,7 @@ close: 1 nil === TEST 8: ambiguous boundary patterns (aaaaad), small buffer, 2 bytes +--- no_http2 --- config server_tokens off; lua_socket_buffer_size 2; @@ -564,6 +571,7 @@ close: 1 nil === TEST 9: ambiguous boundary patterns (aaaaad), small buffer, 1 byte +--- no_http2 --- config server_tokens off; lua_socket_buffer_size 1; @@ -636,6 +644,7 @@ close: 1 nil === TEST 10: ambiguous boundary patterns (abcabdabcabe) +--- no_http2 --- config server_tokens off; location /t { @@ -707,6 +716,7 @@ close: 1 nil === TEST 11: ambiguous boundary patterns (abcabdabcabe 2) +--- no_http2 --- config server_tokens off; location /t { @@ -778,6 +788,7 @@ close: 1 nil === TEST 12: ambiguous boundary patterns (abcabdabcabe 3) +--- no_http2 --- config server_tokens off; location /t { @@ -849,6 +860,7 @@ close: 1 nil === TEST 13: ambiguous boundary patterns (abcabdabcabe 4) +--- no_http2 --- config server_tokens off; location /t { @@ -920,6 +932,7 @@ close: 1 nil === TEST 14: ambiguous boundary patterns (--abc) +--- no_http2 --- config server_tokens off; location /t { @@ -991,6 +1004,7 @@ close: 1 nil === TEST 15: ambiguous boundary patterns (--abc) +--- no_http2 --- config server_tokens off; location /t { @@ -1027,7 +1041,7 @@ close: 1 nil local reader = sock:receiveuntil("--abc") - for i = 1, 7 do + for i = 1, 6 do local line, err, part = reader(4) if line then ngx.say("read: ", line) @@ -1055,7 +1069,6 @@ read: hell read: o, w read: orld read: -- -read: failed to read a line: nil [nil] failed to read a line: closed [ ] @@ -1067,6 +1080,7 @@ close: 1 nil === TEST 16: ambiguous boundary patterns (--abc), small buffer +--- no_http2 --- config server_tokens off; location /t { @@ -1104,7 +1118,7 @@ close: 1 nil local reader = sock:receiveuntil("--abc") - for i = 1, 7 do + for i = 1, 6 do local line, err, part = reader(4) if line then ngx.say("read: ", line) @@ -1132,7 +1146,6 @@ read: hell read: o, w read: orld read: -- -read: failed to read a line: nil [nil] failed to read a line: closed [ ] @@ -1144,6 +1157,7 @@ close: 1 nil === TEST 17: ambiguous boundary patterns (--abc), small buffer, mixed by other reading calls +--- no_http2 --- config server_tokens off; location /t { @@ -1232,6 +1246,7 @@ close: 1 nil === TEST 18: ambiguous boundary patterns (abcabd), small buffer +--- no_http2 --- config server_tokens off; lua_socket_buffer_size 3; @@ -1329,3 +1344,671 @@ this exposed a memory leak in receiveuntil ok --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} + + + +=== TEST 20: add pending bytes +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + lua_socket_buffer_size 1; + + content_by_lua ' + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = ngx.var.port + + 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) + + local req = "GET /foo HTTP/1.0\\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 + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\\r\\n\\r\\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc") + + for i = 1, 4 do + local line, err, part = reader(2) + if line then + ngx.say("read: ", line) + + else + ngx.say("failed to read a line: ", err, " [", part, "]") + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + '; + } + + location /foo { + echo -- -----abc; + more_clear_headers Date; + } +--- request +GET /t + +--- response_body eval +qq{connected: 1 +request sent: 57 +read: -- +read: - +failed to read a line: nil [nil] +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 21: ambiguous boundary patterns (--abc), mixed by other reading calls consume boundary +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = ngx.var.port + + 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) + + local req = "GET /foo HTTP/1.0\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 + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc") + + for i = 1, 5 do + local line, err, part = reader(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + break + + else + ngx.say("read: ", line) + end + + local data, err, part = sock:receive(1) + if not data then + ngx.say("failed to read a byte: ", err, " [", part, "]") + break + + else + ngx.say("read one byte: ", data) + end + end + + local line, err, part = reader(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + echo -- ----abc----abc-; + more_clear_headers Date; + } +--- request +GET /t + +--- response_body eval +qq{connected: 1 +request sent: 57 +read: -- +read one byte: - +read: -a +read one byte: b +read: c- +read one byte: - +read: +read one byte: - +failed to read a line: nil [nil] +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 22: ambiguous boundary patterns (--abc), mixed by other reading calls (including receiveuntil) consume boundary +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = ngx.var.port + + 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) + + local req = "GET /foo HTTP/1.0\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 + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader1 = sock:receiveuntil("--abc") + local reader2 = sock:receiveuntil("-ab") + + local line, err, part = reader1(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local data, err, part = sock:receive(1) + if not data then + ngx.say("failed to read a byte: ", err, " [", part, "]") + + else + ngx.say("read one byte: ", data) + end + + local line, err, part = reader1(1) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local line, err, part = reader2(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local line, err, part = reader1() + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local line, err, part = reader1() + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + echo -- ------abd----abc; + more_clear_headers Date; + } +--- request +GET /t + +--- response_body eval +qq{connected: 1 +request sent: 57 +read: -- +read one byte: - +read: - +read: - +read: d-- +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 23: ambiguous boundary patterns (--abc), mixed by other reading calls consume boundary, small buffer +--- config + lua_socket_buffer_size 3; + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = ngx.var.port + + 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) + + local req = "GET /foo HTTP/1.0\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 + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader = sock:receiveuntil("--abc") + + for i = 1, 5 do + local line, err, part = reader(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + break + + else + ngx.say("read: ", line) + end + + local data, err, part = sock:receive(1) + if not data then + ngx.say("failed to read a byte: ", err, " [", part, "]") + break + + else + ngx.say("read one byte: ", data) + end + end + + local line, err, part = reader(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + echo -- ----abc----abc-; + more_clear_headers Date; + } +--- request +GET /t + +--- response_body eval +qq{connected: 1 +request sent: 57 +read: -- +read one byte: - +read: -a +read one byte: b +read: c- +read one byte: - +read: +read one byte: - +failed to read a line: nil [nil] +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 24: ambiguous boundary patterns (--abc), mixed by other reading calls (including receiveuntil) consume boundary, small buffer +--- config + lua_socket_buffer_size 3; + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = ngx.var.port + + 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) + + local req = "GET /foo HTTP/1.0\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 + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + local reader1 = sock:receiveuntil("--abc") + local reader2 = sock:receiveuntil("-ab") + + local line, err, part = reader1(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local data, err, part = sock:receive(1) + if not data then + ngx.say("failed to read a byte: ", err, " [", part, "]") + + else + ngx.say("read one byte: ", data) + end + + local line, err, part = reader1(1) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local line, err, part = reader2(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local line, err, part = reader1() + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + local line, err, part = reader1() + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + echo -- ------abd----abc; + more_clear_headers Date; + } +--- request +GET /t + +--- response_body eval +qq{connected: 1 +request sent: 57 +read: -- +read one byte: - +read: - +read: - +read: d-- +failed to read a line: closed [ +] +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 25: ambiguous boundary patterns (ab1ab2), ends half way +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = ngx.var.port + + 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) + + local req = "GET /foo HTTP/1.0\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 + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + if true then + local reader = sock:receiveuntil("ab1ab2") + + local line, err, part = reader(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + end + + collectgarbage("collect") + + local data, err, part = sock:receive(3) + if not data then + ngx.say("failed to read three bytes: ", err, " [", part, "]") + + else + ngx.say("read three bytes: ", data) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + echo -- ab1ab1; + more_clear_headers Date; + } +--- request +GET /t + +--- response_body eval +qq{connected: 1 +request sent: 57 +read: ab1 +read three bytes: ab1 +close: 1 nil +} +--- no_error_log +[error] + + + +=== TEST 26: ambiguous boundary patterns (ab1ab2), ends half way, small buffer +--- config + lua_socket_buffer_size 3; + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + -- collectgarbage("collect") + + local sock = ngx.socket.tcp() + local port = ngx.var.port + + 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) + + local req = "GET /foo HTTP/1.0\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 + ngx.say("request sent: ", bytes) + + local read_headers = sock:receiveuntil("\r\n\r\n") + local headers, err, part = read_headers() + if not headers then + ngx.say("failed to read headers: ", err, " [", part, "]") + end + + if true then + local reader = sock:receiveuntil("ab1ab2") + + local line, err, part = reader(2) + if not line then + ngx.say("failed to read a line: ", err, " [", part, "]") + + else + ngx.say("read: ", line) + end + end + + collectgarbage("collect") + + local data, err, part = sock:receive(3) + if not data then + ngx.say("failed to read three bytes: ", err, " [", part, "]") + + else + ngx.say("read three bytes: ", data) + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + echo -- ab1ab1; + more_clear_headers Date; + } +--- request +GET /t + +--- response_body eval +qq{connected: 1 +request sent: 57 +read: ab1 +read three bytes: ab1 +close: 1 nil +} +--- no_error_log +[error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/067-req-socket.t b/t/067-req-socket.t index c5b2631e5e..ae6bc3f3b6 100644 --- a/t/067-req-socket.t +++ b/t/067-req-socket.t @@ -1,6 +1,16 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support ngx.req.socket"; + } elsif ($ENV{TEST_NGINX_USE_HTTP2}) { + $SkipReason = "http2 does not support ngx.req.socket"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); repeat_each(2); @@ -519,6 +529,7 @@ Expect: 100-Continue \breceived: hello\b.*?\breceived: worl\b --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -1166,3 +1177,32 @@ GET /t received: received: abc --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "") + + + +=== TEST 19: getfd +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; +--- config + location /t { + content_by_lua_block { + local sock, err = ngx.req.socket() + if sock then + ngx.say("got the request socket") + else + ngx.say("failed to get the request socket: ", err) + end + + ngx.say(sock:getfd()) + } + } +--- request +POST /t +hello world +--- response_body eval +qr/\Agot the request socket +\d+ +\z/ms +--- no_error_log +[error] diff --git a/t/068-socket-keepalive.t b/t/068-socket-keepalive.t index d67da3bd1e..423d391799 100644 --- a/t/068-socket-keepalive.t +++ b/t/068-socket-keepalive.t @@ -168,6 +168,7 @@ received: OK === TEST 3: upstream sockets close prematurely +--- no_http3 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -243,6 +244,7 @@ done === TEST 4: http keepalive +--- quic_max_idle_timeout: 1.2 --- http_config eval "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" --- config @@ -318,6 +320,7 @@ done === TEST 5: lua_socket_keepalive_timeout +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -395,6 +398,7 @@ qr/lua tcp socket connection pool size: 30\b/] === TEST 6: lua_socket_pool_size +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -473,6 +477,7 @@ qr/lua tcp socket connection pool size: 1\b/] === TEST 7: "lua_socket_keepalive_timeout 0" means unlimited +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -548,6 +553,7 @@ qr/lua tcp socket connection pool size: 30\b/] === TEST 8: setkeepalive(timeout) overrides lua_socket_keepalive_timeout +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -625,6 +631,7 @@ qr/lua tcp socket connection pool size: 30\b/] === TEST 9: sock:setkeepalive(timeout, size) overrides lua_socket_pool_size +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -731,6 +738,7 @@ bad argument #3 to '?' (bad "pool_size" option value: 0) === TEST 11: sock:keepalive_timeout(0) means unlimited +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -887,7 +895,7 @@ received response of 119 bytes -=== TEST 13: github issue #108: ngx.locaiton.capture + redis.set_keepalive +=== TEST 13: github issue #108: ngx.location.capture + redis.set_keepalive --- http_config eval qq{ lua_package_path "$::HtmlDir/?.lua;;"; @@ -1376,6 +1384,7 @@ bad argument #3 to 'connect' (bad "pool" option type: boolean) === TEST 23: clear the redis store +--- no_http2 --- config location /t { redis2_query flushall; @@ -1508,6 +1517,7 @@ done === TEST 25: setkeepalive() with explicit nil args +--- quic_max_idle_timeout: 1.2 --- config server_tokens off; location /t { @@ -1919,6 +1929,7 @@ too many waiting connect operations === TEST 32: conn queuing: once 'pool_size' is reached and pool has 'backlog' +--- quic_max_idle_timeout: 1.2 --- config location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -2536,6 +2547,8 @@ GET /t --- abort --- no_error_log [error] +--- curl_error eval +qr/curl: \(28\) Operation timed out after \d+ milliseconds with 0 bytes received/ @@ -2643,6 +2656,7 @@ lua tcp socket connect timed out, when connecting to === TEST 46: conn queuing: resume connect operation if resumed connect failed (could not be resolved) +--- quic_max_idle_timeout: 1.2 --- config resolver 127.0.0.2:12345 ipv6=off; resolver_timeout 1s; @@ -3016,3 +3030,169 @@ 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" + + + +=== TEST 54: wrong first argument for setkeepalive +--- no_http2 +--- quic_max_idle_timeout: 1.2 +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config + server_tokens off; + location /t { + keepalive_timeout 60s; + + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local port = ngx.var.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 + + 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") + + ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + + ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ok, err = sock:setkeepalive("not a number", "not a number") + if not ok then + ngx.say("failed to set reusable: ", err) + end + } + } + + location /foo { + echo foo; + } + + location /sleep { + echo_sleep 1; + } +--- request +GET /t +--- error_code: +--- response_body +--- error_log eval +qr/\Qbad argument #1 to 'setkeepalive' (number expected, got string)\E/ +--- no_error_log +[crit] +--- timeout: 4 +--- curl_error eval +qr{HTTP/3 stream 0 reset by server} + + + +=== TEST 55: wrong second argument for setkeepalive +--- no_http2 +--- quic_max_idle_timeout: 1.2 +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config + server_tokens off; + location /t { + keepalive_timeout 60s; + + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local port = ngx.var.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 + + 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") + + ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + + ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ok, err = sock:setkeepalive(10, "not a number") + if not ok then + ngx.say("failed to set reusable: ", err) + end + } + } + + location /foo { + echo foo; + } + + location /sleep { + echo_sleep 1; + } +--- request +GET /t +--- error_code: +--- response_body +--- error_log eval +qr/\Qbad argument #2 to 'setkeepalive' (number expected, got string)\E/ +--- no_error_log +[crit] +--- timeout: 4 +--- curl_error eval +qr{HTTP/3 stream 0 reset by server} diff --git a/t/071-idle-socket.t b/t/071-idle-socket.t index c9002be8ea..46ad84f317 100644 --- a/t/071-idle-socket.t +++ b/t/071-idle-socket.t @@ -367,7 +367,7 @@ Transfer-Encoding: chunked\r Connection: close\r \r 6\r -failed to set keepalive: (?:unread data in buffer|connection in dubious state) +failed to set keepalive: (?:unread data in buffer|closed|connection in dubious state) } --- no_error_log [error] @@ -431,3 +431,4 @@ failed to set keepalive: unread data in buffer } --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "") diff --git a/t/072-conditional-get.t b/t/072-conditional-get.t index 7cf2dcd22d..d3400c613a 100644 --- a/t/072-conditional-get.t +++ b/t/072-conditional-get.t @@ -88,3 +88,4 @@ delete thread 1 say failed: nginx output filter error --- no_error_log [error] +--- skip_eval: 5:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) diff --git a/t/075-logby.t b/t/075-logby.t index 8eece698ec..3e9743584a 100644 --- a/t/075-logby.t +++ b/t/075-logby.t @@ -444,6 +444,7 @@ GET /lua ok --- error_log API disabled in the context of log_by_lua* +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -457,8 +458,16 @@ API disabled in the context of log_by_lua* GET /lua --- response_body ok ---- error_log -API disabled in the context of log_by_lua* +--- error_log eval +my $err_log; + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $err_log = "http v3 not supported yet"; +} else { + $err_log = "API disabled in the context of log_by_lua*"; +} + +$err_log; diff --git a/t/077-sleep.t b/t/077-sleep.t index 96f04bd98e..4867b9ca7f 100644 --- a/t/077-sleep.t +++ b/t/077-sleep.t @@ -105,6 +105,7 @@ bad argument #1 to 'sleep' === TEST 5: sleep 0.33 - multi-times in content +--- quic_max_idle_timeout: 1.1 --- config location /test { content_by_lua ' @@ -129,6 +130,7 @@ lua sleep timer expired: "/test?" === TEST 6: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep +--- quic_max_idle_timeout: 2.2 --- config location /test { content_by_lua ' @@ -156,6 +158,7 @@ lua sleep timer expired: "/test?" === TEST 7: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep +--- quic_max_idle_timeout: 0.9 --- config location /test { content_by_lua ' diff --git a/t/082-body-filter-2.t b/t/082-body-filter-2.t new file mode 100644 index 0000000000..3c9b9797d5 --- /dev/null +++ b/t/082-body-filter-2.t @@ -0,0 +1,271 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_EVENT_TYPE} && $ENV{TEST_NGINX_EVENT_TYPE} ne 'poll') { + $SkipReason = "unavailable for the event type '$ENV{TEST_NGINX_EVENT_TYPE}'"; + + } elsif ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support mockeagain"; + + } elsif ($ENV{TEST_NGINX_USE_HTTP2}) { + $SkipReason = "http2 does not support mockeagain"; + + } else { + if ($ENV{LD_PRELOAD} && $ENV{LD_PRELOAD} =~ /\bmockeagain\.so\b/) { + $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1; + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + $ENV{MOCKEAGAIN}='w' + } else { + $SkipReason = "'mockeagain.so' does not appear to be preloaded " + . "with 'LD_PRELOAD'"; + } + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +log_level('debug'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 5); + +#no_diff(); +no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: check ctx->busy_bufs +--- config + location /t { + postpone_output 1; + content_by_lua_block { + for i = 1, 5 do + ngx.say(i, ": Hello World!") + end + } + + body_filter_by_lua_block { + ngx.arg[1] = ngx.arg[1] + } + } +--- request +GET /t +--- response_body +1: Hello World! +2: Hello World! +3: Hello World! +4: Hello World! +5: Hello World! + +--- error_log +waiting body filter busy buffer to be sent +lua say response has busy bufs +--- no_error_log +[error] + + + +=== TEST 2: arg[1] not change and say long string +--- config + location /t { + header_filter_by_lua_block {ngx.header.content_length = nil} + body_filter_by_lua_block { + local function anyting_not_change_arg1() + return + end + anyting_not_change_arg1() + } + content_by_lua_block { + for i = 1, 100 do + ngx.say("12345678901234567890") + end + } + } +--- request +GET /t +--- response_body eval +("12345678901234567890\n" x 100) +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 3: arg[1] not change and chunked_transfer_encoding off +--- config + location /t { + header_filter_by_lua_block {ngx.header.content_length = nil} + body_filter_by_lua_block { + local function anyting_not_change_arg1() + return + end + anyting_not_change_arg1() + } + chunked_transfer_encoding off; + content_by_lua_block { + for i = 1, 100 do + ngx.say("12345678901234567890123456789012345678901234567890".."_"..tostring(i/3)) + end + } + } +--- request +GET /t +--- response_body +12345678901234567890123456789012345678901234567890_0.33333333333333 +12345678901234567890123456789012345678901234567890_0.66666666666667 +12345678901234567890123456789012345678901234567890_1 +12345678901234567890123456789012345678901234567890_1.3333333333333 +12345678901234567890123456789012345678901234567890_1.6666666666667 +12345678901234567890123456789012345678901234567890_2 +12345678901234567890123456789012345678901234567890_2.3333333333333 +12345678901234567890123456789012345678901234567890_2.6666666666667 +12345678901234567890123456789012345678901234567890_3 +12345678901234567890123456789012345678901234567890_3.3333333333333 +12345678901234567890123456789012345678901234567890_3.6666666666667 +12345678901234567890123456789012345678901234567890_4 +12345678901234567890123456789012345678901234567890_4.3333333333333 +12345678901234567890123456789012345678901234567890_4.6666666666667 +12345678901234567890123456789012345678901234567890_5 +12345678901234567890123456789012345678901234567890_5.3333333333333 +12345678901234567890123456789012345678901234567890_5.6666666666667 +12345678901234567890123456789012345678901234567890_6 +12345678901234567890123456789012345678901234567890_6.3333333333333 +12345678901234567890123456789012345678901234567890_6.6666666666667 +12345678901234567890123456789012345678901234567890_7 +12345678901234567890123456789012345678901234567890_7.3333333333333 +12345678901234567890123456789012345678901234567890_7.6666666666667 +12345678901234567890123456789012345678901234567890_8 +12345678901234567890123456789012345678901234567890_8.3333333333333 +12345678901234567890123456789012345678901234567890_8.6666666666667 +12345678901234567890123456789012345678901234567890_9 +12345678901234567890123456789012345678901234567890_9.3333333333333 +12345678901234567890123456789012345678901234567890_9.6666666666667 +12345678901234567890123456789012345678901234567890_10 +12345678901234567890123456789012345678901234567890_10.333333333333 +12345678901234567890123456789012345678901234567890_10.666666666667 +12345678901234567890123456789012345678901234567890_11 +12345678901234567890123456789012345678901234567890_11.333333333333 +12345678901234567890123456789012345678901234567890_11.666666666667 +12345678901234567890123456789012345678901234567890_12 +12345678901234567890123456789012345678901234567890_12.333333333333 +12345678901234567890123456789012345678901234567890_12.666666666667 +12345678901234567890123456789012345678901234567890_13 +12345678901234567890123456789012345678901234567890_13.333333333333 +12345678901234567890123456789012345678901234567890_13.666666666667 +12345678901234567890123456789012345678901234567890_14 +12345678901234567890123456789012345678901234567890_14.333333333333 +12345678901234567890123456789012345678901234567890_14.666666666667 +12345678901234567890123456789012345678901234567890_15 +12345678901234567890123456789012345678901234567890_15.333333333333 +12345678901234567890123456789012345678901234567890_15.666666666667 +12345678901234567890123456789012345678901234567890_16 +12345678901234567890123456789012345678901234567890_16.333333333333 +12345678901234567890123456789012345678901234567890_16.666666666667 +12345678901234567890123456789012345678901234567890_17 +12345678901234567890123456789012345678901234567890_17.333333333333 +12345678901234567890123456789012345678901234567890_17.666666666667 +12345678901234567890123456789012345678901234567890_18 +12345678901234567890123456789012345678901234567890_18.333333333333 +12345678901234567890123456789012345678901234567890_18.666666666667 +12345678901234567890123456789012345678901234567890_19 +12345678901234567890123456789012345678901234567890_19.333333333333 +12345678901234567890123456789012345678901234567890_19.666666666667 +12345678901234567890123456789012345678901234567890_20 +12345678901234567890123456789012345678901234567890_20.333333333333 +12345678901234567890123456789012345678901234567890_20.666666666667 +12345678901234567890123456789012345678901234567890_21 +12345678901234567890123456789012345678901234567890_21.333333333333 +12345678901234567890123456789012345678901234567890_21.666666666667 +12345678901234567890123456789012345678901234567890_22 +12345678901234567890123456789012345678901234567890_22.333333333333 +12345678901234567890123456789012345678901234567890_22.666666666667 +12345678901234567890123456789012345678901234567890_23 +12345678901234567890123456789012345678901234567890_23.333333333333 +12345678901234567890123456789012345678901234567890_23.666666666667 +12345678901234567890123456789012345678901234567890_24 +12345678901234567890123456789012345678901234567890_24.333333333333 +12345678901234567890123456789012345678901234567890_24.666666666667 +12345678901234567890123456789012345678901234567890_25 +12345678901234567890123456789012345678901234567890_25.333333333333 +12345678901234567890123456789012345678901234567890_25.666666666667 +12345678901234567890123456789012345678901234567890_26 +12345678901234567890123456789012345678901234567890_26.333333333333 +12345678901234567890123456789012345678901234567890_26.666666666667 +12345678901234567890123456789012345678901234567890_27 +12345678901234567890123456789012345678901234567890_27.333333333333 +12345678901234567890123456789012345678901234567890_27.666666666667 +12345678901234567890123456789012345678901234567890_28 +12345678901234567890123456789012345678901234567890_28.333333333333 +12345678901234567890123456789012345678901234567890_28.666666666667 +12345678901234567890123456789012345678901234567890_29 +12345678901234567890123456789012345678901234567890_29.333333333333 +12345678901234567890123456789012345678901234567890_29.666666666667 +12345678901234567890123456789012345678901234567890_30 +12345678901234567890123456789012345678901234567890_30.333333333333 +12345678901234567890123456789012345678901234567890_30.666666666667 +12345678901234567890123456789012345678901234567890_31 +12345678901234567890123456789012345678901234567890_31.333333333333 +12345678901234567890123456789012345678901234567890_31.666666666667 +12345678901234567890123456789012345678901234567890_32 +12345678901234567890123456789012345678901234567890_32.333333333333 +12345678901234567890123456789012345678901234567890_32.666666666667 +12345678901234567890123456789012345678901234567890_33 +12345678901234567890123456789012345678901234567890_33.333333333333 +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 4: set resp body nil with ngx.arg[1] first +--- config + location /t { + content_by_lua_block { + ngx.say("Hello World!") + } + + body_filter_by_lua_block { + ngx.arg[1] = "" + ngx.arg[2] = true + } + } +--- request +GET /t +--- response_body +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 5: set resp body nil with ngx.arg[2] first +--- config + location /t { + content_by_lua_block { + ngx.say("Hello World!") + } + + body_filter_by_lua_block { + ngx.arg[2] = true + ngx.arg[1] = "" + } + } +--- request +GET /t +--- response_body +--- no_error_log +[error] +[alert] +[crit] diff --git a/t/082-body-filter.t b/t/082-body-filter.t index 5f765fa439..4033bac8ce 100644 --- a/t/082-body-filter.t +++ b/t/082-body-filter.t @@ -317,7 +317,7 @@ hiya globe GET /t --- ignore_response --- error_log -failed to run body_filter_by_lua*: body_filter_by_lua:4: something bad happened! +failed to run body_filter_by_lua*: body_filter_by_lua(nginx.conf:49):4: something bad happened! @@ -482,6 +482,8 @@ stack traceback: in function 'error' in function 'bar' in function 'foo' +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server# @@ -524,6 +526,8 @@ GET /lua?a=1&b=2 --- ignore_response --- error_log eval qr/failed to load external Lua file ".*?test2\.lua": cannot open .*? No such file or directory/ +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server# @@ -838,3 +842,61 @@ GET /lua --- ignore_response --- error_log API disabled in the context of body_filter_by_lua* +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server# + + + +=== TEST 27: syntax error in body_filter_by_lua_block +--- config + location /lua { + + body_filter_by_lua_block { + 'for end'; + } + content_by_lua_block { + ngx.say("Hello world") + } + } +--- request +GET /lua +--- ignore_response +--- error_log +failed to load inlined Lua code: body_filter_by_lua(nginx.conf:41):2: unexpected symbol near ''for end'' +--- no_error_log +no_such_error1 +no_such_error2 +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server# + + + +=== TEST 28: syntax error in second body_by_lua_block +--- config + location /foo { + body_filter_by_lua_block { + 'for end'; + } + content_by_lua_block { + ngx.say("Hello world") + } + } + + location /lua { + body_filter_by_lua_block { + 'for end'; + } + content_by_lua_block { + ngx.say("Hello world") + } + } +--- request +GET /lua +--- ignore_response +--- error_log +failed to load inlined Lua code: body_filter_by_lua(nginx.conf:49):2: unexpected symbol near ''for end'' +--- no_error_log +no_such_error1 +no_such_error2 +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server# diff --git a/t/083-bad-sock-self.t b/t/083-bad-sock-self.t index 7a76752d9f..206de8b990 100644 --- a/t/083-bad-sock-self.t +++ b/t/083-bad-sock-self.t @@ -33,6 +33,7 @@ __DATA__ --- error_code: 500 --- error_log bad argument #1 to 'receive' (table expected, got string) +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -51,6 +52,7 @@ bad argument #1 to 'receive' (table expected, got string) --- error_code: 500 --- error_log bad argument #1 to 'receiveuntil' (table expected, got number) +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/084-inclusive-receiveuntil.t b/t/084-inclusive-receiveuntil.t index 66be893d5c..60f8363f92 100644 --- a/t/084-inclusive-receiveuntil.t +++ b/t/084-inclusive-receiveuntil.t @@ -19,6 +19,7 @@ run_tests(); __DATA__ === TEST 1: ambiguous boundary patterns (abcabd) - inclusive mode +--- no_http2 --- config server_tokens off; location /t { @@ -91,6 +92,7 @@ close: 1 nil === TEST 2: ambiguous boundary patterns (abcabdabcabe 4) - inclusive mode +--- no_http2 --- config server_tokens off; location /t { @@ -162,6 +164,7 @@ close: 1 nil === TEST 3: ambiguous boundary patterns (abcabd) - inclusive mode - small buffers +--- no_http2 --- config server_tokens off; lua_socket_buffer_size 1; @@ -235,6 +238,7 @@ close: 1 nil === TEST 4: inclusive option value nil +--- no_http2 --- config server_tokens off; location /t { @@ -306,6 +310,7 @@ close: 1 nil === TEST 5: inclusive option value false +--- no_http2 --- config server_tokens off; location /t { @@ -377,6 +382,7 @@ close: 1 nil === TEST 6: inclusive option value true (aa) +--- no_http2 --- config server_tokens off; location /t { @@ -448,6 +454,7 @@ close: 1 nil === TEST 7: bad inclusive option value type +--- no_http2 --- config server_tokens off; location /t { @@ -511,10 +518,13 @@ bad "inclusive" option value type: string --- no_error_log [alert] [warn] +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server# === TEST 8: bad option table +--- no_http2 --- config server_tokens off; location /t { @@ -578,10 +588,13 @@ bad "inclusive" option value type: string --- no_error_log [alert] [warn] +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP\/3 stream 0 reset by server# === TEST 9: ambiguous boundary patterns (--abc), small buffer +--- no_http2 --- config server_tokens off; location /t { @@ -619,7 +632,7 @@ bad "inclusive" option value type: string local reader = sock:receiveuntil("--abc", { inclusive = true }) - for i = 1, 7 do + for i = 1, 6 do local line, err, part = reader(4) if line then ngx.say("read: ", line) @@ -646,8 +659,7 @@ request sent: 57 read: hell read: o, w read: orld -read: -- -read: --abc +read: ----abc failed to read a line: nil [nil] failed to read a line: closed [ ] @@ -659,6 +671,7 @@ close: 1 nil === TEST 10: ambiguous boundary patterns (--abc), small buffer, mixed by other reading calls +--- no_http2 --- config server_tokens off; location /t { diff --git a/t/086-init-by.t b/t/086-init-by.t index bea34a461a..04f8160eb1 100644 --- a/t/086-init-by.t +++ b/t/086-init-by.t @@ -321,3 +321,47 @@ INIT 2: foo = 3 failed to init --- error_log [error] + + + +=== TEST 13: syntax error in init_by_lua_block +--- http_config + init_by_lua_block { + ngx.log(ngx.debug, "pass") + error("failed to init" + ngx.log(ngx.debug, "unreachable") + } +--- config + location /lua { + content_by_lua_block { + ngx.say("hello world") + } + } +--- must_die +--- error_log +init_by_lua error: init_by_lua(nginx.conf:25):4: ')' expected (to close '(' at line 3) near 'ngx' +--- no_error_log +no_such_error_log + + + +=== TEST 14: syntax error in init_by_lua_file +--- http_config + init_by_lua_file html/init.lua; +--- config + location /lua { + content_by_lua_block { + ngx.say("hello world") + } + } +--- user_files +>>> init.lua + ngx.log(ngx.debug, "pass") + error("failed to init" + ngx.log(ngx.debug, "unreachable") + +--- must_die +--- error_log eval +qr|init_by_lua_file error: .*?/t/servroot\w*?/html/init.lua:3: '\)' expected \(to close '\(' at line 2\) near 'ngx'| +--- no_error_log +no_such_error_log diff --git a/t/087-udp-socket.t b/t/087-udp-socket.t index fc467d769a..d1bc654365 100644 --- a/t/087-udp-socket.t +++ b/t/087-udp-socket.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); -plan tests => repeat_each() * (3 * blocks() + 15); +plan tests => repeat_each() * (3 * blocks() + 16); our $HtmlDir = html_dir; @@ -69,7 +69,7 @@ GET /t [error] --- log_level: debug --- error_log -lua udp socket receive buffer size: 8192 +lua udp socket receive buffer size: 65536 @@ -143,12 +143,15 @@ GET /t === TEST 3: access a TCP interface +test-nginx use the same port for tcp(http) and udp(http3) +so need to change to a port that is not listen by any app. +default port range: +net.ipv4.ip_local_port_range = 32768 60999 +choose a port greater than 61000 should be less race. --- config server_tokens off; location /t { - #set $port 5000; - set $port $TEST_NGINX_SERVER_PORT; - #set $port 1234; + set $port 65432; content_by_lua ' local socket = ngx.socket @@ -595,7 +598,7 @@ received a good response. [error] --- log_level: debug --- error_log -lua udp socket receive buffer size: 8192 +lua udp socket receive buffer size: 65536 --- no_check_leak @@ -662,7 +665,7 @@ received a good response. [error] --- log_level: debug --- error_log -lua udp socket receive buffer size: 8192 +lua udp socket receive buffer size: 65536 --- no_check_leak @@ -1171,7 +1174,7 @@ send: fd:\d+ 15 of 15/ -=== TEST 22: send tables of string framents (with numbers too) +=== TEST 22: send tables of string fragments (with numbers too) the maximum number of significant digits is 14 in lua --- config server_tokens off; @@ -1219,3 +1222,113 @@ qr/send: fd:\d+ 22 of 22 send: fd:\d+ 16 of 16 send: fd:\d+ 22 of 22/ --- log_level: debug + + + +=== TEST 23: udp bind +--- config + server_tokens off; + location /t { + #set $port 5000; + set $port $TEST_NGINX_MEMCACHED_PORT; + #set $port 1234; + + content_by_lua_block { + local socket = ngx.socket + local udp = socket.udp() + + local port = ngx.var.port + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:bind("127.0.0.10") + if not ok then + ngx.say("failed to bind: ", err) + return + end + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected") + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("received ", #data, " bytes: ", data) + } + } +--- request +GET /t +--- response_body eval +"connected\nreceived 12 bytes: \x{00}\x{01}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}OK\x{0d}\x{0a}" +--- no_error_log +[error] +--- log_level: debug +--- error_log +lua udp socket receive buffer size: 65536 + + + +=== TEST 24: udp bind failed +--- config + server_tokens off; + location /t { + #set $port 5000; + set $port $TEST_NGINX_MEMCACHED_PORT; + #set $port 1234; + + content_by_lua_block { + local socket = ngx.socket + local udp = socket.udp() + + local port = ngx.var.port + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:bind("127.0.0.1000") + if not ok then + ngx.say("failed to bind: ", err) + return + end + + local ok, err = udp:setpeername("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected") + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = udp:send(req) + if not ok then + ngx.say("failed to send: ", err) + return + end + + local data, err = udp:receive() + if not data then + ngx.say("failed to receive data: ", err) + return + end + ngx.print("received ", #data, " bytes: ", data) + } + } +--- request +GET /t +--- response_body +failed to bind: bad address +--- no_error_log +[error] +--- log_level: debug diff --git a/t/088-req-method.t b/t/088-req-method.t index 198b3b3492..9e1499fb86 100644 --- a/t/088-req-method.t +++ b/t/088-req-method.t @@ -164,6 +164,7 @@ PUT === TEST 8: set GET to HEAD +--- no_http2 --- config location /t { rewrite_by_lua ' @@ -209,6 +210,8 @@ main: GET === TEST 10: set HEAD to GET +XXX: does http3 do not support set HEAD to GET?? +--- no_http2 --- config location /t { rewrite_by_lua ' @@ -223,10 +226,13 @@ main: GET method: GET --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} === TEST 11: set GET to WebDAV methods +XXX: does http3 do not support change HEAD method? +--- no_http2 --- config location /t { content_by_lua ' @@ -262,3 +268,4 @@ method: PATCH method: TRACE --- no_error_log [error] +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/089-phase.t b/t/089-phase.t index a7405f2d54..028c400c73 100644 --- a/t/089-phase.t +++ b/t/089-phase.t @@ -8,7 +8,7 @@ log_level('warn'); repeat_each(2); -plan tests => repeat_each() * (blocks() * 2 + 1) + 2; +plan tests => repeat_each() * (blocks() * 2 + 2) + 2; #no_diff(); #no_long_string(); @@ -198,6 +198,26 @@ GET /lua ok --- shutdown_error_log eval [ -qr/exit_worker_by_lua:\d+: exit_worker/, +qr/exit_worker_by_lua\(nginx\.conf:\d+\):\d+: exit_worker/, qr/exiting now$/, ] + + + +=== TEST 12: server_rewrite_by_lua_block in http +--- http_config + server_rewrite_by_lua_block { + ngx.ctx.phase = ngx.get_phase() + } +--- config + location /lua { + content_by_lua_block { + ngx.say(ngx.ctx.phase) + } + } +--- request +GET /lua +--- response_body +server_rewrite +--- no_error_log +[error] diff --git a/t/091-coroutine.t b/t/091-coroutine.t index b9b3d9d38d..16930ed3c0 100644 --- a/t/091-coroutine.t +++ b/t/091-coroutine.t @@ -159,6 +159,8 @@ cc3: 2 === TEST 3: basic coroutine and cosocket +access the public network is unstable, need a bigger timeout +--- quic_max_idle_timeout: 4 --- config resolver $TEST_NGINX_RESOLVER ipv6=off; location /lua { @@ -760,6 +762,8 @@ GET /lua --- ignore_response --- error_log API disabled in the context of header_filter_by_lua* +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -1419,6 +1423,7 @@ GET /t === TEST 35: coroutine.wrap runtime errors do not log errors +--- no_http2 --- config location = /t { content_by_lua_block { @@ -1691,10 +1696,12 @@ GET /t --- error_log eval [ qr/\[notice\] .*? in wrapped coroutine/, - qr/\[error\] .*? failed to run header_filter_by_lua\*: header_filter_by_lua:\d+: header_filter_by_lua:\d+: something went wrong/, + qr/\[error\] .*? failed to run header_filter_by_lua\*: header_filter_by_lua\(nginx.conf:\d+\):\d+: header_filter_by_lua\(nginx.conf:\d+\):\d+: something went wrong/, "stack traceback:", "in function 'co'" ] +--- curl_error eval +qr/curl: \(52\) Empty reply from server|curl: \(92\) HTTP\/2 stream 1 was not closed cleanly|curl: \(95\) HTTP\/3 stream 0 reset by server/ @@ -1713,9 +1720,9 @@ GET /t --- config --- must_die ---- grep_error_log eval: qr/init_by_lua error: .*? something went wrong/ +--- grep_error_log eval: qr/init_by_lua\(nginx.conf:25\).*? something went wrong/ --- grep_error_log_out -init_by_lua error: init_by_lua:7: init_by_lua:4: something went wrong +init_by_lua(nginx.conf:25):7: init_by_lua(nginx.conf:25):4: something went wrong diff --git a/t/093-uthread-spawn.t b/t/093-uthread-spawn.t index ab6db2ff26..99750d3c0b 100644 --- a/t/093-uthread-spawn.t +++ b/t/093-uthread-spawn.t @@ -962,7 +962,7 @@ received: OK content_by_lua ' local function f() local sock = ngx.socket.udp() - local ok, err = sock:setpeername("127.0.0.1", 12345) + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_RAND_PORT_1) local bytes, err = sock:send("blah") if not bytes then ngx.say("failed to send query: ", err) @@ -1002,7 +1002,7 @@ delete thread 2 delete thread 1 )$ ---- udp_listen: 12345 +--- udp_listen: $TEST_NGINX_RAND_PORT_1 --- udp_query: blah --- udp_reply: hello udp --- response_body_like chop @@ -1064,6 +1064,8 @@ body: hello world)$ === TEST 23: simple user thread with ngx.req.socket() +ngx.req.socket() does not support in http3 +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} --- config location /lua { content_by_lua ' diff --git a/t/094-uthread-exit.t b/t/094-uthread-exit.t index 58d8d0b7db..375d8d6023 100644 --- a/t/094-uthread-exit.t +++ b/t/094-uthread-exit.t @@ -8,7 +8,7 @@ our $StapScript = $t::StapThread::StapScript; repeat_each(2); -plan tests => repeat_each() * (blocks() * 4); +plan tests => repeat_each() * (blocks() * 4 - 2); $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; $ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; @@ -966,6 +966,7 @@ hello in thread after --- no_error_log [error] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} @@ -1051,6 +1052,7 @@ hello in thread after --- no_error_log [error] +--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3} @@ -1133,7 +1135,6 @@ free request attempt to abort with pending subrequests --- no_error_log [alert] -[warn] @@ -1311,6 +1312,7 @@ attempt to abort with pending subrequests === TEST 16: exit in entry thread (user thread is still pending on ngx.location.capture_multi), without pending output +--- no_http2 --- config location /lua { client_body_timeout 12000ms; @@ -1399,10 +1401,13 @@ attempt to abort with pending subrequests --- no_error_log [alert] [warn] +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# === TEST 17: exit(444) in user thread (entry thread is still pending on ngx.location.capture), with pending output +--- no_http2 --- config location /lua { client_body_timeout 12000ms; @@ -1482,10 +1487,13 @@ free request [alert] [error] [warn] +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# === TEST 18: exit(408) in user thread (entry thread is still pending on ngx.location.capture), with pending output +--- no_http2 --- config location /lua { client_body_timeout 12000ms; @@ -1565,6 +1573,8 @@ free request [alert] [error] [warn] +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# @@ -1647,4 +1657,6 @@ free request --- no_error_log [alert] [error] -[warn] + +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# diff --git a/t/095-uthread-exec.t b/t/095-uthread-exec.t index 7bf37c0e8d..9ef6356090 100644 --- a/t/095-uthread-exec.t +++ b/t/095-uthread-exec.t @@ -344,6 +344,7 @@ attempt to abort with pending subrequests === TEST 6: exec in entry thread (user thread is still pending on ngx.location.capture), without pending output +--- no_http2 --- config location /lua { client_body_timeout 12000ms; @@ -423,3 +424,5 @@ attempt to abort with pending subrequests --- no_error_log [alert] [warn] +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# diff --git a/t/096-uthread-redirect.t b/t/096-uthread-redirect.t index 95bff01f3a..5df5ecfb09 100644 --- a/t/096-uthread-redirect.t +++ b/t/096-uthread-redirect.t @@ -190,6 +190,7 @@ free request === TEST 3: ngx.redirect() in entry thread (user thread is still pending on ngx.location.capture_multi), without pending output +--- no_http2 --- config location /lua { client_body_timeout 12000ms; @@ -277,3 +278,5 @@ attempt to abort with pending subrequests --- no_error_log [alert] [warn] +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# diff --git a/t/098-uthread-wait.t b/t/098-uthread-wait.t index c2819ae886..e2818e008e 100644 --- a/t/098-uthread-wait.t +++ b/t/098-uthread-wait.t @@ -1321,3 +1321,22 @@ $s; --- no_error_log [error] [alert] + + + +=== TEST 23: no parameters for ngx.thread.wait +--- config + location /lua { + content_by_lua_block { + ngx.thread.wait() + ngx.say("ok") + } + } +--- request +GET /lua +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log +at least one coroutine should be specified +--- no_error_log +[crit] diff --git a/t/100-client-abort.t b/t/100-client-abort.t index f5713874e7..f1f6894beb 100644 --- a/t/100-client-abort.t +++ b/t/100-client-abort.t @@ -1,6 +1,14 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; +BEGIN { + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "client abort detect does not support in http3"; + } elsif ($ENV{TEST_NGINX_USE_HTTP2}) { + $SkipReason = "client abort detect does not support in http2"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); use t::StapThread; our $GCScript = <<_EOC_; @@ -947,6 +955,7 @@ GET /t [alert] --- error_log say failed: nginx output filter error +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -975,6 +984,7 @@ GET /t [alert] --- error_log print failed: nginx output filter error +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -1034,6 +1044,7 @@ GET /t [alert] --- error_log flush succeeded +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) @@ -1064,3 +1075,4 @@ GET /t eof succeeded --- error_log eof failed: nginx output filter error +--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/) diff --git a/t/101-on-abort.t b/t/101-on-abort.t index 182d12c546..784f244e04 100644 --- a/t/101-on-abort.t +++ b/t/101-on-abort.t @@ -1,6 +1,14 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; +BEGIN { + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "client abort detect does not support in http3"; + } elsif ($ENV{TEST_NGINX_USE_HTTP2}) { + $SkipReason = "client abort detect does not support in http2"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); use t::StapThread; our $GCScript = <<_EOC_; @@ -408,7 +416,7 @@ main handler done -=== TEST 9: regsiter on_abort callback but no client abortion +=== TEST 9: register on_abort callback but no client abortion --- config location /t { lua_check_client_abort on; @@ -546,7 +554,7 @@ on abort called -=== TEST 12: regsiter on_abort callback but no client abortion (uthread) +=== TEST 12: register on_abort callback but no client abortion (uthread) --- config location /t { lua_check_client_abort on; @@ -591,7 +599,7 @@ main handler done -=== TEST 13: regsiter on_abort callback multiple times +=== TEST 13: register on_abort callback multiple times --- config location /t { lua_check_client_abort on; @@ -794,7 +802,7 @@ on abort called -=== TEST 18: regsiter on_abort callback but no client abortion (2 uthreads and 1 pending) +=== TEST 18: register on_abort callback but no client abortion (2 uthreads and 1 pending) --- config location /t { lua_check_client_abort on; diff --git a/t/102-req-start-time.t b/t/102-req-start-time.t index 3b041afda7..d54b19941d 100644 --- a/t/102-req-start-time.t +++ b/t/102-req-start-time.t @@ -59,7 +59,7 @@ GET /start --- request GET /req_time --- response_body_like chop -^(?:0\.[12]|0\.099)\d* +^(?:0\.[12]|0\.099|0\.098)\d* true$ --- no_error_log [error] @@ -88,7 +88,7 @@ true$ --- request GET /req_time --- response_body_like chomp -^(?:0\.[12]|0\.099)\d* +^(?:0\.[12]|0\.099|0\.098)\d* 0\.\d+ true$ --- no_error_log diff --git a/t/103-req-http-ver.t b/t/103-req-http-ver.t index e6de2388c5..73ecccae28 100644 --- a/t/103-req-http-ver.t +++ b/t/103-req-http-ver.t @@ -26,8 +26,17 @@ __DATA__ } --- request GET /t ---- response_body -1.1 +--- response_body eval +my $body; +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + $body="3\n"; +} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) { + $body="2\n"; +} else { + $body="1.1\n"; +} + +$body; --- no_error_log [error] diff --git a/t/104-req-raw-header.t b/t/104-req-raw-header.t index ac1f89e1b4..459d190ac7 100644 --- a/t/104-req-raw-header.t +++ b/t/104-req-raw-header.t @@ -1,5 +1,14 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support ngx.req.raw_header()"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); #worker_connections(1014); #master_on(); @@ -331,6 +340,8 @@ Foo: bar baz\r } --- no_error_log [error] +--- skip_nginx +3: >= 1.21.1 @@ -362,6 +373,8 @@ Connection: close\r --- no_error_log [error] --- timeout: 5 +--- skip_nginx +3: >= 1.21.1 @@ -988,3 +1001,61 @@ Connection: close --- no_error_log [error] --- timeout: 5 + + + +=== TEST 34: multi-line header is invalid (nginx >= 1.21.1) +--- config + location /t { + content_by_lua ' + ngx.print(ngx.req.raw_header()) + '; + } +--- raw_request eval +"GET /t HTTP/1.1\r +Host: localhost\r +Connection: close\r +Foo: bar baz\r + blah\r +\r +" +--- error_code: 400 +--- error_log +client sent invalid header line: "\x20..." while reading client request headers +--- no_error_log +[error] +--- skip_nginx +3: < 1.21.1 + + + +=== TEST 35: bugfix: invalid http request +--- log_level: error +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; +--- config + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + sock:send("\n") + sock:close() + + ngx.say("OK") + } + } + + log_by_lua_block { + local h = ngx.req.raw_header() + } + +--- request +GET /t +--- response_body +OK +--- no_error_log +[error] diff --git a/t/105-pressure.t b/t/105-pressure.t index 9d1ba1ff1a..2fc130d09b 100644 --- a/t/105-pressure.t +++ b/t/105-pressure.t @@ -2,7 +2,7 @@ use Test::Nginx::Socket::Lua; -#worker_connections(1014); +worker_connections(1014); #master_on(); #log_level('debug'); @@ -28,6 +28,8 @@ run_tests(); __DATA__ === TEST 1: memory issue in the "args" string option for ngx.location.capture +the default worker_connections is 64, HTTP3 will keep the connection when curl +request finished. So need to change the worker_connection. --- config location /test1 { content_by_lua ' diff --git a/t/106-timer.t b/t/106-timer.t index 2996762ad4..513e1e5b45 100644 --- a/t/106-timer.t +++ b/t/106-timer.t @@ -118,7 +118,7 @@ F(ngx_http_lua_timer_handler) { registered timer foo = 3 ---- wait: 0.1 +--- wait: 0.2 --- no_error_log [error] [alert] @@ -228,6 +228,7 @@ qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.(?:1[4-9]|2[0-6]?) === TEST 5: tcp cosocket in timer handler (short connections) +--- no_http2 --- config server_tokens off; @@ -603,7 +604,7 @@ delete thread 2 --- response_body hello world ---- wait: 0.12 +--- wait: 0.3 --- no_error_log [error] [alert] @@ -613,7 +614,7 @@ hello world [ "registered timer", qr/\[lua\] .*? my lua timer handler/, -qr/\[lua\] log_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:6[4-9]|7[0-6])/, +qr/\[lua\] log_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.0(?:6[4-9]|7[0-9]|8[1-3])/, "lua ngx.timer expired", "http lua close fake http connection" ] @@ -1332,6 +1333,7 @@ API disabled === TEST 19: exit in user thread (entry thread is still pending on ngx.sleep) +--- quic_max_idle_timeout: 1.3 --- config location /t { content_by_lua ' @@ -2208,8 +2210,8 @@ qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: .*?, context: ngx\.ti '; } --- log_level: error ---- error_log_file: syslog:server=127.0.0.1:12345 ---- udp_listen: 12345 +--- error_log_file: syslog:server=127.0.0.1:$TEST_NGINX_RAND_PORT_1 +--- udp_listen: $TEST_NGINX_RAND_PORT_1 --- udp_query eval: qr/Bad bad bad/ --- udp_reply: hello --- wait: 0.1 diff --git a/t/108-timer-safe.t b/t/108-timer-safe.t index 387f203c3d..a23634cd3b 100644 --- a/t/108-timer-safe.t +++ b/t/108-timer-safe.t @@ -124,6 +124,7 @@ qr/\[lua\] content_by_lua\(nginx\.conf:\d+\):\d+: elapsed: 0\.(?:6[4-9]|7[0-6])/ === TEST 3: tcp cosocket in timer handler (short connections) +--- no_http2 --- config server_tokens off; location = /t { diff --git a/t/109-timer-hup.t b/t/109-timer-hup.t index 2c197c2826..551dd486c7 100644 --- a/t/109-timer-hup.t +++ b/t/109-timer-hup.t @@ -6,6 +6,10 @@ BEGIN { if ($ENV{TEST_NGINX_CHECK_LEAK}) { $SkipReason = "unavailable for the hup tests"; + } elsif (defined $ENV{TEST_NGINX_USE_HTTP3}) { + #os.execute("kill -HUP " .. pid) + $SkipReason = "send HUP relaod signal by self make two workers with same id"; + } else { $ENV{TEST_NGINX_USE_HUP} = 1; undef $ENV{TEST_NGINX_USE_STAP}; @@ -336,7 +340,7 @@ lua found 100 pending timers lua_shared_dict test_dict 1m; server { - listen 12355; + listen $TEST_NGINX_RAND_PORT_1; location = /foo { echo 'foo'; } @@ -350,7 +354,7 @@ lua found 100 pending timers -- Connect the socket local sock = ngx.socket.tcp() - local ok,err = sock:connect("127.0.0.1", 12355) + local ok,err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.log(ngx.ERR, err) end diff --git a/t/114-config.t b/t/114-config.t index 9d6680dda8..46d98de2fb 100644 --- a/t/114-config.t +++ b/t/114-config.t @@ -33,7 +33,7 @@ GET /t -=== TEST 2: ngx.config.subystem +=== TEST 2: ngx.config.subsystem --- config location /t { content_by_lua ' diff --git a/t/116-raw-req-socket.t b/t/116-raw-req-socket.t index 4dfb92664e..78a0e8d5ce 100644 --- a/t/116-raw-req-socket.t +++ b/t/116-raw-req-socket.t @@ -1,10 +1,18 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support ngx.req.socket(true)"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); repeat_each(2); -plan tests => repeat_each() * 43; +plan tests => repeat_each() * 46; our $HtmlDir = html_dir; @@ -969,3 +977,90 @@ GET /t msg: 1: received: hello --- no_error_log [error] +--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "") + + + +=== TEST 17: getfd() +--- config + server_tokens off; + location = /t { + #set $port 5000; + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local req = "GET /mysock HTTP/1.1\r\nUpgrade: mysock\r\nHost: localhost\r\nConnection: close\r\n\r\nhello" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + local reader = sock:receiveuntil("\r\n\r\n") + local data, err, partial = reader() + if not data then + ngx.say("no response header found") + return + end + + local msg, err = sock:receive() + if not msg then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("msg: ", msg) + + ok, err = sock:close() + if not ok then + ngx.say("failed to close socket: ", err) + return + end + } + } + + location = /mysock { + content_by_lua_block { + ngx.status = 101 + ngx.send_headers() + ngx.flush(true) + ngx.req.read_body() + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local s = sock:getfd() + local data, err = sock:receive(5) + if not data then + ngx.log(ngx.ERR, "server: failed to receive: ", err) + return + end + + local bytes, err = sock:send("fd: " .. tostring(s) .. " 1: received: " .. data .. "\n") + if not bytes then + ngx.log(ngx.ERR, "server: failed to send: ", err) + return + end + } + more_clear_headers Date; + } + +--- request +GET /t +--- response_body eval +qr/\Amsg: fd: \d+ 1: received: hello +/ms +--- no_error_log +[error] diff --git a/t/120-re-find.t b/t/120-re-find.t index f80ca4f57d..43ccece1ab 100644 --- a/t/120-re-find.t +++ b/t/120-re-find.t @@ -354,8 +354,11 @@ matched: he } --- request GET /re ---- response_body -error: pcre_compile() failed: missing ) in "(abc" +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre2_compile() failed: missing closing parenthesis in \"(abc\"\n" +: +"error: pcre_compile() failed: missing ) in \"(abc\"\n" --- no_error_log [error] @@ -562,8 +565,11 @@ matched: hello, 1234 } --- request GET /t ---- response_body_like chop -^error: pcre_exec\(\) failed: -10$ +--- response_body eval +$Test::Nginx::Util::PcreVersion == 2 ? +"error: pcre_exec\(\) failed: -4\n" +: +"error: pcre_exec\(\) failed: -10\n" --- no_error_log [error] @@ -587,6 +593,7 @@ GET /t '; } --- stap +# TODO: PCRE2 use different option values from PCRE probe process("$LIBPCRE_PATH").function("pcre_compile") { printf("compile opts: %x\n", $options) } @@ -645,8 +652,14 @@ end --- request GET /re ---- response_body -error: pcre_exec() failed: -8 +--- response_body eval +# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE, +# but PCRE2 replaces this with pcre2_set_match_limit interface, +# which has different effects. +$Test::Nginx::Util::PcreVersion == 2 ? +"failed to match.\n" +: +"error: pcre_exec() failed: -8\n" --- no_error_log [error] @@ -917,3 +930,35 @@ to: 4 pos: 5 --- no_error_log [error] + + + +=== TEST 32: ignore match limit in DFA mode +--- http_config + lua_regex_match_limit 1; +--- config + location /re { + content_by_lua_block { + local s = "This is no more" + local from, to, err = ngx.re.find(s, "<.*>", "d") + if from then + ngx.say("from: ", from) + ngx.say("to: ", to) + ngx.say("matched: ", string.sub(s, from, to)) + else + if err then + ngx.say("error: ", err) + return + end + ngx.say("not matched!") + end + } + } +--- request + GET /re +--- response_body +from: 9 +to: 56 +matched: +--- no_error_log +[error] diff --git a/t/122-worker-2.t b/t/122-worker-2.t new file mode 100644 index 0000000000..c4ad2aea2a --- /dev/null +++ b/t/122-worker-2.t @@ -0,0 +1,49 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +#worker_connections(1014); +master_on(); +workers(4); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: get worker pids with multiple worker +--- config + location /lua { + content_by_lua_block { + local pids, err = ngx.worker.pids() + if err ~= nil then + return + end + local pid = ngx.worker.pid() + ngx.say("worker pid: ", pid) + local count = ngx.worker.count() + ngx.say("worker count: ", count) + ngx.say("worker pids count: ", #pids) + for i = 1, count do + if pids[i] == pid then + ngx.say("worker pid is correct.") + return + end + end + } + } +--- request +GET /lua +--- response_body_like +worker pid: \d+ +worker count: 4 +worker pids count: 4 +worker pid is correct\. +--- no_error_log +[error] diff --git a/t/122-worker-3.t b/t/122-worker-3.t new file mode 100644 index 0000000000..050486e532 --- /dev/null +++ b/t/122-worker-3.t @@ -0,0 +1,58 @@ +# 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 'no_plan'; + +#worker_connections(1014); +master_on(); +workers(4); +#log_level('warn'); + +repeat_each(2); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: get worker pids with multiple worker +--- config + location /lua { + content_by_lua_block { + local pids, err = ngx.worker.pids() + if err ~= nil then + return + end + local pid = ngx.worker.pid() + ngx.say("worker pid: ", pid) + local count = ngx.worker.count() + ngx.say("worker count: ", count) + ngx.say("worker pids count: ", #pids) + for i = 1, count do + if pids[i] == pid then + ngx.say("worker pid is correct.") + return + end + end + } + } +--- request +GET /lua +--- response_body_like +worker pid: \d+ +worker count: 4 +worker pids count: 4 +worker pid is correct\. +--- no_error_log +[error] diff --git a/t/122-worker.t b/t/122-worker.t index b74c81fbcd..244c3142d3 100644 --- a/t/122-worker.t +++ b/t/122-worker.t @@ -9,7 +9,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); -plan tests => repeat_each() * (blocks() * 3); +plan tests => repeat_each() * (blocks() * 4 - 4); #no_diff(); no_long_string(); @@ -79,3 +79,33 @@ worker pid: \d+ worker pid is correct\. --- no_error_log [error] + + + +=== TEST 4: content_by_lua + ngx.worker.pids +--- config + location /lua { + content_by_lua ' + local pids = ngx.worker.pids() + local pid = ngx.worker.pid() + ngx.say("worker pid: ", pid) + local count = ngx.worker.count() + if count ~= #pids then + ngx.say("worker pids is wrong.") + end + for i = 1, count do + if pids[i] == pid then + ngx.say("worker pid is correct.") + return + end + end + ngx.say("worker pid is wrong.") + '; + } +--- request +GET /lua +--- response_body_like +worker pid: \d+ +worker pid is correct\. +--- no_error_log +[error] diff --git a/t/124-init-worker.t b/t/124-init-worker.t index b7e17a07f1..c68d74bb18 100644 --- a/t/124-init-worker.t +++ b/t/124-init-worker.t @@ -529,6 +529,7 @@ second line received: (?:Date|Server): .*? --- no_error_log [error] --- timeout: 10 +--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3} @@ -729,8 +730,8 @@ ok '; } --- log_level: error ---- error_log_file: syslog:server=127.0.0.1:12345 ---- udp_listen: 12345 +--- error_log_file: syslog:server=127.0.0.1:$TEST_NGINX_RAND_PORT_1 +--- udp_listen: $TEST_NGINX_RAND_PORT_1 --- udp_query eval: qr/Bad bad bad/ --- udp_reply: hello --- wait: 0.1 @@ -842,6 +843,11 @@ lua close the global Lua VM \3 in the cache helper process \d+ lua close the global Lua VM \3 lua close the global Lua VM \3 in the cache helper process \d+ )(?:lua close the global Lua VM [0-9A-F]+ +|lua close the global Lua VM ([0-9A-F]+) +lua close the global Lua VM \4 in the cache helper process \d+ +lua close the global Lua VM \4 in the cache helper process \d+ +lua close the global Lua VM \4 +lua close the global Lua VM \4 )*\z/ --- no_error_log [error] @@ -975,3 +981,53 @@ qr/lua close the global Lua VM ([0-9A-F]+)$/, --- no_error_log [error] start privileged agent process + + + +=== TEST 25: syntax error in init_worker_by_lua_block +--- http_config + init_worker_by_lua_block { + ngx.log(ngx.debug, "pass") + error("failed to init" + ngx.log(ngx.debug, "unreachable") + } +--- config + location /t { + content_by_lua_block { + ngx.say("hello world") + } + } +--- request + GET /t +--- response_body +hello world +--- error_log +init_worker_by_lua error: init_worker_by_lua(nginx.conf:25):4: ')' expected (to close '(' at line 3) near 'ngx' +--- no_error_log +no_such_error_log + + + +=== TEST 26: syntax error in init_worker_by_lua_file +--- http_config + init_worker_by_lua_file html/init.lua; +--- config + location /t { + content_by_lua_block { + ngx.say("hello world") + } + } +--- user_files +>>> init.lua + ngx.log(ngx.debug, "pass") + error("failed to init" + ngx.log(ngx.debug, "unreachable") + +--- request + GET /t +--- response_body +hello world +--- error_log eval +qr|init_worker_by_lua_file error: .*?t/servroot\w*/html/init.lua:3: '\)' expected \(to close '\(' at line 2\) near 'ngx'| +--- no_error_log +no_such_error_log diff --git a/t/127-uthread-kill.t b/t/127-uthread-kill.t index 5542153cf2..11caee582f 100644 --- a/t/127-uthread-kill.t +++ b/t/127-uthread-kill.t @@ -8,7 +8,7 @@ our $StapScript = $t::StapThread::StapScript; repeat_each(2); -plan tests => repeat_each() * (blocks() * 5 + 2); +plan tests => repeat_each() * (blocks() * 5 + 1); $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; $ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211'; @@ -139,6 +139,7 @@ lua clean up the timer for pending ngx.sleep === TEST 3: kill pending resolver --- config resolver 127.0.0.2:12345; + resolver_timeout 5ms; location /lua { content_by_lua ' local function f() @@ -184,7 +185,6 @@ killed [error] --- error_log lua tcp socket abort resolver -resolve name done: -2 diff --git a/t/128-duplex-tcp-socket.t b/t/128-duplex-tcp-socket.t index 22b9f036dd..511cc20bfc 100644 --- a/t/128-duplex-tcp-socket.t +++ b/t/128-duplex-tcp-socket.t @@ -193,7 +193,7 @@ close: 1 nil$ lua_socket_log_errors off; location /t { #set $port 5000; - set $port 7658; + set $port $TEST_NGINX_RAND_PORT_1; content_by_lua ' local sock = ngx.socket.tcp() @@ -258,7 +258,7 @@ received: OK! close: (?:nil socket busy writing|1 nil failed to send request: closed)$ ---- tcp_listen: 7658 +--- tcp_listen: $TEST_NGINX_RAND_PORT_1 --- tcp_shutdown: 0 --- tcp_reply: OK! --- tcp_no_close: 1 @@ -273,7 +273,7 @@ failed to send request: closed)$ lua_socket_log_errors off; location /t { #set $port 5000; - set $port 7658; + set $port $TEST_NGINX_RAND_PORT_1; content_by_lua ' local sock = ngx.socket.tcp() @@ -349,7 +349,7 @@ F(ngx_http_lua_socket_tcp_finalize_write_part) { print_ubacktrace() } --- stap_out2 ---- tcp_listen: 7658 +--- tcp_listen: $TEST_NGINX_RAND_PORT_1 --- tcp_shutdown: 1 --- tcp_query eval: "flush_all\r\n" --- tcp_query_len: 11 diff --git a/t/129-ssl-socket.t b/t/129-ssl-socket.t index 3b9b34c33b..5651f3f817 100644 --- a/t/129-ssl-socket.t +++ b/t/129-ssl-socket.t @@ -1,18 +1,36 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: -use Test::Nginx::Socket::Lua; +our $SkipReason; +BEGIN { + if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + # FIXME: we still need to enable this test file for HTTP3. + $SkipReason = "the test cases are very unstable, skip for now."; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); + use Cwd qw(abs_path realpath); use File::Basename; repeat_each(2); -plan tests => repeat_each() * 211; +sub resolve($$); + +plan tests => repeat_each() * (blocks() * 7 - 4); $ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; $ENV{TEST_NGINX_SERVER_SSL_PORT} ||= 12345; $ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__))); +$ENV{TEST_NGINX_OPENRESTY_ORG_IP} ||= resolve("openresty.org", $ENV{TEST_NGINX_RESOLVER}); + +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; +if ($openssl_version =~ m/BoringSSL/) { + $ENV{TEST_NGINX_USE_BORINGSSL} = 1; +} #log_level 'warn'; log_level 'debug'; @@ -29,6 +47,19 @@ sub read_file { $cert; } +sub resolve ($$) { + my ($domain, $resolver) = @_; + my $ips = qx/dig \@$resolver +short $domain/; + + my $exit_code = $? >> 8; + if (!$ips || $exit_code != 0) { + die "failed to resolve '$domain' using '$resolver' as resolver"; + } + + my ($ip) = split /\n/, $ips; + return $ip; +} + our $DSTRootCertificate = read_file("t/cert/dst-ca.crt"); our $EquifaxRootCertificate = read_file("t/cert/equifax.crt"); our $TestCertificate = read_file("t/cert/test.crt"); @@ -40,6 +71,8 @@ run_tests(); __DATA__ === TEST 1: www.google.com +access the public network is unstable, need a bigger timeout value. +--- quic_max_idle_timeout: 3 --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -48,7 +81,7 @@ __DATA__ set $port $TEST_NGINX_MEMCACHED_PORT; content_by_lua ' - -- avoid flushing google in "check leak" testing mode: + -- avoid flushing bing in "check leak" testing mode: local counter = package.loaded.counter if not counter then counter = 1 @@ -106,7 +139,7 @@ __DATA__ GET /t --- response_body_like chop \Aconnected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 59 bytes. received: HTTP/1.1 (?:200 OK|302 Found) close: 1 nil @@ -190,7 +223,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 53 bytes. received: HTTP/1.1 201 Created close: 1 nil @@ -271,7 +304,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 58 bytes. received: HTTP/1.1 302 Moved Temporarily close: 1 nil @@ -293,6 +326,8 @@ SSL reused session === TEST 4: ssl session reuse +access the public network is unstable, need a bigger timeout value. +--- quic_max_idle_timeout: 3 --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -355,12 +390,12 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 200 OK close: 1 nil connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 200 OK close: 1 nil @@ -603,7 +638,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 80 bytes. received: HTTP/1.1 404 Not Found close: 1 nil @@ -688,7 +723,7 @@ $::DSTRootCertificate" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 58 bytes. received: HTTP/1.1 302 Moved Temporarily close: 1 nil @@ -1008,7 +1043,7 @@ $::DSTRootCertificate" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 58 bytes. received: HTTP/1.1 302 Moved Temporarily close: 1 nil @@ -1034,6 +1069,8 @@ SSL reused session --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; + location /t { #set $port 5000; set $port $TEST_NGINX_MEMCACHED_PORT; @@ -1087,7 +1124,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 58 bytes. received: HTTP/1.1 302 Moved Temporarily close: 1 nil @@ -1118,7 +1155,7 @@ SSL reused session server_name test.com; ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; - ssl_protocols TLSv1; + ssl_protocols TLSv1.2; location / { content_by_lua_block { @@ -1128,7 +1165,7 @@ SSL reused session } --- config server_tokens off; - lua_ssl_ciphers ECDHE-RSA-AES256-SHA; + lua_ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384; location /t { content_by_lua ' @@ -1179,7 +1216,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 53 bytes. received: HTTP/1.1 200 OK close: 1 nil @@ -1192,7 +1229,7 @@ lua ssl free session: ([0-9A-F]+) $/ --- error_log eval ['lua ssl server name: "test.com"', -qr/SSL: TLSv\d(?:\.\d)?, cipher: "ECDHE-RSA-AES256-SHA (SSLv3|TLSv1)/] +qr/SSL: TLSv\d(?:\.\d)?, cipher: "ECDHE-RSA-AES256-GCM-SHA384 (SSLv3|TLSv1\.2)/] --- no_error_log SSL reused session [error] @@ -1208,7 +1245,7 @@ SSL reused session server_name test.com; ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; - ssl_protocols TLSv1; + ssl_protocols TLSv1.2; location / { content_by_lua_block { @@ -1218,7 +1255,7 @@ SSL reused session } --- config server_tokens off; - lua_ssl_protocols TLSv1; + lua_ssl_protocols TLSv1.2; location /t { content_by_lua ' @@ -1269,7 +1306,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 53 bytes. received: HTTP/1.1 200 OK close: 1 nil @@ -1282,7 +1319,7 @@ lua ssl free session: ([0-9A-F]+) $/ --- error_log eval ['lua ssl server name: "test.com"', -qr/SSL: TLSv1, cipher: "ECDHE-RSA-AES256-SHA (SSLv3|TLSv1)/] +qr/SSL: TLSv1\.2, cipher: "ECDHE-RSA-AES256-GCM-SHA384 TLSv1\.2/] --- no_error_log SSL reused session [error] @@ -1376,6 +1413,7 @@ SSL reused session location /t { #set $port 5000; set $port $TEST_NGINX_MEMCACHED_PORT; + set $openresty_org_ip $TEST_NGINX_OPENRESTY_ORG_IP; content_by_lua ' local sock = ngx.socket.tcp() @@ -1385,7 +1423,8 @@ SSL reused session local session for i = 1, 3 do - local ok, err = sock:connect("openresty.org", 443) + -- Use the same IP to ensure that the connection can be reused + local ok, err = sock:connect(ngx.var.openresty_org_ip, 443) if not ok then ngx.say("failed to connect: ", err) return @@ -1418,13 +1457,13 @@ $::DSTRootCertificate" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata set keepalive: 1 nil connected: 1 -ssl handshake: userdata +ssl handshake: cdata set keepalive: 1 nil connected: 1 -ssl handshake: userdata +ssl handshake: cdata set keepalive: 1 nil --- log_level: debug @@ -1445,6 +1484,72 @@ SSL reused session === TEST 18: openresty.org: passing SSL verify: keepalive (no reusing the ssl session) +The session returned by SSL_get1_session maybe different. +After function tls_process_new_session_ticket, the session saved in SSL->session +will be replace by a new one. + +ngx_ssl_session_t * +ngx_ssl_get_session(ngx_connection_t *c) +{ +#ifdef TLS1_3_VERSION + if (c->ssl->session) { + SSL_SESSION_up_ref(c->ssl->session); + return c->ssl->session; + } +#endif + + return SSL_get1_session(c->ssl->connection); +} + +SSL_SESSION *SSL_get1_session(SSL *ssl) +/* variant of SSL_get_session: caller really gets something */ +{ + SSL_SESSION *sess; + /* + * Need to lock this all up rather than just use CRYPTO_add so that + * somebody doesn't free ssl->session between when we check it's non-null + * and when we up the reference count. + */ + CRYPTO_THREAD_read_lock(ssl->lock); + sess = ssl->session; + if (sess) + SSL_SESSION_up_ref(sess); + CRYPTO_THREAD_unlock(ssl->lock); + return sess; +} + +#0 tls_process_new_session_ticket (s=0x7e6ea0, pkt=0x7fffffffc820) at ssl/statem/statem_clnt.c:2650 +#1 0x00007ffff7af50fd in read_state_machine (s=0x7e6ea0) at ssl/statem/statem.c:636 +#2 state_machine (s=0x7e6ea0, server=0) at ssl/statem/statem.c:434 +#3 0x00007ffff7aca6b3 in ssl3_read_bytes (s=, type=23, recvd_type=0x0, buf=0x7fffffffc9d7 "\027\320\355t", len=1, + peek=0, readbytes=0x7fffffffc978) at ssl/record/rec_layer_s3.c:1677 +#4 0x00007ffff7ad2250 in ssl3_read_internal (readbytes=0x7fffffffc978, peek=0, len=1, buf=0x7fffffffc9d7, s=0x7e6ea0) + at ssl/s3_lib.c:4477 +#5 ssl3_read (s=0x7e6ea0, buf=0x7fffffffc9d7, len=1, readbytes=0x7fffffffc978) at ssl/s3_lib.c:4500 +#6 0x00007ffff7ade695 in SSL_read (s=, buf=buf@entry=0x7fffffffc9d7, num=num@entry=1) at ssl/ssl_lib.c:1799 +#7 0x000000000045a965 in ngx_ssl_recv (c=0x72c3b0, buf=0x7fffffffc9d7 "\027\320\355t", size=1) + at src/event/ngx_event_openssl.c:2337 +#8 0x0000000000533b17 in ngx_http_lua_socket_keepalive_close_handler (ev=0x7e2f20) + at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_socket_tcp.c:5753 +#9 0x000000000052cf40 in ngx_http_lua_socket_tcp_setkeepalive (L=0x74edd0) + at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_socket_tcp.c:5602 +#10 0x00007ffff7f0fabe in lj_BC_FUNCC () + from /tmp/undodb.72729.1722915526.2470007.80d50d088e818fd4/debuggee-1-zwqz8svp/symbol-files/opt/luajit-sysm/lib/libluajit-5.1.so.2 +#11 0x000000000051f2b2 in ngx_http_lua_run_thread (L=L@entry=0x767670, r=r@entry=0x7edf80, ctx=ctx@entry=0x750e40, nrets=0) + at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_util.c:1194 +#12 0x0000000000524347 in ngx_http_lua_content_by_chunk (L=0x767670, r=0x7edf80) + at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_contentby.c:124 +#13 0x000000000047c663 in ngx_http_core_content_phase (r=0x7edf80, ph=0x7b4470) at src/http/ngx_http_core_module.c:1271 +#14 0x000000000047b80d in ngx_http_core_run_phases (r=0x7edf80) at src/http/ngx_http_core_module.c:885 +#15 ngx_http_handler (r=r@entry=0x7edf80) at src/http/ngx_http_core_module.c:868 +#16 0x00000000004854ad in ngx_http_process_request (r=r@entry=0x7edf80) at src/http/ngx_http_request.c:2140 +#17 0x00000000004868e8 in ngx_http_process_request_headers (rev=rev@entry=0x7e2f80) at src/http/ngx_http_request.c:1529 +#18 0x0000000000486468 in ngx_http_process_request_line (rev=0x7e2f80) at src/http/ngx_http_request.c:1196 +#19 0x000000000044b338 in ngx_event_process_posted (cycle=cycle@entry=0x721690, posted=0x62f250 ) + at src/event/ngx_event_posted.c:35 +#20 0x000000000044a522 in ngx_process_events_and_timers (cycle=cycle@entry=0x721690) at src/event/ngx_event.c:273 +#21 0x0000000000453819 in ngx_single_process_cycle (cycle=cycle@entry=0x721690) at src/os/unix/ngx_process_cycle.c:323 +#22 0x0000000000429dee in main (argc=argc@entry=5, argv=argv@entry=0x7fffffffd1a8) at src/core/nginx.c:384 --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -1452,6 +1557,7 @@ SSL reused session lua_ssl_verify_depth 2; location /t { #set $port 5000; + set $openresty_org_ip $TEST_NGINX_OPENRESTY_ORG_IP; set $port $TEST_NGINX_MEMCACHED_PORT; content_by_lua ' @@ -1461,7 +1567,8 @@ SSL reused session do for i = 1, 3 do - local ok, err = sock:connect("openresty.org", 443) + -- Use the same IP to ensure that the connection can be reused + local ok, err = sock:connect(ngx.var.openresty_org_ip, 443) if not ok then ngx.say("failed to connect: ", err) return @@ -1494,24 +1601,24 @@ $::DSTRootCertificate" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata set keepalive: 1 nil connected: 1 -ssl handshake: userdata +ssl handshake: cdata set keepalive: 1 nil connected: 1 -ssl handshake: userdata +ssl handshake: cdata set keepalive: 1 nil --- log_level: debug --- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ --- grep_error_log_out eval qr/^lua ssl save session: ([0-9A-F]+) -lua ssl save session: \1 -lua ssl save session: \1 -lua ssl free session: \1 -lua ssl free session: \1 -lua ssl free session: \1 +lua ssl save session: ([0-9A-F]+) +lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) $/ --- error_log @@ -1562,6 +1669,7 @@ attempt to call method 'sslhandshake' (a nil value) --- no_error_log [alert] --- timeout: 3 +--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3} @@ -1637,7 +1745,7 @@ attempt to call method 'sslhandshake' (a nil value) GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -1742,7 +1850,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -1855,7 +1963,7 @@ $::TestCertificate" --- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ --- grep_error_log_out --- error_log eval -qr/SSL_do_handshake\(\) failed .*?(unknown protocol|wrong version number)/ +qr/SSL_do_handshake\(\) failed .*?(unknown protocol|wrong version number|routines::record layer failure)/ --- no_error_log lua ssl server name: SSL reused session @@ -1939,7 +2047,7 @@ SSL reused session --- request GET /t --- response_body eval -# Since nginx version 1.19.1, invalidity date is considerd a non-critical CRL +# Since nginx version 1.19.1, invalidity date is considered a non-critical CRL # entry extension, in other words, revoke still works even if CRL has expired. $Test::Nginx::Util::NginxVersion >= 1.019001 ? @@ -2031,8 +2139,8 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata -ssl handshake: userdata +ssl handshake: cdata +ssl handshake: cdata sent http request: 58 bytes. received: HTTP/1.1 302 Moved Temporarily close: 1 nil @@ -2099,8 +2207,6 @@ failed to do SSL handshake: timeout --- log_level: debug --- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ --- grep_error_log_out ---- error_log -lua ssl server name: "openresty.org" --- no_error_log SSL reused session [error] @@ -2232,7 +2338,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata --- user_files eval ">>> test.key @@ -2405,7 +2511,7 @@ SSL reused session GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -2508,10 +2614,10 @@ SSL reused session --- request GET /t ---- response_body -connected: 1 -failed to do SSL handshake: 18: self signed certificate - +--- response_body eval +qr/connected: 1 +failed to do SSL handshake: 18: self[- ]signed certificate +/ms --- user_files eval ">>> test.key $::TestCertificateKey @@ -2520,8 +2626,8 @@ $::TestCertificate" --- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ --- grep_error_log_out ---- error_log -lua ssl certificate verify error: (18: self signed certificate) +--- error_log eval +qr/lua ssl certificate verify error: \(18: self[- ]signed certificate\)/ --- no_error_log SSL reused session [alert] @@ -2529,7 +2635,7 @@ SSL reused session -=== TEST 31: handshake, too many arguments +=== TEST 31: handshake, too few arguments --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; @@ -2561,3 +2667,382 @@ qr/\[error\] .* ngx.socket sslhandshake: expecting 1 ~ 5 arguments \(including t --- no_error_log [alert] --- timeout: 10 +--- curl_error eval +qr#curl: \(52\) Empty reply from server|curl: \(95\) HTTP/3 stream 0 reset by server# + + + +=== TEST 32: default cipher -TLSv1.3 +--- skip_openssl: 8: < 1.1.1 +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1.3; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- config + server_tokens off; + lua_ssl_protocols TLSv1.3; + + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + } +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 53 bytes. +received: HTTP/1.1 200 OK +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log eval +['lua ssl server name: "test.com"', +qr/SSL: TLSv1.3, cipher: "TLS_AES_256_GCM_SHA384 TLSv1.3/] +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 + + + +=== TEST 33: explicit cipher configuration - TLSv1.3 +--- skip_openssl: 8: < 1.1.1 +--- skip_nginx: 8: < 1.19.4 +--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1.3; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- config + server_tokens off; + lua_ssl_protocols TLSv1.3; + lua_ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256; + + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + } +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 53 bytes. +received: HTTP/1.1 200 OK +close: 1 nil + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- error_log eval +['lua ssl server name: "test.com"', +qr/SSL: TLSv1.3, cipher: "TLS_AES_128_GCM_SHA256 TLSv1.3/] +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 + + + +=== TEST 34: explicit cipher configuration not in the default list - TLSv1.3 +--- skip_openssl: 8: < 1.1.1 +--- skip_nginx: 8: < 1.19.4 +--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1.3; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- config + server_tokens off; + lua_ssl_protocols TLSv1.3; + lua_ssl_conf_command Ciphersuites TLS_AES_128_CCM_SHA256; + + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + } +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- log_level: debug +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out +--- error_log eval +[ +qr/\[info\] .*?SSL_do_handshake\(\) failed .*?no shared cipher/, +'lua ssl server name: "test.com"', +] +--- no_error_log +SSL reused session +[alert] +[emerg] +--- timeout: 10 + + + +=== TEST 35: lua_ssl_key_log directive +--- skip_openssl: 8: < 1.1.1 +--- http_config + server { + listen $TEST_NGINX_SERVER_SSL_PORT ssl; + server_name test.com; + ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; + ssl_protocols TLSv1.3; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- config + server_tokens off; + lua_ssl_protocols TLSv1.3; + lua_ssl_key_log sslkey.log; + + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + do + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_SSL_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local session, err = sock:sslhandshake(nil, "test.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/conf/sslkey.log", "r") + if not f then + ngx.log(ngx.ERR, "failed to open sslkey.log: ", err) + return + end + + local key_log = f:read("*a") + ngx.say(key_log) + f:close() + end -- do + collectgarbage() + } + } +--- request +GET /t +--- response_body_like +connected: 1 +ssl handshake: cdata +sent http request: 53 bytes. +received: HTTP/1.1 200 OK +close: 1 nil +SERVER_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\s]+ +EXPORTER_SECRET [0-9a-z\s]+ +SERVER_TRAFFIC_SECRET_0 [0-9a-z\s]+ +CLIENT_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\s]+ +CLIENT_TRAFFIC_SECRET_0 [0-9a-z\s]+ + +--- log_level: debug +--- error_log eval +['lua ssl server name: "test.com"', +qr/SSL: TLSv1.3, cipher: "TLS_AES_256_GCM_SHA384 TLSv1.3/] +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 diff --git a/t/131-duplex-req-socket.t b/t/131-duplex-req-socket.t index 5d698b9344..f88649b8d8 100644 --- a/t/131-duplex-req-socket.t +++ b/t/131-duplex-req-socket.t @@ -1,5 +1,7 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: +our $SkipReason; + BEGIN { if (!defined $ENV{LD_PRELOAD}) { $ENV{LD_PRELOAD} = ''; @@ -18,9 +20,13 @@ BEGIN { $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'slow'; + + if ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support ngx.req.socket()"; + } } -use Test::Nginx::Socket::Lua; +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); log_level('debug'); diff --git a/t/132-lua-blocks.t b/t/132-lua-blocks.t index 76f4a6184d..35638a8489 100644 --- a/t/132-lua-blocks.t +++ b/t/132-lua-blocks.t @@ -166,6 +166,7 @@ ok === TEST 8: content_by_lua_block (cosockets) +--- no_http2 --- config server_tokens off; location = /t { @@ -312,7 +313,7 @@ hello, world [error] --- must_die --- error_log eval -qr/\[emerg\] .*? Lua code block missing the closing long bracket "]]" in .*?\bnginx\.conf:41/ +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]]", the inlined Lua code may be too long in .*?\bnginx\.conf:\d+/ @@ -331,7 +332,7 @@ hello, world [error] --- must_die --- error_log eval -qr/\[emerg\] .*? Lua code block missing the closing long bracket "]==]" in .*?\bnginx\.conf:41/ +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]==]", the inlined Lua code may be too long in .*?\bnginx.conf:\d+/ @@ -350,7 +351,7 @@ hello, world [error] --- must_die --- error_log eval -qr/\[emerg\] .*? Lua code block missing the closing long bracket "]]" in .*?\bnginx\.conf:41/ +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]]", the inlined Lua code may be too long in .*?\bnginx\.conf:\d+/ @@ -369,7 +370,7 @@ hello, world [error] --- must_die --- error_log eval -qr/\[emerg\] .*? Lua code block missing the closing long bracket "]=]" in .*?\bnginx\.conf:41/ +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]=]", the inlined Lua code may be too long in .*?\bnginx\.conf:\d+/ @@ -606,3 +607,38 @@ GET /t --- response_body_like: } --- no_error_log [error] + + + +=== TEST 24: too long bracket +--- config + location = /t { + content_by_lua_block { + local foo = [[ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ]] + ngx.say(foo) + } + } +--- request +GET /t +--- response_body +hello, world +--- no_error_log +[error] +--- must_die +--- error_log eval +qr/\[emerg\] .*? Lua code block missing the closing long bracket "]]", the inlined Lua code may be too long in .*?\bnginx\.conf:\d+/ diff --git a/t/138-balancer-upstream-bind.t b/t/138-balancer-upstream-bind.t new file mode 100644 index 0000000000..442f37c8f3 --- /dev/null +++ b/t/138-balancer-upstream-bind.t @@ -0,0 +1,142 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: bind to empty +--- no_http2 +--- http_config + lua_package_path "$TEST_NGINX_SERVER_ROOT/html/?.lua;;"; + + upstream backend { + server 127.0.0.1:$TEST_NGINX_SERVER_PORT; + } +--- config + set $proxy_local_addr ""; + proxy_bind $proxy_local_addr; + + location = /t { + proxy_pass http://backend/back; + } + + location = /back { + echo ok; + } + +--- request + GET /t +--- response_body +ok +--- no_error_log +[cirt] + + + +=== TEST 2: bind to 127.0.0.1 +--- no_http2 +--- http_config + lua_package_path "$TEST_NGINX_SERVER_ROOT/html/?.lua;;"; + + upstream backend { + server 127.0.0.1:$TEST_NGINX_SERVER_PORT; + } +--- config + set $proxy_local_addr ""; + proxy_bind $proxy_local_addr; + + location = /t { + access_by_lua_block { + ngx.var.proxy_local_addr="127.0.0.1" + } + proxy_pass http://backend/back; + } + + location = /back { + echo ok; + } + +--- request + GET /t +--- response_body +ok +--- no_error_log +[cirt] + + + +=== TEST 3: bind to 127.0.0.10 +--- no_http2 +--- http_config + lua_package_path "$TEST_NGINX_SERVER_ROOT/html/?.lua;;"; + + upstream backend { + server 127.0.0.1:$TEST_NGINX_SERVER_PORT; + } +--- config + set $proxy_local_addr ""; + proxy_bind $proxy_local_addr; + + location = /t { + access_by_lua_block { + ngx.var.proxy_local_addr="127.0.0.10" + } + proxy_pass http://backend/back; + } + + location = /back { + echo ok; + } + +--- request + GET /t +--- response_body +ok +--- no_error_log +[cirt] + + + +=== TEST 4: bind to not exist addr 100.100.100.100 +--- no_http2 +--- http_config + lua_package_path "$TEST_NGINX_SERVER_ROOT/html/?.lua;;"; + + upstream backend { + server 127.0.0.1:$TEST_NGINX_SERVER_PORT; + } +--- config + set $proxy_local_addr ""; + proxy_bind $proxy_local_addr; + + location = /t { + access_by_lua_block { + ngx.var.proxy_local_addr="100.100.100.100" + } + proxy_pass http://backend/back; + } + + location = /back { + echo ok; + } + +--- request + GET /t +--- response_body_like chomp +500 Internal Server Error +--- error_code: 500 +--- error_log +bind(100.100.100.100) failed (99: Cannot assign requested address) diff --git a/t/138-balancer.t b/t/138-balancer.t index 238dea8fa3..f48c7fa41d 100644 --- a/t/138-balancer.t +++ b/t/138-balancer.t @@ -7,9 +7,12 @@ use Test::Nginx::Socket::Lua; #workers(2); #log_level('warn'); +#connect 0.0.0.1 on newer kernel won't return EINVAL +#so add an route with cmd: sudo ip route add prohibit 0.0.0.1/32 + repeat_each(2); -plan tests => repeat_each() * (blocks() * 4 + 9); +plan tests => repeat_each() * (blocks() * 4 - 3); #no_diff(); no_long_string(); @@ -35,11 +38,9 @@ __DATA__ --- error_code: 502 --- error_log eval [ -'[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream,', +'[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream,', qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\.0\.1:80/t"}, ] ---- no_error_log -[warn] @@ -61,10 +62,9 @@ qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\ --- response_body_like: 403 Forbidden --- error_code: 403 --- error_log -[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream, +[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream, --- no_error_log eval [ -'[warn]', qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\.0\.1:80/t"}, ] @@ -89,11 +89,9 @@ qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\ --- error_code: 502 --- error_log eval [ -'[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream,', +'[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream,', qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\.0\.1:80/t"}, ] ---- no_error_log -[warn] @@ -122,8 +120,6 @@ qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\ "2: variable foo = 33", qr/\[crit\] .* connect\(\) .*? failed/, ] ---- no_error_log -[warn] @@ -150,8 +146,6 @@ Foo: bar "header foo: bar", qr/\[crit\] .* connect\(\) .*? failed/, ] ---- no_error_log -[warn] @@ -177,12 +171,11 @@ Foo: bar ["arg foo: bar", qr/\[crit\] .* connect\(\) .*? failed/, ] ---- no_error_log -[warn] === TEST 7: ngx.req.get_method() works +--- no_http2 --- http_config upstream backend { server 0.0.0.1; @@ -205,8 +198,6 @@ Foo: bar "method: GET", qr/\[crit\] .* connect\(\) .*? failed/, ] ---- no_error_log -[warn] @@ -232,8 +223,6 @@ print("hello from balancer by lua!") '[lua] a.lua:1: hello from balancer by lua! while connecting to upstream,', qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\.0\.1:80/t"}, ] ---- no_error_log -[warn] @@ -254,7 +243,7 @@ qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\ --- response_body_like: 500 Internal Server Error --- error_code: 500 --- error_log eval -qr/\[error\] .*? failed to run balancer_by_lua\*: balancer_by_lua:2: API disabled in the context of balancer_by_lua\*/ +qr/\[error\] .*? failed to run balancer_by_lua\*: balancer_by_lua\(nginx\.conf:27\):2: API disabled in the context of balancer_by_lua\*/ @@ -275,7 +264,7 @@ qr/\[error\] .*? failed to run balancer_by_lua\*: balancer_by_lua:2: API disable --- response_body_like: 500 Internal Server Error --- error_code: 500 --- error_log eval -qr/\[error\] .*? failed to run balancer_by_lua\*: balancer_by_lua:2: API disabled in the context of balancer_by_lua\*/ +qr/\[error\] .*? failed to run balancer_by_lua\*: balancer_by_lua\(nginx\.conf:27\):2: API disabled in the context of balancer_by_lua\*/ @@ -306,6 +295,7 @@ qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\ === TEST 12: code cache off +--- no_http2 --- http_config lua_package_path "$TEST_NGINX_SERVER_ROOT/html/?.lua;;"; @@ -424,15 +414,13 @@ ctx counter: nil --- error_code: 502 --- error_log eval [ -'[lua] balancer_by_lua:2: hello from balancer by lua! while connecting to upstream,', +'[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream,', qr{\[crit\] .*? connect\(\) to 0\.0\.0\.1:80 failed .*?, upstream: "http://0\.0\.0\.1:80/t"}, ] ---- no_error_log -[warn] -=== TEST 15: test if execeed proxy_next_upstream_limit +=== TEST 15: test if exceed proxy_next_upstream_limit --- http_config lua_package_path "../lua-resty-core/lib/?.lua;;"; @@ -532,7 +520,7 @@ failed to set more tries: reduced tries due to limit lua_package_path "../lua-resty-core/lib/?.lua;;"; server { - listen 127.0.0.1:8888; + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1; location / { return 200 "it works"; @@ -540,8 +528,8 @@ failed to set more tries: reduced tries due to limit } upstream foo { - server 127.0.0.1:8888 max_fails=0; - server 127.0.0.1:8889 max_fails=0 weight=9999; + server 127.0.0.1:$TEST_NGINX_RAND_PORT_1 max_fails=0; + server 127.0.0.1:$TEST_NGINX_RAND_PORT_2 max_fails=0 weight=9999; balancer_by_lua_block { local bal = require "ngx.balancer" @@ -564,3 +552,111 @@ connect() failed (111: Connection refused) while connecting to upstream --- no_error_log upstream sent more data than specified in "Content-Length" header while reading upstream [alert] + + + +=== TEST 18: error in balancer_by_lua_block +--- http_config + upstream backend { + server 0.0.0.1; + balancer_by_lua_block { + ngx.say("hello" + } + } +--- config + location = /t { + proxy_pass http://backend; + } +--- request + GET /t +--- response_body_like: 500 Internal Server Error +--- error_code: 500 +--- error_log eval + "failed to load inlined Lua code: balancer_by_lua(nginx.conf:27):3: ')' expected (to close '(' at line 2) near ''", + + + +=== TEST 19: disable ssl +--- http_config + lua_package_path "$TEST_NGINX_SERVER_ROOT/html/?.lua;;"; + + upstream backend { + server 127.0.0.1:$TEST_NGINX_SERVER_PORT; + balancer_by_lua_block { + local ffi = require "ffi" + local C = ffi.C +ffi.cdef[[ +int +ngx_http_lua_ffi_balancer_set_upstream_tls(ngx_http_request_t *r, int on, char **err); +]] + local errmsg = ffi.new("char *[1]") + local r = require "resty.core.base" .get_request() + if r == nil then + ngx.log(ngx.ERR, "no request found") + return + end + + local rc = C.ngx_http_lua_ffi_balancer_set_upstream_tls(r, 0, errmsg) + if rc < 0 then + ngx.log(ngx.ERR, "failed to disable ssl: ", ffi.string(errmsg[0])) + return + end + } + } +--- config + location = /t { + proxy_pass https://backend/back; + } + + location = /back { + echo ok; + } + +--- request + GET /t +--- response_body +ok +--- no_error_log +[error] +[cirt] + + + +=== TEST 20: recreate_request refresh body buffer when ngx.req.set_body_data is used in balancer phase +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + server { + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1; + + location / { + content_by_lua_block { + ngx.req.read_body() + local body = ngx.req.get_body_data() + ngx.log(ngx.ERR, "body: ", body) + ngx.say(body) + } + } + } + + upstream foo { + server 127.0.0.1:$TEST_NGINX_RAND_PORT_1 max_fails=0; + + balancer_by_lua_block { + local bal = require "ngx.balancer" + ngx.req.set_body_data("hello world") + assert(bal.recreate_request()) + } + } + +--- config + location = /t { + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://foo; + } +--- request +GET /t +--- error_code: 200 +--- response_body +hello world diff --git a/t/139-ssl-cert-by.t b/t/139-ssl-cert-by.t index d7e20bafea..d905cb3f16 100644 --- a/t/139-ssl-cert-by.t +++ b/t/139-ssl-cert-by.t @@ -7,15 +7,19 @@ repeat_each(3); # All these tests need to have new openssl my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; my $openssl_version = eval { `$NginxBinary -V 2>&1` }; +if ($openssl_version =~ m/BoringSSL/) { + $ENV{TEST_NGINX_USE_BORINGSSL} = 1; +} if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) { - plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1"); + plan(skip_all => "too old OpenSSL, need >= 1.0.2e, was $1"); } else { plan tests => repeat_each() * (blocks() * 6 + 4); } $ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6; #log_level 'warn'; log_level 'debug'; @@ -100,7 +104,7 @@ __DATA__ GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -117,18 +121,18 @@ lua ssl server name: "test.com" --- no_error_log [error] [alert] ---- grep_error_log eval: qr/ssl_certificate_by_lua:.*?,|\bssl cert: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log eval: qr/ssl_certificate_by_lua\(nginx.conf:\d+\):.*?,|\bssl cert: connection reusable: \d+|\breusable connection: \d+/ --- grep_error_log_out eval # Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0) # before call ssl callback function $Test::Nginx::Util::NginxVersion >= 1.017009 ? qr/reusable connection: 0 ssl cert: connection reusable: 0 -ssl_certificate_by_lua:1: ssl cert by lua is running!,/ +ssl_certificate_by_lua\(nginx.conf:28\):1: ssl cert by lua is running!,/ : qr /reusable connection: 1 ssl cert: connection reusable: 1 reusable connection: 0 -ssl_certificate_by_lua:1: ssl cert by lua is running!,/ +ssl_certificate_by_lua\(nginx.conf:28\):1: ssl cert by lua is running!,/ @@ -209,7 +213,7 @@ ssl_certificate_by_lua:1: ssl cert by lua is running!,/ GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -314,7 +318,7 @@ qr/elapsed in ssl cert by lua: 0.(?:09|1\d)\d+,/, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -432,7 +436,7 @@ my timer run! GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -456,7 +460,7 @@ received memc reply: OK === TEST 5: ngx.exit(0) - no yield --- http_config server { - listen 127.0.0.2:8080 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; server_name test.com; ssl_certificate_by_lua_block { ngx.exit(0) @@ -484,7 +488,7 @@ received memc reply: OK sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.2", 8080) + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.say("failed to connect: ", err) return @@ -523,7 +527,7 @@ should never reached here === TEST 6: ngx.exit(ngx.ERROR) - no yield --- http_config server { - listen 127.0.0.2:8080 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; server_name test.com; ssl_certificate_by_lua_block { ngx.exit(ngx.ERROR) @@ -551,7 +555,7 @@ should never reached here sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.2", 8080) + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.say("failed to connect: ", err) return @@ -578,8 +582,8 @@ failed to do SSL handshake: handshake failed --- error_log eval [ -'lua_certificate_by_lua: handler return value: -1, cert cb exit code: 0', -qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +'ssl_certificate_by_lua: handler return value: -1, cert cb exit code: 0', +qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/, 'lua exit with code -1', ] @@ -593,7 +597,7 @@ should never reached here === TEST 7: ngx.exit(0) - yield --- http_config server { - listen 127.0.0.2:8080 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; server_name test.com; ssl_certificate_by_lua_block { ngx.sleep(0.001) @@ -623,7 +627,7 @@ should never reached here sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.2", 8080) + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.say("failed to connect: ", err) return @@ -662,7 +666,7 @@ should never reached here === TEST 8: ngx.exit(ngx.ERROR) - yield --- http_config server { - listen 127.0.0.2:8080 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; server_name test.com; ssl_certificate_by_lua_block { ngx.sleep(0.001) @@ -692,7 +696,7 @@ should never reached here sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.2", 8080) + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.say("failed to connect: ", err) return @@ -719,8 +723,8 @@ failed to do SSL handshake: handshake failed --- error_log eval [ -'lua_certificate_by_lua: cert cb exit code: 0', -qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +'ssl_certificate_by_lua: cert cb exit code: 0', +qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/, 'lua exit with code -1', ] @@ -734,7 +738,7 @@ should never reached here === TEST 9: lua exception - no yield --- http_config server { - listen 127.0.0.2:8080 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; server_name test.com; ssl_certificate_by_lua_block { error("bad bad bad") @@ -762,7 +766,7 @@ should never reached here sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.2", 8080) + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.say("failed to connect: ", err) return @@ -789,9 +793,9 @@ failed to do SSL handshake: handshake failed --- error_log eval [ -'runtime error: ssl_certificate_by_lua:2: bad bad bad', -'lua_certificate_by_lua: handler return value: 500, cert cb exit code: 0', -qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +'runtime error: ssl_certificate_by_lua(nginx.conf:28):2: bad bad bad', +'ssl_certificate_by_lua: handler return value: 500, cert cb exit code: 0', +qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/, qr/context: ssl_certificate_by_lua\*, client: \d+\.\d+\.\d+\.\d+, server: \d+\.\d+\.\d+\.\d+:\d+/, ] @@ -805,7 +809,7 @@ should never reached here === TEST 10: lua exception - yield --- http_config server { - listen 127.0.0.2:8080 ssl; + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl; server_name test.com; ssl_certificate_by_lua_block { ngx.sleep(0.001) @@ -834,7 +838,7 @@ should never reached here sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.2", 8080) + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.say("failed to connect: ", err) return @@ -861,9 +865,9 @@ failed to do SSL handshake: handshake failed --- error_log eval [ -'runtime error: ssl_certificate_by_lua:3: bad bad bad', -'lua_certificate_by_lua: cert cb exit code: 0', -qr/\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error/, +'runtime error: ssl_certificate_by_lua(nginx.conf:28):3: bad bad bad', +'ssl_certificate_by_lua: cert cb exit code: 0', +qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/, ] --- no_error_log @@ -924,7 +928,7 @@ should never reached here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata --- error_log lua ssl server name: "test.com" @@ -1050,8 +1054,8 @@ failed to do SSL handshake: handshake failed --- error_log eval [ 'lua ssl server name: "test.com"', -'ssl_certificate_by_lua:1: API disabled in the context of ssl_certificate_by_lua*', -qr/\[info\] .*?cert cb error/, +'ssl_certificate_by_lua(nginx.conf:28):1: API disabled in the context of ssl_certificate_by_lua*', +qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/, ] --- no_error_log @@ -1137,7 +1141,7 @@ print("ssl cert by lua is running!") GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -1248,7 +1252,7 @@ a.lua:1: ssl cert by lua is running! GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -1372,7 +1376,7 @@ lua ssl server name: "test.com" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -1468,7 +1472,7 @@ GitHub openresty/lua-resty-core#42 GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -1481,7 +1485,7 @@ close: 1 nil --- error_log lua ssl server name: "test.com" -ssl_certificate_by_lua:1: ssl cert by lua is running! +ssl_certificate_by_lua(nginx.conf:25):1: ssl cert by lua is running! --- no_error_log [error] @@ -1566,7 +1570,7 @@ github issue #723 GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -1668,7 +1672,7 @@ ssl_certificate_by_lua:1: ssl cert by lua is running! GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 59 bytes. received: HTTP/1.1 200 OK received: Server: nginx @@ -1681,7 +1685,7 @@ close: 1 nil --- error_log eval [ -'ssl_certificate_by_lua:1: ssl cert by lua is running!', +'ssl_certificate_by_lua(nginx.conf:29):1: ssl cert by lua is running!', 'lua ssl server name: "test.com"', ] --- no_error_log @@ -1885,7 +1889,7 @@ qr/\[info\] .*? SSL_do_handshake\(\) failed\b/, lua_package_path "../lua-resty-core/lib/?.lua;;"; server { - listen 127.0.0.1:12345 ssl; + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1 ssl; server_name test.com; ssl_certificate_by_lua_block { @@ -1917,7 +1921,7 @@ qr/\[info\] .*? SSL_do_handshake\(\) failed\b/, sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.1", 12345) + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.say("failed to connect: ", err) return @@ -1963,7 +1967,7 @@ qr/\[info\] .*? SSL_do_handshake\(\) failed\b/, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -2063,7 +2067,7 @@ client ip: 127.0.0.1 GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -2075,7 +2079,7 @@ received: foo close: 1 nil --- error_log -client socket file: +client socket file: --- no_error_log [error] @@ -2221,7 +2225,7 @@ qr/elapsed in ssl_certificate_by_lua\*: 0\.(?:09|1\d)\d+,/, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata --- no_error_log [error] [alert] @@ -2311,7 +2315,7 @@ ssl handshake: userdata GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata --- no_error_log [error] [alert] @@ -2321,3 +2325,445 @@ ssl handshake: userdata uthread: hello from f() uthread: killed uthread: failed to kill: already waited or killed + + + +=== TEST 27: ssl_certificate_by_lua* with TCP cosocket (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_certificate_by_lua_block { + print("ssl certificate: TCP cosocket test start") + + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send flush_all command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("ssl certificate: received TCP memc reply: ", res) + sock:close() + print("ssl certificate: TCP cosocket test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl certificate: TCP cosocket test start|ssl certificate: received TCP memc reply: OK|ssl certificate: TCP cosocket test done|test completed)/ +--- grep_error_log_out +ssl certificate: TCP cosocket test start +ssl certificate: received TCP memc reply: OK +ssl certificate: TCP cosocket test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 28: ssl_certificate_by_lua* with UDP cosocket (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_certificate_by_lua_block { + print("ssl certificate: UDP cosocket test start") + + local sock = ngx.socket.udp() + sock:settimeout(1000) + + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = sock:send(req) + if not ok then + ngx.log(ngx.ERR, "failed to send flush_all to memc: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("ssl certificate: received UDP memc reply of ", #res, " bytes") + sock:close() + print("ssl certificate: UDP cosocket test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl certificate: UDP cosocket test start|ssl certificate: received UDP memc reply of \d+ bytes|ssl certificate: UDP cosocket test done|test completed)/ +--- grep_error_log_out eval +[ +qr/ssl certificate: UDP cosocket test start/, +qr/ssl certificate: received UDP memc reply of 12 bytes/, +qr/ssl certificate: UDP cosocket test done/, +qr/test completed/, +] + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 29: ssl_certificate_by_lua* with ngx.timer (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_certificate_by_lua_block { + print("ssl certificate: timer test start") + + local function timer_handler() + print("ssl certificate: timer executed") + end + + local ok, err = ngx.timer.at(0, timer_handler) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + + print("ssl certificate: timer created") + print("ssl certificate: timer test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl certificate: timer test start|ssl certificate: timer created|ssl certificate: timer test done|ssl certificate: timer executed|test completed)/ +--- grep_error_log_out +ssl certificate: timer test start +ssl certificate: timer created +ssl certificate: timer test done +ssl certificate: timer executed +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 30: ssl_certificate_by_lua* with user threads (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_certificate_by_lua_block { + print("ssl certificate: uthread test start") + + local function worker() + ngx.sleep(0.01) + print("ssl certificate: uthread worker executed") + return "worker_result" + end + + local t, err = ngx.thread.spawn(worker) + if not t then + ngx.log(ngx.ERR, "failed to spawn thread: ", err) + return + end + + print("ssl certificate: uthread spawned") + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.log(ngx.ERR, "failed to wait thread: ", res) + return + end + + print("ssl certificate: uthread result: ", res) + print("ssl certificate: uthread test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl certificate: uthread test start|ssl certificate: uthread spawned|ssl certificate: uthread worker executed|ssl certificate: uthread result: worker_result|ssl certificate: uthread test done|test completed)/ +--- grep_error_log_out +ssl certificate: uthread test start +ssl certificate: uthread spawned +ssl certificate: uthread worker executed +ssl certificate: uthread result: worker_result +ssl certificate: uthread test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 31: ssl_certificate_by_lua* with coroutines (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_certificate_by_lua_block { + print("ssl certificate: coroutine test start") + + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function coro_func() + local cnt = 0 + for i = 1, 3 do + print("ssl certificate: coro yield: ", cnt) + cy() + cnt = cnt + 1 + end + return "coro_done" + end + + local c = cc(coro_func) + for i = 1, 4 do + print("ssl certificate: coro resume, status: ", coroutine.status(c)) + local ok, res = cr(c) + if not ok then + print("ssl certificate: coro error: ", res) + break + end + if coroutine.status(c) == "dead" then + print("ssl certificate: coro result: ", res) + break + end + end + + print("ssl certificate: coroutine test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl certificate: coroutine test start|ssl certificate: coro resume, status: \w+|ssl certificate: coro yield: \d+|ssl certificate: coro result: coro_done|ssl certificate: coroutine test done|test completed)/ +--- grep_error_log_out +ssl certificate: coroutine test start +ssl certificate: coro resume, status: suspended +ssl certificate: coro yield: 0 +ssl certificate: coro resume, status: suspended +ssl certificate: coro yield: 1 +ssl certificate: coro resume, status: suspended +ssl certificate: coro yield: 2 +ssl certificate: coro resume, status: suspended +ssl certificate: coro result: coro_done +ssl certificate: coroutine test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 32: ssl_certificate_by_lua* without yield API (simple logic) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_certificate_by_lua_block { + print("ssl certificate: simple test start") + + -- Simple calculations without yield + local sum = 0 + for i = 1, 10 do + sum = sum + i + end + + print("ssl certificate: calculated sum: ", sum) + + -- String operations + local str = "hello" + str = str .. " world" + print("ssl certificate: concatenated string: ", str) + + -- Table operations + local t = {a = 1, b = 2, c = 3} + local count = 0 + for k, v in pairs(t) do + count = count + v + end + print("ssl certificate: table sum: ", count) + + print("ssl certificate: simple test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl certificate: simple test start|ssl certificate: calculated sum: 55|ssl certificate: concatenated string: hello world|ssl certificate: table sum: 6|ssl certificate: simple test done|test completed)/ +--- grep_error_log_out +ssl certificate: simple test start +ssl certificate: calculated sum: 55 +ssl certificate: concatenated string: hello world +ssl certificate: table sum: 6 +ssl certificate: simple test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 33: ssl_certificate_by_lua* with multiple network operations (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_certificate_by_lua_block { + print("ssl certificate: multiple network test start") + + -- First TCP operation + local sock1 = ngx.socket.tcp() + sock1:settimeout(2000) + local ok, err = sock1:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if ok then + local bytes, err = sock1:send("version\r\n") + if bytes then + local res, err = sock1:receive() + if res then + print("ssl certificate: TCP1 version: ", res) + end + end + sock1:close() + end + + ngx.sleep(0.01) -- Small delay + + -- Second UDP operation + local sock2 = ngx.socket.udp() + sock2:settimeout(1000) + local ok, err = sock2:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if ok then + local req = "\0\1\0\0\0\1\0\0version\r\n" + local ok, err = sock2:send(req) + if ok then + local res, err = sock2:receive() + if res then + print("ssl certificate: UDP version reply length: ", #res) + end + end + sock2:close() + end + + print("ssl certificate: multiple network test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl certificate: multiple network test start|ssl certificate: TCP1 version: VERSION|ssl certificate: UDP version reply length: \d+|ssl certificate: multiple network test done|test completed)/ +--- grep_error_log_out eval +[ +qr/ssl certificate: multiple network test start/, +qr/ssl certificate: TCP1 version: VERSION/, +qr/ssl certificate: UDP version reply length: \d+/, +qr/ssl certificate: multiple network test done/, +qr/test completed/, +] + +--- no_error_log +[error] +[alert] +[emerg] diff --git a/t/140-ssl-c-api.t b/t/140-ssl-c-api.t index 1c59135492..9f647c41bc 100644 --- a/t/140-ssl-c-api.t +++ b/t/140-ssl-c-api.t @@ -9,10 +9,13 @@ my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; my $openssl_version = eval { `$NginxBinary -V 2>&1` }; if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) { - plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1"); - + plan(skip_all => "too old OpenSSL, need >= 1.0.2e, was $1"); +} elsif ($openssl_version =~ m/BoringSSL/) { + $ENV{TEST_NGINX_USE_BORINGSSL} = 1; + plan tests => repeat_each() * (blocks() * 6 - 6); } else { - plan tests => repeat_each() * (blocks() * 5 + 1); + plan tests => repeat_each() * (blocks() * 5 - 5); + $ENV{TEST_NGINX_USE_OPENSSL} = 1; } $ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); @@ -36,7 +39,8 @@ ffi.cdef[[ size_t pem_len, unsigned char *der, char **err); int ngx_http_lua_ffi_priv_key_pem_to_der(const unsigned char *pem, - size_t pem_len, unsigned char *der, char **err); + size_t pem_len, const unsigned char *passphrase, + unsigned char *der, char **err); int ngx_http_lua_ffi_ssl_set_der_certificate(void *r, const char *data, size_t len, char **err); @@ -52,21 +56,32 @@ ffi.cdef[[ void *ngx_http_lua_ffi_parse_pem_priv_key(const unsigned char *pem, size_t pem_len, char **err); + void *ngx_http_lua_ffi_parse_der_cert(const char *data, size_t len, + char **err); + + void *ngx_http_lua_ffi_parse_der_priv_key(const char *data, size_t len, + char **err); + int ngx_http_lua_ffi_set_cert(void *r, void *cdata, char **err); int ngx_http_lua_ffi_set_priv_key(void *r, void *cdata, char **err); + void *ngx_http_lua_ffi_get_req_ssl_pointer(void *r); + void ngx_http_lua_ffi_free_cert(void *cdata); void ngx_http_lua_ffi_free_priv_key(void *cdata); - int ngx_http_lua_ffi_ssl_clear_certs(void *r, char **err); - int ngx_http_lua_ffi_ssl_verify_client(void *r, void *cdata, - int depth, char **err); + void *cdata, int depth, char **err); + + int ngx_http_lua_ffi_ssl_client_random(ngx_http_request_t *r, + unsigned char *out, size_t *outlen, char **err); + int ngx_http_lua_ffi_req_shared_ssl_ciphers(void *r, uint16_t *ciphers, + uint16_t *nciphers, int filter_grease, char **err); ]] _EOC_ } @@ -132,7 +147,7 @@ __DATA__ out = ffi.new("char [?]", #pkey) - local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, out, errmsg) + local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg) if rc < 1 then ngx.log(ngx.ERR, "failed to parse PEM priv key: ", ffi.string(errmsg[0])) @@ -216,7 +231,7 @@ __DATA__ GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -286,7 +301,7 @@ lua ssl server name: "test.com" out = ffi.new("char [?]", #pkey) - local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, out, errmsg) + local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg) if rc < 1 then ngx.log(ngx.ERR, "failed to parse PEM priv key: ", ffi.string(errmsg[0])) @@ -370,7 +385,7 @@ lua ssl server name: "test.com" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -424,7 +439,7 @@ lua ssl server name: "test.com" out = ffi.new("char [?]", #pkey) - local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, out, errmsg) + local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg) if rc < 1 then ngx.log(ngx.ERR, "failed to parse PEM priv key: ", ffi.string(errmsg[0])) @@ -498,7 +513,7 @@ lua ssl server name: "test.com" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -649,7 +664,7 @@ failed to parse PEM priv key: PEM_read_bio_PrivateKey() failed GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -799,7 +814,7 @@ lua ssl server name: "test.com" GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created received: Server: nginx @@ -843,21 +858,21 @@ lua ssl server name: "test.com" local cert_data = f:read("*all") f:close() - local cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) - if not cert then - ngx.log(ngx.ERR, "failed to parse PEM cert: ", + local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not client_cert then + ngx.log(ngx.ERR, "failed to parse PEM client cert: ", ffi.string(errmsg[0])) return end - local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, cert, 1, errmsg) + local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 1, errmsg) if rc ~= 0 then ngx.log(ngx.ERR, "failed to verify client: ", ffi.string(errmsg[0])) return end - ffi.C.ngx_http_lua_ffi_free_cert(cert) + ffi.C.ngx_http_lua_ffi_free_cert(client_cert) } ssl_certificate ../../cert/test2.crt; @@ -914,7 +929,7 @@ client certificate subject: emailAddress=agentzh@gmail.com,CN=test.com return end - local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, nil, -1, errmsg) + local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, nil, nil, -1, errmsg) if rc ~= 0 then ngx.log(ngx.ERR, "failed to verify client: ", ffi.string(errmsg[0])) @@ -944,8 +959,8 @@ client certificate subject: emailAddress=agentzh@gmail.com,CN=test.com --- request GET /t ---- response_body -FAILED:self signed certificate +--- response_body eval +qr/FAILED:self[- ]signed certificate/ --- error_log client certificate subject: emailAddress=agentzh@gmail.com,CN=test.com @@ -980,21 +995,21 @@ client certificate subject: emailAddress=agentzh@gmail.com,CN=test.com local cert_data = f:read("*all") f:close() - local cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) - if not cert then - ngx.log(ngx.ERR, "failed to parse PEM cert: ", + local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not client_cert then + ngx.log(ngx.ERR, "failed to parse PEM client cert: ", ffi.string(errmsg[0])) return end - local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, cert, 1, errmsg) + local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 1, errmsg) if rc ~= 0 then ngx.log(ngx.ERR, "failed to verify client: ", ffi.string(errmsg[0])) return end - ffi.C.ngx_http_lua_ffi_free_cert(cert) + ffi.C.ngx_http_lua_ffi_free_cert(client_cert) } ssl_certificate ../../cert/test2.crt; @@ -1026,3 +1041,1036 @@ client certificate subject: nil --- no_error_log [error] [alert] + + + +=== TEST 9: simple cert + private key with passphrase +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + + ffi.cdef[[ + int ngx_http_lua_ffi_cert_pem_to_der(const unsigned char *pem, + size_t pem_len, unsigned char *der, char **err); + + int ngx_http_lua_ffi_priv_key_pem_to_der(const unsigned char *pem, + size_t pem_len, const unsigned char *passphrase, + unsigned char *der, char **err); + + int ngx_http_lua_ffi_ssl_set_der_certificate(void *r, + const char *data, size_t len, char **err); + + int ngx_http_lua_ffi_ssl_set_der_private_key(void *r, + const char *data, size_t len, char **err); + + int ngx_http_lua_ffi_ssl_clear_certs(void *r, char **err); + ]] + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg) + + local f = assert(io.open("t/cert/test_passphrase.crt", "rb")) + local cert = f:read("*all") + f:close() + + local out = ffi.new("char [?]", #cert) + + local rc = ffi.C.ngx_http_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM cert: ", + ffi.string(errmsg[0])) + return + end + + local cert_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_certificate(r, cert_der, #cert_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER cert: ", + ffi.string(errmsg[0])) + return + end + + f = assert(io.open("t/cert/test_passphrase.key", "rb")) + local pkey = f:read("*all") + f:close() + + local passphrase = "123456" + + out = ffi.new("char [?]", #pkey) + + local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, passphrase, out, errmsg) + if rc < 1 then + ngx.log(ngx.ERR, "failed to parse PEM priv key: ", + ffi.string(errmsg[0])) + return + end + + local pkey_der = ffi.string(out, rc) + + local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_private_key(r, pkey_der, #pkey_der, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set DER priv key: ", + ffi.string(errmsg[0])) + return + end + } + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test_passphrase.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 10: Raw SSL pointer +--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local r = require "resty.core.base" .get_request() + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + local ssl = ffi.C.ngx_http_lua_ffi_get_req_ssl_pointer(r); + if ssl == nil then + ngx.log(ngx.ERR, "failed to retrieve SSL*") + return + end + + ffi.cdef[[ + const char *SSL_get_servername(const void *, const int); + ]] + local TLSEXT_NAMETYPE_host_name = 0 + local sni = ffi.C.SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name) + if sni == nil then + ngx.log(ngx.ERR, "failed to get sni") + return + end + + ngx.log(ngx.INFO, "SNI is ", ffi.string(sni)) + } + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +SNI is test.com + +--- no_error_log +[error] +[alert] + + + +=== TEST 11: DER cert + private key cdata +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if r == nil then + ngx.log(ngx.ERR, "no request found") + return + end + + ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg) + + local f = assert(io.open("t/cert/test_der.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local cert = ffi.C.ngx_http_lua_ffi_parse_der_cert(cert_data, #cert_data, errmsg) + if not cert then + ngx.log(ngx.ERR, "failed to parse DER cert: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_http_lua_ffi_set_cert(r, cert, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata cert: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_http_lua_ffi_free_cert(cert) + + f = assert(io.open("t/cert/test_der.key", "rb")) + local pkey_data = f:read("*all") + f:close() + + local pkey = ffi.C.ngx_http_lua_ffi_parse_der_priv_key(pkey_data, #pkey_data, errmsg) + if pkey == nil then + ngx.log(ngx.ERR, "failed to parse DER priv key: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_http_lua_ffi_set_priv_key(r, pkey, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set cdata priv key: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_http_lua_ffi_free_priv_key(pkey) + } + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 12: client random +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if r == nil then + ngx.log(ngx.ERR, "no request found") + return + end + + -- test client random length + local out = ffi.new("unsigned char[?]", 0) + local sizep = ffi.new("size_t[1]", 0) + + local rc = ffi.C.ngx_http_lua_ffi_ssl_client_random(r, out, sizep, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to get client random length: ", + ffi.string(errmsg[0])) + return + end + + if tonumber(sizep[0]) ~= 32 then + ngx.log(ngx.ERR, "client random length does not equal 32") + return + end + + -- test client random value + out = ffi.new("unsigned char[?]", 50) + sizep = ffi.new("size_t[1]", 50) + + rc = ffi.C.ngx_http_lua_ffi_ssl_client_random(r, out, sizep, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to get client random: ", + ffi.string(errmsg[0])) + return + end + + local init_v = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + if ffi.string(out, sizep[0]) == init_v then + ngx.log(ngx.ERR, "maybe the client random value is incorrect") + return + end + } + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 13: verify client, but server don't trust root ca +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + + ssl_certificate_by_lua_block { + collectgarbage() + + require "defines" + local ffi = require "ffi" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if r == nil then + ngx.log(ngx.ERR, "no request found") + return + end + + local f = assert(io.open("t/cert/mtls_server.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not client_cert then + ngx.log(ngx.ERR, "failed to parse PEM client cert: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 2, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to verify client: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_http_lua_ffi_free_cert(client_cert) + } + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + content_by_lua_block { + ngx.say(ngx.var.ssl_client_verify) + } + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_session_reuse off; + } + +--- request +GET /t +--- response_body +FAILED:unable to verify the first certificate + +--- no_error_log +[error] +[alert] + + + +=== TEST 14: verify client and server trust root ca +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + + ssl_certificate_by_lua_block { + collectgarbage() + + require "defines" + local ffi = require "ffi" + + local errmsg = ffi.new("char *[1]") + + local r = require "resty.core.base" .get_request() + if r == nil then + ngx.log(ngx.ERR, "no request found") + return + end + + local f = assert(io.open("t/cert/mtls_server.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not client_cert then + ngx.log(ngx.ERR, "failed to parse PEM client cert: ", + ffi.string(errmsg[0])) + return + end + + local f = assert(io.open("t/cert/mtls_ca.crt", "rb")) + local cert_data = f:read("*all") + f:close() + + local trusted_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg) + if not trusted_cert then + ngx.log(ngx.ERR, "failed to parse PEM trusted cert: ", + ffi.string(errmsg[0])) + return + end + + local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, cert, trusted_cert, 2, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to verify client: ", + ffi.string(errmsg[0])) + return + end + + ffi.C.ngx_http_lua_ffi_free_cert(client_cert) + ffi.C.ngx_http_lua_ffi_free_cert(trusted_cert) + } + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + content_by_lua_block { + ngx.say(ngx.var.ssl_client_verify) + } + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_session_reuse off; + } + +--- request +GET /t +--- response_body +SUCCESS + +--- no_error_log +[error] +[alert] + + + +=== TEST 15: Get supported ciphers +--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_protocols TLSv1.2; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384; + + server_tokens off; + + location /ciphers { + content_by_lua_block { + require "defines" + local ffi = require "ffi" + local cjson = require "cjson.safe" + local base = require "resty.core.base" + local get_request = base.get_request + + local MAX_CIPHERS = 64 + local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS) + local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS) + local err = ffi.new("char*[1]") + + local r = get_request() + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) + + if ret ~= 0 then + ngx.log(ngx.ERR, "error: ", ffi.string(err[0])) + return + end + + local res = {} + for i = 0, nciphers[0] - 1 do + local cipher_id = string.format("%04x", ciphers[i]) + table.insert(res, cipher_id) + end + + ngx.say(cjson.encode(res)) + } + } + } +--- config + server_tokens off; + location /t { + proxy_ssl_protocols TLSv1.2; + proxy_ssl_session_reuse off; + proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers; + } +--- request +GET /t +--- response_body_like +\["c02f","c02b"\] +--- error_log chomp +TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD" + + + +=== TEST 16: SSL cipher API error handling (no SSL) +--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} +--- config + location /t { + content_by_lua_block { + require "defines" + local ffi = require "ffi" + + local ciphers = ffi.new("uint16_t[64]") + local nciphers = ffi.new("uint16_t[1]", 64) + local err = ffi.new("char*[1]") + + -- use nil request to trigger error + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(nil, ciphers, nciphers, 0, err) + + ngx.say("ret: ", ret) + if err[0] ~= nil then + ngx.say("err: ", ffi.string(err[0])) + end + } + } +--- request +GET /t +--- response_body +ret: -1 +err: bad request + +--- no_error_log +[error] +[alert] + + + +=== TEST 17: Buffer overflow handling +--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_protocols TLSv1.2; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384; + + server_tokens off; + + + location /ciphers { + content_by_lua_block { + require "defines" + local ffi = require "ffi" + local base = require "resty.core.base" + local get_request = base.get_request + local cjson = require "cjson.safe" + + local MAX_CIPHERS = 64 + local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS) + local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS) + local err = ffi.new("char*[1]") + + local r = get_request() + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) + + if ret ~= 0 then + ngx.log(ngx.ERR, "error: ", ffi.string(err[0])) + return + end + local res = {} + for i = 0, nciphers[0] - 1 do + local cipher_id = string.format("%04x", ciphers[i]) + + table.insert(res, cipher_id) + end + ngx.say(cjson.encode(res)) + } + } + } +--- config + server_tokens off; + location /t { + proxy_ssl_protocols TLSv1.2; + proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256; + proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers; + } +--- request +GET /t +--- response_body_like +\["c02f"\] +--- error_code: 200 +--- error_log chomp +TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD" + + + +=== TEST 18: BORINGSSL error handling +--- skip_eval: 8:$ENV{TEST_NGINX_USE_OPENSSL} +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384; + + server_tokens off; + + + location /ciphers { + content_by_lua_block { + require "defines" + local ffi = require "ffi" + local base = require "resty.core.base" + local get_request = base.get_request + + local MAX_CIPHERS = 64 + local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS) + local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS) + local err = ffi.new("char*[1]") + + local r = get_request() + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) + + if ret ~= 0 then + ngx.say("Error: ", ffi.string(err[0])) + return + end + + } + } + } +--- config + server_tokens off; + location /t { + proxy_ssl_protocols TLSv1.2 TLSv1.3; + proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers; + } +--- request +GET /t +--- response_body_like chomp +Error: BoringSSL is not supported for SSL cipher operations +--- error_code: 200 + +--- no_error_log +[error] +[alert] + + + +=== TEST 19: Get supported ciphers with GREASE filtering +--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_protocols TLSv1.2; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384; + + server_tokens off; + + location /ciphers { + content_by_lua_block { + require "defines" + local ffi = require "ffi" + local cjson = require "cjson.safe" + local base = require "resty.core.base" + local get_request = base.get_request + + local MAX_CIPHERS = 64 + local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS) + local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS) + local err = ffi.new("char*[1]") + + local r = get_request() + -- Test without GREASE filtering + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) + if ret ~= 0 then + ngx.log(ngx.ERR, "error without filtering: ", ffi.string(err[0])) + return + end + + local res_no_filter = {} + for i = 0, nciphers[0] - 1 do + local cipher_id = string.format("%04x", ciphers[i]) + table.insert(res_no_filter, cipher_id) + end + + -- Reset buffers + nciphers[0] = MAX_CIPHERS + + -- Test with GREASE filtering + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 1, err) + if ret ~= 0 then + ngx.log(ngx.ERR, "error with filtering: ", ffi.string(err[0])) + return + end + + local res_with_filter = {} + for i = 0, nciphers[0] - 1 do + local cipher_id = string.format("%04x", ciphers[i]) + table.insert(res_with_filter, cipher_id) + end + + ngx.say("without_filter:", cjson.encode(res_no_filter)) + ngx.say("with_filter:", cjson.encode(res_with_filter)) + } + } + } +--- config + server_tokens off; + location /t { + proxy_ssl_protocols TLSv1.2; + proxy_ssl_session_reuse off; + proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers; + } +--- request +GET /t +--- response_body_like +without_filter:\[.*\] +with_filter:\[.*\] +--- error_log chomp +TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD" diff --git a/t/142-ssl-session-store.t b/t/142-ssl-session-store.t index 14e90574b0..11deb83207 100644 --- a/t/142-ssl-session-store.t +++ b/t/142-ssl-session-store.t @@ -40,6 +40,7 @@ __DATA__ server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -77,7 +78,7 @@ __DATA__ GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -86,11 +87,11 @@ lua ssl server name: "test.com" --- no_error_log [error] [alert] ---- grep_error_log eval: qr/ssl_session_store_by_lua_block:.*?,|\bssl session store: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log eval: qr/ssl_session_store_by_lua\(nginx.conf:\d+\):.*?,|\bssl session store: connection reusable: \d+|\breusable connection: \d+/ --- grep_error_log_out eval qr/^reusable connection: 0 ssl session store: connection reusable: 0 -ssl_session_store_by_lua_block:1: ssl session store by lua is running!, +ssl_session_store_by_lua\(nginx\.conf:25\):1: ssl session store by lua is running!, /m, @@ -115,6 +116,7 @@ ssl_session_store_by_lua_block:1: ssl session store by lua is running!, server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -152,7 +154,7 @@ ssl_session_store_by_lua_block:1: ssl session store by lua is running!, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -190,6 +192,7 @@ API disabled in the context of ssl_session_store_by_lua* server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -227,7 +230,7 @@ API disabled in the context of ssl_session_store_by_lua* GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -282,6 +285,7 @@ my timer run! server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -319,7 +323,7 @@ my timer run! GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -351,6 +355,7 @@ API disabled in the context of ssl_session_store_by_lua* resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -390,7 +395,7 @@ API disabled in the context of ssl_session_store_by_lua* GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -423,6 +428,7 @@ ngx.exit does not yield and the error code is eaten. resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -462,7 +468,7 @@ ngx.exit does not yield and the error code is eaten. GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -496,6 +502,7 @@ ssl_session_store_by_lua*: handler return value: 0, sess new cb exit code: 0 resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -532,11 +539,11 @@ ssl_session_store_by_lua*: handler return value: 0, sess new cb exit code: 0 GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log -failed to run session_store_by_lua*: ssl_session_store_by_lua_block:2: bad bad bad +failed to run session_store_by_lua*: ssl_session_store_by_lua(nginx.conf:25):2: bad bad bad --- no_error_log should never reached here @@ -564,6 +571,7 @@ should never reached here resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -600,7 +608,7 @@ should never reached here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -634,6 +642,7 @@ get_phase: ssl_session_store server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -671,14 +680,14 @@ get_phase: ssl_session_store GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log eval [ 'lua ssl server name: "test.com"', qr/elapsed in ssl cert by lua: 0.(?:09|1[01])\d+,/, -'ssl_session_store_by_lua_block:1: ssl store session by lua is running!', +'ssl_session_store_by_lua(nginx.conf:25):1: ssl store session by lua is running!', ] --- no_error_log @@ -703,6 +712,7 @@ qr/elapsed in ssl cert by lua: 0.(?:09|1[01])\d+,/, server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -744,7 +754,7 @@ print("ssl store session by lua is running!") GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log @@ -784,6 +794,7 @@ a.lua:1: ssl store session by lua is running! resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -820,7 +831,7 @@ a.lua:1: ssl store session by lua is running! GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- no_error_log @@ -854,6 +865,7 @@ qr/\[emerg\] .*? "ssl_session_store_by_lua_block" directive is not allowed here server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -891,12 +903,12 @@ qr/\[emerg\] .*? "ssl_session_store_by_lua_block" directive is not allowed here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log lua ssl server name: "test.com" -ssl_session_store_by_lua_block:1: ssl session store by lua is running! +ssl_session_store_by_lua(nginx.conf:25):1: ssl session store by lua is running! --- no_error_log [error] @@ -955,7 +967,7 @@ ssl_session_store_by_lua_block:1: ssl session store by lua is running! GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- error_log eval qr/ssl_session_store_by_lua\*: skipped since TLS version >= 1\.3 \(\d+\)/ @@ -963,3 +975,4 @@ qr/ssl_session_store_by_lua\*: skipped since TLS version >= 1\.3 \(\d+\)/ [error] [alert] [emerg] +--- skip_eval: 6:$ENV{TEST_NGINX_USE_HTTP3} diff --git a/t/143-ssl-session-fetch.t b/t/143-ssl-session-fetch.t index 084e7d596c..9cc1870a87 100644 --- a/t/143-ssl-session-fetch.t +++ b/t/143-ssl-session-fetch.t @@ -7,7 +7,13 @@ use File::Basename; repeat_each(3); -plan tests => repeat_each() * (blocks() * 6); +plan tests => repeat_each() * (blocks() * 6) - 3; + +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; +if ($openssl_version =~ m/BoringSSL/) { + $ENV{TEST_NGINX_USE_BORINGSSL} = 1; +} $ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); @@ -41,6 +47,7 @@ __DATA__ server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -80,10 +87,10 @@ __DATA__ GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil ---- grep_error_log eval: qr/ssl_session_fetch_by_lua_block:.*?,|\bssl session fetch: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log eval: qr/ssl_session_fetch_by_lua\(nginx\.conf:\d+\):.*?,|\bssl session fetch: connection reusable: \d+|\breusable connection: \d+/ --- grep_error_log_out eval # Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0) @@ -93,11 +100,11 @@ $Test::Nginx::Util::NginxVersion >= 1.017009 ? qr/\A(?:reusable connection: [01]\n)+\z/s, qr/^reusable connection: 0 ssl session fetch: connection reusable: 0 -ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!, +ssl_session_fetch_by_lua\(nginx\.conf:25\):1: ssl fetch sess by lua is running!, /m, qr/^reusable connection: 0 ssl session fetch: connection reusable: 0 -ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!, +ssl_session_fetch_by_lua\(nginx\.conf:25\):1: ssl fetch sess by lua is running!, /m, ] : @@ -106,12 +113,12 @@ qr/\A(?:reusable connection: [01]\n)+\z/s, qr/^reusable connection: 1 ssl session fetch: connection reusable: 1 reusable connection: 0 -ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!, +ssl_session_fetch_by_lua\(nginx\.conf:25\):1: ssl fetch sess by lua is running!, /m, qr/^reusable connection: 1 ssl session fetch: connection reusable: 1 reusable connection: 0 -ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!, +ssl_session_fetch_by_lua\(nginx\.conf:25\):1: ssl fetch sess by lua is running!, /m, ] --- no_error_log @@ -137,10 +144,12 @@ ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!, server_tokens off; } +--- skip_eval: 6:$ENV{TEST_NGINX_USE_BORINGSSL} --- config server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2 TLSv1.3; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -180,7 +189,7 @@ ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -197,6 +206,7 @@ qr/elapsed in ssl fetch session by lua: 0.(?:09|1[01])\d+,/, [error] [alert] [emerg] +--- skip_openssl: 6: > 1.1.1w @@ -225,6 +235,7 @@ qr/elapsed in ssl fetch session by lua: 0.(?:09|1[01])\d+,/, server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -264,7 +275,7 @@ qr/elapsed in ssl fetch session by lua: 0.(?:09|1[01])\d+,/, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -326,6 +337,7 @@ qr/my timer run!/s server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -365,7 +377,7 @@ qr/my timer run!/s GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -384,6 +396,7 @@ qr/received memc reply: OK/s [alert] [error] [emerg] +--- skip_openssl: 6: > 1.1.1w @@ -407,6 +420,7 @@ qr/received memc reply: OK/s resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -446,7 +460,7 @@ qr/received memc reply: OK/s GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -488,6 +502,7 @@ should never reached here resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -527,7 +542,7 @@ should never reached here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -570,6 +585,7 @@ should never reached here resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; lua_ssl_verify_depth 3; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -609,7 +625,7 @@ should never reached here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -628,6 +644,7 @@ qr/ssl_session_fetch_by_lua\*: sess get cb exit code: 0/s should never reached here [alert] [emerg] +--- skip_openssl: 6: > 1.1.1w @@ -650,6 +667,7 @@ should never reached here server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; lua_ssl_verify_depth 3; location /t { @@ -690,18 +708,18 @@ should never reached here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval -qr/ssl_session_fetch_by_lua_block:2: bad bad bad/s +qr/ssl_session_fetch_by_lua\(nginx.conf:\d+\):2: bad bad bad/s --- grep_error_log_out eval [ '', -'ssl_session_fetch_by_lua_block:2: bad bad bad +'ssl_session_fetch_by_lua(nginx.conf:25):2: bad bad bad ', -'ssl_session_fetch_by_lua_block:2: bad bad bad +'ssl_session_fetch_by_lua(nginx.conf:25):2: bad bad bad ', ] @@ -733,6 +751,7 @@ should never reached here server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; lua_ssl_verify_depth 3; location /t { @@ -773,19 +792,19 @@ should never reached here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval -qr/ssl_session_fetch_by_lua_block:3: bad bad bad|ssl_session_fetch_by_lua\*: sess get cb exit code: 0/s +qr/ssl_session_fetch_by_lua\(nginx.conf:\d+\):3: bad bad bad|ssl_session_fetch_by_lua\*: sess get cb exit code: 0/s --- grep_error_log_out eval [ '', -'ssl_session_fetch_by_lua_block:3: bad bad bad +'ssl_session_fetch_by_lua(nginx.conf:25):3: bad bad bad ssl_session_fetch_by_lua*: sess get cb exit code: 0 ', -'ssl_session_fetch_by_lua_block:3: bad bad bad +'ssl_session_fetch_by_lua(nginx.conf:25):3: bad bad bad ssl_session_fetch_by_lua*: sess get cb exit code: 0 ', @@ -795,6 +814,7 @@ ssl_session_fetch_by_lua*: sess get cb exit code: 0 should never reached here [alert] [emerg] +--- skip_openssl: 6: > 1.1.1w @@ -814,6 +834,7 @@ should never reached here server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -853,7 +874,7 @@ should never reached here GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -900,6 +921,7 @@ qr/get_phase: ssl_session_fetch/s server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -939,13 +961,28 @@ qr/get_phase: ssl_session_fetch/s GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval qr/ssl ((fetch|store) session|cert) by lua is running!/s --- grep_error_log_out eval +if ($ENV{TEST_NGINX_USE_HTTP3}) { +[ +'ssl cert by lua is running! +ssl store session by lua is running! +', +'ssl cert by lua is running! +ssl fetch session by lua is running! +ssl store session by lua is running! +', +'ssl cert by lua is running! +ssl fetch session by lua is running! +ssl store session by lua is running! +', +] +} else { [ 'ssl cert by lua is running! ssl store session by lua is running! @@ -959,11 +996,13 @@ ssl cert by lua is running! ssl store session by lua is running! ', ] +} --- no_error_log [error] [alert] [emerg] +--- skip_openssl: 6: > 1.1.1w @@ -982,6 +1021,7 @@ ssl store session by lua is running! --- config server_tokens off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -1025,7 +1065,7 @@ print("ssl fetch sess by lua is running!") GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval @@ -1070,6 +1110,7 @@ qr/\S+:\d+: ssl fetch sess by lua is running!/s server_tokens off; resolver $TEST_NGINX_RESOLVER ipv6=off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -1109,18 +1150,18 @@ qr/\S+:\d+: ssl fetch sess by lua is running!/s GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval -qr/ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!/s +qr/ssl_session_fetch_by_lua\(nginx.conf:\d+\):1: ssl fetch sess by lua is running!/s --- grep_error_log_out eval [ '', -'ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running! +'ssl_session_fetch_by_lua(nginx.conf:25):1: ssl fetch sess by lua is running! ', -'ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running! +'ssl_session_fetch_by_lua(nginx.conf:25):1: ssl fetch sess by lua is running! ', ] @@ -1170,6 +1211,7 @@ qr/ssl_session_fetch_by_lua_block:1: ssl fetch sess by lua is running!/s --- config server_tokens off; lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { content_by_lua_block { @@ -1249,6 +1291,7 @@ GET /t --- config server_tokens off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { content_by_lua_block { @@ -1285,99 +1328,8 @@ GET /t GET /t --- response_body connected: 1 -ssl handshake: userdata -close: 1 nil ---- no_error_log -[warn] -[error] -[alert] -[emerg] - - - -=== TEST 16: ssl_session_fetch_by_lua* always runs when using SSLv3 (SSLv3 does not support session tickets) ---- http_config - ssl_session_fetch_by_lua_block { print("ssl_session_fetch_by_lua* is running!") } - server { - listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; - ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key; - ssl_protocols SSLv3; - server_tokens off; - } ---- config - server_tokens off; - lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; - lua_ssl_protocols SSLv3; - - location /t { - content_by_lua_block { - do - local sock = ngx.socket.tcp() - - sock:settimeout(5000) - - local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") - if not ok then - ngx.say("failed to connect: ", err) - return - end - - ngx.say("connected: ", ok) - - local sess, err = sock:sslhandshake(package.loaded.session, "test.com", true) - if not sess then - ngx.say("failed to do SSL handshake: ", err) - return - end - - ngx.say("ssl handshake: ", type(sess)) - - package.loaded.session = sess - - local ok, err = sock:close() - ngx.say("close: ", ok, " ", err) - end -- do - -- collectgarbage() - } - } ---- request -GET /t ---- response_body -connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil ---- grep_error_log eval: qr/ssl_session_fetch_by_lua_block:.*?,|\bssl session fetch: connection reusable: \d+|\breusable connection: \d+/ ---- grep_error_log_out eval -# Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0) -# before call ssl callback function -$Test::Nginx::Util::NginxVersion >= 1.017009 ? -[ -qr/\A(?:reusable connection: [01]\n)+\z/s, -qr/^reusable connection: 0 -ssl session fetch: connection reusable: 0 -ssl_session_fetch_by_lua_block:1: ssl_session_fetch_by_lua\* is running!, -/m, -qr/^reusable connection: 0 -ssl session fetch: connection reusable: 0 -ssl_session_fetch_by_lua_block:1: ssl_session_fetch_by_lua\* is running!, -/m, -] -: -[ -qr/\A(?:reusable connection: [01]\n)+\z/s, -qr/^reusable connection: 1 -ssl session fetch: connection reusable: 1 -reusable connection: 0 -ssl_session_fetch_by_lua_block:1: ssl_session_fetch_by_lua\* is running!, -/m, -qr/^reusable connection: 1 -ssl session fetch: connection reusable: 1 -reusable connection: 0 -ssl_session_fetch_by_lua_block:1: ssl_session_fetch_by_lua\* is running!, -/m, -] --- no_error_log [error] [alert] @@ -1385,7 +1337,7 @@ ssl_session_fetch_by_lua_block:1: ssl_session_fetch_by_lua\* is running!, -=== TEST 17: ssl_session_fetch_by_lua* can yield when reading early data +=== TEST 16: ssl_session_fetch_by_lua* can yield when reading early data --- skip_openssl: 6: < 1.1.1 --- http_config ssl_session_fetch_by_lua_block { @@ -1406,6 +1358,7 @@ ssl_session_fetch_by_lua_block:1: ssl_session_fetch_by_lua\* is running!, --- config server_tokens off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { set $port $TEST_NGINX_MEMCACHED_PORT; @@ -1444,7 +1397,7 @@ ssl_session_fetch_by_lua_block:1: ssl_session_fetch_by_lua\* is running!, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval qr/elapsed in ssl_session_fetch_by_lua\*: 0\.(?:09|1[01])\d+,/, @@ -1458,10 +1411,11 @@ qr/elapsed in ssl_session_fetch_by_lua\*: 0\.(?:09|1[01])\d+,/, [error] [alert] [emerg] +--- skip_openssl: 6: > 1.1.1w -=== TEST 18: cosocket (UDP) +=== TEST 17: cosocket (UDP) --- http_config ssl_session_fetch_by_lua_block { local sock = ngx.socket.udp() @@ -1501,6 +1455,7 @@ qr/elapsed in ssl_session_fetch_by_lua\*: 0\.(?:09|1[01])\d+,/, --- config server_tokens off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { content_by_lua_block { @@ -1537,7 +1492,7 @@ qr/elapsed in ssl_session_fetch_by_lua\*: 0\.(?:09|1[01])\d+,/, GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval: qr/received memc reply of \d+ bytes/ --- grep_error_log_out eval @@ -1552,10 +1507,11 @@ close: 1 nil [alert] [error] [emerg] +--- skip_openssl: 6: > 1.1.1w -=== TEST 19: uthread (kill) +=== TEST 18: uthread (kill) --- http_config ssl_session_fetch_by_lua_block { local function f() @@ -1596,6 +1552,7 @@ close: 1 nil --- config server_tokens off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { content_by_lua_block { @@ -1632,7 +1589,7 @@ close: 1 nil GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval: qr/uthread: [^.,]+/ --- grep_error_log_out eval @@ -1654,7 +1611,7 @@ uthread: failed to kill: already waited or killed -=== TEST 20: uthread (wait) +=== TEST 19: uthread (wait) --- http_config ssl_session_fetch_by_lua_block { local function f() @@ -1696,6 +1653,7 @@ uthread: failed to kill: already waited or killed --- config server_tokens off; lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt; + lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2; location /t { content_by_lua_block { @@ -1732,7 +1690,7 @@ uthread: failed to kill: already waited or killed GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata close: 1 nil --- grep_error_log eval: qr/uthread: [^.,]+/ --- grep_error_log_out eval @@ -1751,3 +1709,4 @@ uthread: failed to kill: already waited or killed [alert] [error] [emerg] +--- skip_openssl: 6: > 1.1.1w diff --git a/t/145-shdict-list.t b/t/145-shdict-list.t index 9bb1592606..5672adbc55 100644 --- a/t/145-shdict-list.t +++ b/t/145-shdict-list.t @@ -743,3 +743,111 @@ GET /test done --- no_error_log [error] + + + +=== TEST 20: push to an expired list +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + local len, err = dogs:lpush("cc", "1") --add another list to avoid key"aa" be cleaned (run ‘ngx_http_lua_shdict_expire(ctx, 1)’ may clean key ,ensure key'aa' not clean ,just expired)) + if not len then + ngx.say("push cc err: ", err) + end + local len, err = dogs:lpush("aa", "1") + if not len then + ngx.say("push1 err: ", err) + end + local succ, err = dogs:expire("aa", 0.2) + if not succ then + ngx.say("expire err: ",err) + end + ngx.sleep(0.3) -- list aa expired + local len, err = dogs:lpush("aa", "2") --push to an expired list may set as a new list + if not len then + ngx.say("push2 err: ", err) + end + local len, err = dogs:llen("aa") -- new list len is 1 + if not len then + ngx.say("llen err: ", err) + else + ngx.say("aa:len :", dogs:llen("aa")) + end + } + } + +--- request +GET /test +--- response_body +aa:len :1 +--- no_error_log +[error] + + + +=== TEST 21: push to an expired list then pop many time (more then list len ) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + local len, err = dogs:lpush("cc", "1") --add another list to avoid key"aa" be cleaned (run ‘ngx_http_lua_shdict_expire(ctx, 1)’ may clean key ,ensure key'aa' not clean ,just expired)) + if not len then + ngx.say("push cc err: ", err) + end + local len, err = dogs:lpush("aa", "1") + if not len then + ngx.say("push1 err: ", err) + end + local succ, err = dogs:expire("aa", 0.2) + if not succ then + ngx.say("expire err: ",err) + end + ngx.sleep(0.3) -- list aa expired + local len, err = dogs:lpush("aa", "2") --push to an expired list may set as a new list + if not len then + ngx.say("push2 err: ", err) + end + local val, err = dogs:lpop("aa") + if not val then + ngx.say("llen err: ", err) + end + local val, err = dogs:lpop("aa") -- val == nil + ngx.say("aa list value: ", val) + } + } + +--- request +GET /test +--- response_body +aa list value: nil +--- no_error_log +[error] + + + +=== TEST 22: lpush return nil +--- http_config + lua_shared_dict dogs 100k; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + for i = 1, 2920 + do + local len, err = dogs:lpush("foo", "bar") + end + local len, err = dogs:lpush("foo", "bar") + ngx.say(len) + } + } +--- request +GET /test +--- response_body +nil +--- no_error_log +[error] diff --git a/t/147-tcp-socket-timeouts.t b/t/147-tcp-socket-timeouts.t index 8566c0ba6f..ceddb61dcc 100644 --- a/t/147-tcp-socket-timeouts.t +++ b/t/147-tcp-socket-timeouts.t @@ -17,6 +17,7 @@ BEGIN { } $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + delete($ENV{TEST_NGINX_USE_HTTP2}); $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'slowdata'; } diff --git a/t/151-initby-hup.t b/t/151-initby-hup.t index f2788670db..a37db9fe43 100644 --- a/t/151-initby-hup.t +++ b/t/151-initby-hup.t @@ -12,7 +12,7 @@ BEGIN { } } -use Test::Nginx::Socket::Lua 'no_plan'; +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); #worker_connections(1014); #master_on(); @@ -21,7 +21,7 @@ use Test::Nginx::Socket::Lua 'no_plan'; repeat_each(1); -#plan tests => repeat_each() * (blocks() * 3 + 3); +plan tests => repeat_each() * (blocks() * 3); #no_diff(); #no_long_string(); diff --git a/t/152-timer-every.t b/t/152-timer-every.t index 7b42d2d724..374e80e04b 100644 --- a/t/152-timer-every.t +++ b/t/152-timer-every.t @@ -224,6 +224,7 @@ registered timer === TEST 6: memory leak check +--- quic_max_idle_timeout: 8 --- config location /t { content_by_lua_block { diff --git a/t/153-semaphore-hup.t b/t/153-semaphore-hup.t index 5c271a48e7..496db6862e 100644 --- a/t/153-semaphore-hup.t +++ b/t/153-semaphore-hup.t @@ -104,6 +104,7 @@ run_tests(); __DATA__ === TEST 1: timer + reload +--- quic_max_idle_timeout: 1.1 --- config location /test { content_by_lua_block { @@ -129,6 +130,7 @@ created semaphore object === TEST 2: timer + reload (lua code cache off) +--- quic_max_idle_timeout: 1.1 --- http_config lua_code_cache off; --- config diff --git a/t/154-semaphore.t b/t/154-semaphore.t index c7b00e03f6..5bb2b937af 100644 --- a/t/154-semaphore.t +++ b/t/154-semaphore.t @@ -118,3 +118,53 @@ hello, world --- no_shutdown_error_log semaphore gc wait queue is not empty --- SKIP + + + +=== TEST 3: exit before post_handler was called +If gc is called before the ngx_http_lua_sema_handler and free the sema memory +ngx_http_lua_sema_handler would use the freed memory. +--- config + location /up { + content_by_lua_block { + local semaphore = require "ngx.semaphore" + local sem = semaphore.new() + + local function sem_wait() + ngx.log(ngx.ERR, "ngx.sem wait start") + local ok, err = sem:wait(10) + if not ok then + ngx.log(ngx.ERR, "ngx.sem wait err: ", err) + else + ngx.log(ngx.ERR, "ngx.sem wait success") + end + end + local co = ngx.thread.spawn(sem_wait) + ngx.log(ngx.ERR, "ngx.sem post start") + sem:post() + ngx.log(ngx.ERR, "ngx.sem post end") + ngx.say("hello") + ngx.exit(200) + ngx.say("not reach here") + } + } + + location /t { + content_by_lua_block { + local res = ngx.location.capture("/up") + collectgarbage() + ngx.print(res.body) + } + } + +--- request +GET /t +--- response_body +hello +--- grep_error_log eval: qr/(ngx.sem .*?,|http close request|semaphore handler: wait queue: empty, resource count: 1|in lua gc, semaphore)/ +--- grep_error_log_out +ngx.sem wait start, +ngx.sem post start, +ngx.sem post end, +in lua gc, semaphore +http close request diff --git a/t/155-tls13.t b/t/155-tls13.t index 9f7cf86d2e..54379e4412 100644 --- a/t/155-tls13.t +++ b/t/155-tls13.t @@ -9,7 +9,7 @@ my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; my $openssl_version = eval { `$NginxBinary -V 2>&1` }; if ($openssl_version =~ m/built with OpenSSL (0\S*|1\.0\S*|1\.1\.0\S*)/) { - plan(skip_all => "too old OpenSSL, need 1.1.1, was $1"); + plan(skip_all => "too old OpenSSL, need >= 1.1.1, was $1"); } else { plan tests => repeat_each() * (blocks() * 5); } @@ -91,7 +91,7 @@ __DATA__ GET /t --- response_body connected: 1 -ssl handshake: userdata +ssl handshake: cdata --- user_files eval ">>> test.key diff --git a/t/156-slow-network.t b/t/156-slow-network.t index 2d80506085..d5897fb33e 100644 --- a/t/156-slow-network.t +++ b/t/156-slow-network.t @@ -15,6 +15,7 @@ BEGIN { } $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; + delete($ENV{TEST_NGINX_USE_HTTP2}); } use Test::Nginx::Socket::Lua; diff --git a/t/157-socket-keepalive-hup.t b/t/157-socket-keepalive-hup.t index 357bb59682..4ebf637a33 100644 --- a/t/157-socket-keepalive-hup.t +++ b/t/157-socket-keepalive-hup.t @@ -6,6 +6,10 @@ BEGIN { if ($ENV{TEST_NGINX_CHECK_LEAK}) { $SkipReason = "unavailable for the hup tests"; + } elsif (defined $ENV{TEST_NGINX_USE_HTTP3}) { + #os.execute("kill -HUP " .. pid) + $SkipReason = "send HUP relaod signal by self make two workers with same id"; + } else { $ENV{TEST_NGINX_USE_HUP} = 1; undef $ENV{TEST_NGINX_USE_STAP}; @@ -32,7 +36,8 @@ __DATA__ set $port $TEST_NGINX_SERVER_PORT; content_by_lua_block { - local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + local pidfile = ngx.config.prefix() .. "/logs/nginx.pid" + local f, err = io.open(pidfile, "r") if not f then ngx.say("failed to open nginx.pid: ", err) return diff --git a/t/158-global-var.t b/t/158-global-var.t index 258ccdbce0..3f5db60fa2 100644 --- a/t/158-global-var.t +++ b/t/158-global-var.t @@ -60,10 +60,10 @@ __DATA__ --- response_body_like chomp \A[12]\n\z --- grep_error_log eval -qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|set_by_lua:\d+: in main chunk, )/ +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|set_by_lua\(nginx.conf:\d+\):\d+: in main chunk, )/ --- grep_error_log_out eval [qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) -set_by_lua:3: in main chunk, \n\z/, "old foo: 1\n"] +set_by_lua\(nginx.conf:40\):3: in main chunk, \n\z/, "old foo: 1\n"] @@ -86,7 +86,7 @@ set_by_lua:3: in main chunk, \n\z/, "old foo: 1\n"] 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'\) -rewrite_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] +rewrite_by_lua\(nginx\.conf:\d+\):\d+: in main chunk, \n\z/, "old foo: 1\n"] @@ -109,7 +109,7 @@ rewrite_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] 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'\) -access_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] +access_by_lua\(nginx\.conf:\d+\):\d+: in main chunk, \n\z/, "old foo: 1\n"] @@ -132,7 +132,7 @@ access_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] 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'\) -content_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] +content_by_lua\(nginx\.conf:\d+\):\d+: in main chunk, \n\z/, "old foo: 1\n"] @@ -154,10 +154,10 @@ content_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] --- response_body_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\(nginx\.conf:\d+\):\d+: in main chunk, )/ --- grep_error_log_out eval [qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) -header_filter_by_lua:3: in main chunk, \n\z/, "old foo: 1\n"] +header_filter_by_lua\(nginx.conf:43\):3: in main chunk, \n\z/, "old foo: 1\n"] @@ -179,10 +179,10 @@ header_filter_by_lua:3: in main chunk, \n\z/, "old foo: 1\n"] --- response_body_like chomp \A(?:nil|2)\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\(nginx\.conf:\d+\):\d+: in main chunk,)/ --- grep_error_log_out eval [qr/\[warn\] .*?writing a global Lua variable \('foo'\) -body_filter_by_lua:3: in main chunk, +body_filter_by_lua\(nginx.conf:43\):3: in main chunk, old foo: 1\n\z/, "old foo: 2\nold foo: 3\n"] @@ -208,7 +208,7 @@ old foo: 1\n\z/, "old foo: 2\nold foo: 3\n"] 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'\) -log_by_lua\(nginx\.conf:50\):3: in main chunk\n\z/, "old foo: 1\n"] +log_by_lua\(nginx\.conf:\d+\):\d+: in main chunk\n\z/, "old foo: 1\n"] @@ -297,10 +297,10 @@ log_by_lua\(nginx\.conf:50\):3: in main chunk\n\z/, "old foo: 1\n"] --- response_body_like chomp \A[12]done\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\(nginx.conf:\d+\):\d+: in main chunk)/ --- grep_error_log_out eval [qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) -ssl_certificate_by_lua:3: in main chunk\n\z/, "old foo: 1\n"] +ssl_certificate_by_lua\(nginx.conf:28\):3: in main chunk\n\z/, "old foo: 1\n"] @@ -331,7 +331,7 @@ ssl_certificate_by_lua:3: in main chunk\n\z/, "old foo: 1\n"] 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'\) -content_by_lua\(nginx\.conf:56\):4: in\n\z/, "old foo: 1\n"] +content_by_lua\(nginx\.conf:\d+\):\d+: in\n\z/, "old foo: 1\n"] diff --git a/t/160-disable-init-by-lua.t b/t/160-disable-init-by-lua.t index 541771e1b5..e98c136dcf 100644 --- a/t/160-disable-init-by-lua.t +++ b/t/160-disable-init-by-lua.t @@ -12,6 +12,9 @@ my $http_config = <<_EOC_; function set_up_ngx_tmp_conf(conf) if conf == nil then conf = [[ + # to prevent the test process from overwriting the + # original pid file + pid logs/test_nginx.pid; events { worker_connections 64; } @@ -33,6 +36,7 @@ my $http_config = <<_EOC_; end assert(f:write(conf)) + f:close() return conf_file end @@ -85,6 +89,8 @@ __DATA__ else ngx.log(ngx.WARN, out) end + p:close() + collectgarbage("collect") } } --- error_log @@ -115,6 +121,7 @@ qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ else ngx.log(ngx.WARN, out) end + p:close() local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -T" local p, err = io.popen(cmd) @@ -130,6 +137,8 @@ qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ else ngx.log(ngx.WARN, out) end + p:close() + collectgarbage("collect") } } --- error_log @@ -144,6 +153,7 @@ qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ location = /t { content_by_lua_block { local conf = [[ + pid logs/test_nginx.pid; events { worker_connections 64; } @@ -171,6 +181,7 @@ qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ else ngx.log(ngx.WARN, out) end + p:close() local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -T" local p, err = io.popen(cmd) @@ -186,6 +197,8 @@ qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ else ngx.log(ngx.WARN, out) end + p:close() + collectgarbage("collect") } } --- error_log diff --git a/t/162-exit-worker.t b/t/162-exit-worker.t index ef2082f3b4..7aff2a619b 100644 --- a/t/162-exit-worker.t +++ b/t/162-exit-worker.t @@ -5,7 +5,8 @@ use Test::Nginx::Socket::Lua; master_on(); repeat_each(2); -plan tests => repeat_each() * (blocks() * 2 + 2) + 12; +# NB: the shutdown_error_log block is independent from repeat times +plan tests => repeat_each() * (blocks() * 2 + 1) + 15; #log_level("warn"); no_long_string(); @@ -79,11 +80,11 @@ hello, world -=== TEST 4: exit_worker_by_lua single process ngx.timer not work +=== TEST 4: ngx.timer is not allow --- http_config exit_worker_by_lua_block { local function bar() - ngx.log(ngx.ERR, "run the timer!" + ngx.log(ngx.ERR, "run the timer!") end local ok, err = ngx.timer.at(0, bar) @@ -101,8 +102,8 @@ hello, world GET /t --- response_body ok ---- no_error_log -[error] +--- shutdown_error_log +API disabled in the context of exit_worker_by_lua* @@ -193,3 +194,49 @@ qr/cache loader process \d+ exited/, qr/cache manager process \d+ exited/, qr/hello from exit worker by lua, process type: worker/, ] + + + +=== TEST 8: syntax error in exit_worker_by_lua_block +--- http_config + exit_worker_by_lua_block { + ngx.log(ngx.debug, "pass") + error("failed to init" + ngx.log(ngx.debug, "unreachable") + } +--- config + location /t { + content_by_lua_block { + ngx.say("hello world") + } + } +--- request + GET /t +--- response_body +hello world +--- shutdown_error_log +exit_worker_by_lua error: exit_worker_by_lua(nginx.conf:25):4: ')' expected (to close '(' at line 3) near 'ngx' + + + +=== TEST 9: syntax error in exit_worker_by_lua_file +--- http_config + exit_worker_by_lua_file html/exit.lua; +--- config + location /t { + content_by_lua_block { + ngx.say("hello world") + } + } +--- user_files +>>> exit.lua + ngx.log(ngx.debug, "pass") + error("failed to init" + ngx.log(ngx.debug, "unreachable") + +--- request + GET /t +--- response_body +hello world +--- shutdown_error_log eval +qr|exit_worker_by_lua_file error: .*?t/servroot\w*/html/exit.lua:3: '\)' expected \(to close '\(' at line 2\) near 'ngx'| diff --git a/t/162-socket-tls-handshake.t b/t/162-socket-tls-handshake.t new file mode 100644 index 0000000000..19ad67a749 --- /dev/null +++ b/t/162-socket-tls-handshake.t @@ -0,0 +1,373 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => repeat_each() * 43; + +$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; + +log_level 'debug'; + +no_long_string(); +#no_diff(); + +sub read_file { + my $infile = shift; + open my $in, $infile + or die "cannot open $infile for reading: $!"; + my $cert = do { local $/; <$in> }; + close $in; + $cert; +} + +our $MTLSCA = read_file("t/cert/mtls_ca.crt"); +our $MTLSClient = read_file("t/cert/mtls_client.crt"); +our $MTLSClientKey = read_file("t/cert/mtls_client.key"); +our $MTLSServer = read_file("t/cert/mtls_server.crt"); +our $MTLSServerKey = read_file("t/cert/mtls_server.key"); + +our $HtmlDir = html_dir; + +our $mtls_http_config = <<"_EOC_"; +server { + listen unix:$::HtmlDir/mtls.sock ssl; + + ssl_certificate $::HtmlDir/mtls_server.crt; + ssl_certificate_key $::HtmlDir/mtls_server.key; + ssl_client_certificate $::HtmlDir/mtls_ca.crt; + ssl_verify_client on; + server_tokens off; + + location / { + return 200 "hello, \$ssl_client_s_dn"; + } +} +_EOC_ + +our $mtls_user_files = <<"_EOC_"; +>>> mtls_server.key +$::MTLSServerKey +>>> mtls_server.crt +$::MTLSServer +>>> mtls_ca.crt +$::MTLSCA +>>> mtls_client.key +$::MTLSClientKey +>>> mtls_client.crt +$::MTLSClient +_EOC_ + +run_tests(); + +__DATA__ + +=== TEST 1: sanity: www.google.com +--- config + server_tokens off; + resolver $TEST_NGINX_RESOLVER ipv6=off; + + location /t { + content_by_lua_block { + -- avoid flushing bing in "check leak" testing mode: + local counter = package.loaded.counter + if not counter then + counter = 1 + elseif counter >= 2 then + return ngx.exit(503) + else + counter = counter + 1 + end + + package.loaded.counter = counter + + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("www.google.com", 443) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake() + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + + ngx.say("received: ", line) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + + collectgarbage() + } + } +--- request +GET /t +--- response_body_like chop +\Aconnected: 1 +ssl handshake: cdata +sent http request: 59 bytes. +received: HTTP/1.1 (?:200 OK|302 Found) +close: 1 nil +\z +--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ +--- grep_error_log_out eval +qr/^lua ssl save session: ([0-9A-F]+) +lua ssl free session: ([0-9A-F]+) +$/ +--- no_error_log +lua ssl server name: +SSL reused session +[error] +[alert] +--- timeout: 5 + + + +=== TEST 2: mutual TLS handshake, upstream is not accessible without client certs +--- http_config eval: $::mtls_http_config +--- config eval +" + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect('unix:$::HtmlDir/mtls.sock') + if not ok then + ngx.say('failed to connect: ', err) + end + + assert(sock:sslhandshake()) + + ngx.say('connected: ', ok) + + local req = 'GET /\\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) + + ngx.say(sock:receive('*a')) + + assert(sock:close()) + } + } +" +--- user_files eval: $::mtls_user_files +--- request +GET /t +--- response_body_like: 400 No required SSL certificate was sent +--- no_error_log +[alert] +[error] +[crit] +[emerg] + + + +=== TEST 3: mutual TLS handshake, upstream is accessible with client certs +--- http_config eval: $::mtls_http_config +--- config eval +" + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect('unix:$::HtmlDir/mtls.sock') + if not ok then + ngx.say('failed to connect: ', err) + end + + local f = assert(io.open('$::HtmlDir/mtls_client.crt')) + local cert_data = f:read('*a') + f:close() + + f = assert(io.open('$::HtmlDir/mtls_client.key')) + local key_data = f:read('*a') + f:close() + + local ssl = require('ngx.ssl') + + local chain = assert(ssl.parse_pem_cert(cert_data)) + local priv = assert(ssl.parse_pem_priv_key(key_data)) + + sock:setclientcert(chain, priv) + + assert(sock:sslhandshake()) + + ngx.say('connected: ', ok) + + local req = 'GET /\\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) + + ngx.say(sock:receive('*a')) + + assert(sock:close()) + } + } +" +--- user_files eval: $::mtls_user_files +--- request +GET /t +--- response_body +connected: 1 +request sent: 7 +hello, CN=foo@example.com,O=OpenResty,ST=California,C=US +--- no_error_log +[alert] +[error] +[crit] +[emerg] + + + +=== TEST 4: incorrect type of client cert +--- config + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + + local ok, err = sock:setclientcert("doesnt", "work") + if not ok then + ngx.say('failed to setclientcert: ', err) + return + end + + assert(sock:close()) + } + } +--- request +GET /t +--- response_body +failed to setclientcert: bad cert arg: cdata expected, got string +--- no_error_log +[alert] +[error] +[crit] +[emerg] + + + +=== TEST 5: incorrect type of client key +--- config eval +" + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + + local f = assert(io.open('$::HtmlDir/mtls_client.crt')) + local cert_data = f:read('*a') + f:close() + + local ssl = require('ngx.ssl') + + local chain = assert(ssl.parse_pem_cert(cert_data)) + + local ok, err = sock:setclientcert(chain, 'work') + if not ok then + ngx.say('failed to setclientcert: ', err) + return + end + + assert(sock:close()) + } + } +" +--- user_files eval: $::mtls_user_files +--- request +GET /t +--- response_body +failed to setclientcert: bad pkey arg: cdata expected, got string +--- no_error_log +[alert] +[error] +[crit] +[emerg] + + + +=== TEST 6: missing client cert +--- config + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + + local ok, err = sock:setclientcert(nil, "work") + if not ok then + ngx.say('failed to setclientcert: ', err) + return + end + + assert(sock:close()) + } + } +--- request +GET /t +--- response_body +failed to setclientcert: client certificate must be supplied with corresponding private key +--- no_error_log +[alert] +[error] +[crit] +[emerg] + + + +=== TEST 7: missing private key +--- config + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + + local ok, err = sock:setclientcert('doesnt', nil) + if not ok then + ngx.say('failed to setclientcert: ', err) + return + end + + assert(sock:close()) + } + } +--- request +GET /t +--- response_body +failed to setclientcert: client certificate must be supplied with corresponding private key +--- no_error_log +[alert] +[error] +[crit] +[emerg] diff --git a/t/162-static-module-location.t b/t/162-static-module-location.t index 222760c188..9f09dbe631 100644 --- a/t/162-static-module-location.t +++ b/t/162-static-module-location.t @@ -40,9 +40,11 @@ Location: %00%0A%0Dset-cookie:1234567/ Hello, world --- request GET /t/中文 +--- more_headers +host: localhost --- error_code: 301 ---- response_headers -Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/ +--- response_headers_like +Location: https?:\/\/localhost:\d+\/t\/%E4%B8%AD%E6%96%87\/ --- response_body_like .*301 Moved Permanently.* @@ -56,9 +58,11 @@ Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/ Hello, world --- request GET /t/中文?q=name +--- more_headers +host: localhost --- error_code: 301 ---- response_headers -Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/?q=name +--- response_headers_like +Location: https?:\/\/localhost:\d+\/t\/%E4%B8%AD%E6%96%87\/\?q=name --- response_body_like .*301 Moved Permanently.* @@ -72,9 +76,11 @@ Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/?q=name Hello, world --- request GET /t/%E4%B8%AD%E6%96%87 +--- more_headers +host: localhost --- error_code: 301 ---- response_headers -Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/ +--- response_headers_like +Location: https?:\/\/localhost:\d+\/t\/%E4%B8%AD%E6%96%87\/ --- response_body_like .*301 Moved Permanently.* @@ -88,8 +94,10 @@ Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/ Hello, world --- request GET /t/%E4%B8%AD%E6%96%87?q=name +--- more_headers +host: localhost --- error_code: 301 ---- response_headers -Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/?q=name +--- response_headers_like +Location: https?://localhost:\d+\/t\/%E4%B8%AD%E6%96%87\/\?q=name --- response_body_like .*301 Moved Permanently.* diff --git a/t/163-exit-worker-hup.t b/t/163-exit-worker-hup.t index a54d81db14..136efdc7ff 100644 --- a/t/163-exit-worker-hup.t +++ b/t/163-exit-worker-hup.t @@ -6,6 +6,9 @@ BEGIN { if ($ENV{TEST_NGINX_CHECK_LEAK}) { $SkipReason = "unavailable for the hup tests"; + } elsif ($ENV{TEST_NGINX_USE_HTTP3}) { + $SkipReason = "http3 does not support hub reload"; + } else { $ENV{TEST_NGINX_USE_HUP} = 1; undef $ENV{TEST_NGINX_USE_STAP}; @@ -54,7 +57,7 @@ log from exit_worker_by_lua_block } server { - listen 12345; + listen $TEST_NGINX_RAND_PORT_1; location = /t { echo 'hello world'; @@ -65,7 +68,7 @@ log from exit_worker_by_lua_block content_by_lua_block { ngx.timer.at(0, function () local sock = ngx.socket.tcp() - sock:connect("127.0.0.1", 12345) + sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) local reader = sock:receiveuntil("unknow") ngx.log(ngx.NOTICE, "reading to block the exiting") reader() diff --git a/t/163-signal.t b/t/163-signal.t index 5a6b880b09..a0c2ee8082 100644 --- a/t/163-signal.t +++ b/t/163-signal.t @@ -5,6 +5,9 @@ our $SkipReason; BEGIN { if ($ENV{TEST_NGINX_USE_HUP}) { $SkipReason = "unavailable under hup test mode"; + + } elsif ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable under check leak test mode"; } } @@ -38,6 +41,8 @@ GET /t qr/\[notice\] \d+#\d+: exit$/ --- no_error_log eval qr/\[notice\] \d+#\d+: reconfiguring/ +--- curl_error eval +qr/curl: \(28\) Operation timed out after \d+ milliseconds with 0 bytes received|curl: \(56\) Recv failure: Connection reset by peer|curl: \(56\) Failure when receiving data from the peer|curl: \(55\) sendmsg\(\) returned -1 \(errno 111\)/ diff --git a/t/166-ssl-client-hello.t b/t/166-ssl-client-hello.t new file mode 100644 index 0000000000..4372d0d1f8 --- /dev/null +++ b/t/166-ssl-client-hello.t @@ -0,0 +1,3453 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL (0\S*|1\.0\S*|1\.1\.0\S*)/) { + plan(skip_all => "too old OpenSSL, need >= 1.1.1, was $1"); +} elsif ($openssl_version =~ m/running with BoringSSL/) { + plan(skip_all => "does not support BoringSSL"); +} elsif ($ENV{TEST_NGINX_USE_HTTP3}) { + plan tests => repeat_each() * (blocks() * 6 + 6); +} else { + plan tests => repeat_each() * (blocks() * 6 + 10); +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: simple logging +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/ssl_client_hello_by_lua\(.*?,|\bssl client hello: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log_out eval +# Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0) +# before call ssl callback function +$Test::Nginx::Util::NginxVersion >= 1.017009 ? +qr/reusable connection: 0 +ssl client hello: connection reusable: 0 +ssl_client_hello_by_lua\(nginx.conf:\d+\):1: ssl client hello by lua is running!,/ +: qr /reusable connection: 1 +ssl client hello: connection reusable: 1 +reusable connection: 0 +ssl_client_hello_by_lua\(nginx.conf:\d+\):1: ssl client hello by lua is running!,/ + + + +=== TEST 2: sleep +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in ssl client hello by lua: ", ngx.now() - begin) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log eval +[ +'lua ssl server name: "test.com"', +qr/elapsed in ssl client hello by lua: 0.(?:09|1\d)\d+,/, +] + +--- no_error_log +[error] +[alert] + + + +=== TEST 3: timer +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + local function f() + print("my timer run!") + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +my timer run! + +--- no_error_log +[error] +[alert] + + + +=== TEST 4: cosocket +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send flush_all command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("received memc reply: ", res) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +received memc reply: OK + +--- no_error_log +[error] +[alert] + + + +=== TEST 5: ngx.exit(0) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + ngx.exit(0) + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 6: ngx.exit(ngx.ERROR) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'ssl_client_hello_by_lua: handler return value: -1, client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 7: ngx.exit(0) - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + ngx.exit(0) + + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 8: ngx.exit(ngx.ERROR) - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + ngx.exit(ngx.ERROR) + + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'ssl_client_hello_by_lua: client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 9: lua exception - no yield +--- http_config + server { + listen 127.0.0.2:$TEST_NGINX_RAND_PORT_2 ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_2) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_client_hello_by_lua(nginx.conf:28):2: bad bad bad', +'ssl_client_hello_by_lua: handler return value: 500, client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +qr/context: ssl_client_hello_by_lua\*, client: \d+\.\d+\.\d+\.\d+, server: \d+\.\d+\.\d+\.\d+:\d+/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 10: lua exception - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_client_hello_by_lua(nginx.conf:28):3: bad bad bad', +'ssl_client_hello_by_lua: client hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 11: get phase +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block {print("get_phase: ", ngx.get_phase())} + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end + collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata + +--- error_log +lua ssl server name: "test.com" +get_phase: ssl_client_hello + +--- no_error_log +[error] +[alert] + + + +=== TEST 12: connection aborted prematurely +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + ngx.sleep(0.3) + print("ssl-client-hello-by-lua: after sleeping") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(150) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t + +--- response_body +connected: 1 +failed to do SSL handshake: timeout + +--- error_log +lua ssl server name: "test.com" +ssl-client-hello-by-lua: after sleeping + +--- no_error_log +[error] +[alert] +--- wait: 0.6 + + + +=== TEST 13: subrequests disabled +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block {ngx.location.capture("/foo")} + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua ssl server name: "test.com"', +'ssl_client_hello_by_lua(nginx.conf:28):1: API disabled in the context of ssl_client_hello_by_lua*', +qr/\[info\] .*?callback failed/, +] + +--- no_error_log +[alert] + + + +=== TEST 14: simple logging (by_lua_file) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_file html/a.lua; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } + +--- user_files +>>> a.lua +print("ssl client hello by lua is running!") + +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +a.lua:1: ssl client hello by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 15: coroutine API +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function f() + local cnt = 0 + for i = 1, 20 do + print("co yield: ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i = 1, 3 do + print("co resume, status: ", coroutine.status(c)) + cr(c) + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- grep_error_log eval: qr/co (?:yield: \d+|resume, status: \w+)/ +--- grep_error_log_out +co resume, status: suspended +co yield: 0 +co resume, status: suspended +co yield: 1 +co resume, status: suspended +co yield: 2 + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 16: simple user thread wait with yielding +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + local function f() + ngx.sleep(0.01) + print("uthread: hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "uthread: failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + print("uthread: thread created: ", coroutine.status(t)) + + local ok, res = ngx.thread.wait(t) + if not ok then + print("uthread: failed to wait thread: ", res) + return + end + + print("uthread: ", res) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: thread created: running +uthread: hello in thread +uthread: done + + + +=== TEST 17: simple logging - use ssl_client_hello_by_lua* on the http {} level +GitHub openresty/lua-resty-core#42 +--- http_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_hello_by_lua(nginx.conf:25):1: ssl client hello by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 18: simple logging - use ssl_client_hello_by_lua* on the http {} level and server {} level +--- http_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua on the http level is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { print("ssl client hello by lua on the server level is running!") } + server_name test.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_hello_by_lua(nginx.conf:31):1: ssl client hello by lua on the server level is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 19: use ssl_client_hello_by_lua* on the server {} level with non-ssl server +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server_name test.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- no_error_log +ssl client hello by lua is running! +[error] +[alert] + + + +=== TEST 20: use ssl_client_hello_by_lua* on the http {} level with non-ssl server +--- http_config + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + server_name test.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- no_error_log +ssl client hello by lua is running! +[error] +[alert] + + + +=== TEST 21: listen two ports (one for ssl and one for non-ssl) in one server - connect ssl port +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server_name test.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_hello_by_lua(nginx.conf:28):1: ssl client hello by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 22: listen two ports (one for ssl and one for non-ssl) in one server - connect non-ssl port +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server_name test.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx2.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + + +--- no_error_log +ssl client hello by lua is running! +[error] +[alert] + + + +=== TEST 23: simple logging - use ssl_client_hello_by_lua* in multiple virtual servers +--- http_config + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { print("ssl client hello by lua in server1 is running!") } + server_name test.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_client_hello_by_lua_block { print("ssl client hello by lua in server2 is running!") } + server_name test2.com; + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo2") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test2.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 57 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 5 +received: Connection: close +received: +received: foo2 +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_hello_by_lua(nginx.conf:29):1: ssl client hello by lua in server1 is running! + +--- no_error_log +ssl client hello by lua in server2 is running! +[error] +[alert] + + + +=== TEST 24: simple logging (syslog) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + error_log syslog:server=127.0.0.1:12345 debug; + + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log eval +[ +qr/\[error\] .*? send\(\) failed/, +'lua ssl server name: "test.com"', +] +--- no_error_log +[alert] +ssl client hello by lua is running! + + + +=== TEST 25: get raw_client_addr - IPv4 +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + server { + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1 ssl; + server_name test.com; + + ssl_client_hello_by_lua_block { + local ssl = require "ngx.ssl" + local byte = string.byte + local addr, addrtype, err = ssl.raw_client_addr() + local ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2), + byte(addr, 3), byte(addr, 4)) + print("client ip: ", ip) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +client ip: 127.0.0.1 + +--- no_error_log +[error] +[alert] + + + +=== TEST 26: get raw_client_addr - unix domain socket +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_client_hello_by_lua_block { + local ssl = require "ngx.ssl" + local addr, addrtyp, err = ssl.raw_client_addr() + print("client socket file: ", addr) + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +client socket file: + +--- no_error_log +[error] +[alert] + + + +=== TEST 27: ssl_client_hello_by_lua* can yield when reading early data +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_early_data on; + server_tokens off; + + ssl_client_hello_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in ssl_client_hello_by_lua*: ", ngx.now() - begin) + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: boolean +--- grep_error_log eval +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1\d)\d+,/, +--- grep_error_log_out eval +[ +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1\d)\d+,/, +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1\d)\d+,/, +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1\d)\d+,/, +] +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 28: cosocket (UDP) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server_tokens off; + + ssl_client_hello_by_lua_block { + local sock = ngx.socket.udp() + + sock:settimeout(1000) + + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = sock:send(req) + if not ok then + ngx.log(ngx.ERR, "failed to send flush_all to memc: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + ngx.log(ngx.INFO, "received memc reply of ", #res, " bytes") + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + } +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +--- no_error_log +[error] +[alert] +[emerg] +--- grep_error_log eval: qr/received memc reply of \d+ bytes/ +--- grep_error_log_out eval +[ +'received memc reply of 12 bytes +', +'received memc reply of 12 bytes +', +'received memc reply of 12 bytes +', +'received memc reply of 12 bytes +', +] + + + +=== TEST 29: uthread (kill) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + server_tokens off; + + ssl_client_hello_by_lua_block { + local function f() + ngx.log(ngx.INFO, "uthread: hello from f()") + ngx.sleep(1) + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + local ok, res = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.ERR, "failed to kill thread: ", res) + return + end + + ngx.log(ngx.INFO, "uthread: killed") + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.INFO, "uthread: failed to kill: ", err) + end + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + } +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +--- no_error_log +[error] +[alert] +[emerg] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: hello from f() +uthread: killed +uthread: failed to kill: already waited or killed + + + +=== TEST 30: ngx.exit(ngx.OK) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_client_hello_by_lua_block { + ngx.exit(ngx.OK) + ngx.log(ngx.ERR, "should never reached here...") + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: boolean + +--- error_log eval +[ +'ssl_client_hello_by_lua: handler return value: 0, client hello cb exit code: 1', +qr/\[debug\] .*? SSL_do_handshake: 1/, +'lua exit with code 0', +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 31: ssl_client_hello_by_lua* can yield when reading early data +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello by lua is running!") + ngx.sleep(0.1) + print("ssl client hello by lua is done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello by lua is running!|ssl client hello by lua is done|test completed)/ +--- grep_error_log_out +ssl client hello by lua is running! +ssl client hello by lua is done +test completed +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 32: ssl_client_hello_by_lua* with TCP cosocket (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello: TCP cosocket test start") + + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send flush_all command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("ssl client hello: received TCP memc reply: ", res) + sock:close() + print("ssl client hello: TCP cosocket test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello: TCP cosocket test start|ssl client hello: received TCP memc reply: OK|ssl client hello: TCP cosocket test done|test completed)/ +--- grep_error_log_out +ssl client hello: TCP cosocket test start +ssl client hello: received TCP memc reply: OK +ssl client hello: TCP cosocket test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 33: ssl_client_hello_by_lua* with UDP cosocket (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello: UDP cosocket test start") + + local sock = ngx.socket.udp() + sock:settimeout(1000) + + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = sock:send(req) + if not ok then + ngx.log(ngx.ERR, "failed to send flush_all to memc: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("ssl client hello: received UDP memc reply of ", #res, " bytes") + sock:close() + print("ssl client hello: UDP cosocket test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello: UDP cosocket test start|ssl client hello: received UDP memc reply of \d+ bytes|ssl client hello: UDP cosocket test done|test completed)/ +--- grep_error_log_out +ssl client hello: UDP cosocket test start +ssl client hello: received UDP memc reply of 12 bytes +ssl client hello: UDP cosocket test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 34: ssl_client_hello_by_lua* with ngx.timer (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello: timer test start") + + local function timer_handler() + print("ssl client hello: timer executed") + end + + local ok, err = ngx.timer.at(0, timer_handler) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + + print("ssl client hello: timer created") + print("ssl client hello: timer test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello: timer test start|ssl client hello: timer created|ssl client hello: timer test done|ssl client hello: timer executed|test completed)/ +--- grep_error_log_out +ssl client hello: timer test start +ssl client hello: timer created +ssl client hello: timer test done +ssl client hello: timer executed +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 35: ssl_client_hello_by_lua* with user threads (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello: uthread test start") + + local function worker() + ngx.sleep(0.01) + print("ssl client hello: uthread worker executed") + return "worker_result" + end + + local t, err = ngx.thread.spawn(worker) + if not t then + ngx.log(ngx.ERR, "failed to spawn thread: ", err) + return + end + + print("ssl client hello: uthread spawned") + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.log(ngx.ERR, "failed to wait thread: ", res) + return + end + + print("ssl client hello: uthread result: ", res) + print("ssl client hello: uthread test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello: uthread test start|ssl client hello: uthread spawned|ssl client hello: uthread worker executed|ssl client hello: uthread result: worker_result|ssl client hello: uthread test done|test completed)/ +--- grep_error_log_out +ssl client hello: uthread test start +ssl client hello: uthread spawned +ssl client hello: uthread worker executed +ssl client hello: uthread result: worker_result +ssl client hello: uthread test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 36: ssl_client_hello_by_lua* with coroutines (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello: coroutine test start") + + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function coro_func() + local cnt = 0 + for i = 1, 3 do + print("ssl client hello: coro yield: ", cnt) + cy() + cnt = cnt + 1 + end + return "coro_done" + end + + local c = cc(coro_func) + for i = 1, 4 do + print("ssl client hello: coro resume, status: ", coroutine.status(c)) + local ok, res = cr(c) + if not ok then + print("ssl client hello: coro error: ", res) + break + end + if coroutine.status(c) == "dead" then + print("ssl client hello: coro result: ", res) + break + end + end + + print("ssl client hello: coroutine test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello: coroutine test start|ssl client hello: coro resume, status: \w+|ssl client hello: coro yield: \d+|ssl client hello: coro result: coro_done|ssl client hello: coroutine test done|test completed)/ +--- grep_error_log_out +ssl client hello: coroutine test start +ssl client hello: coro resume, status: suspended +ssl client hello: coro yield: 0 +ssl client hello: coro resume, status: suspended +ssl client hello: coro yield: 1 +ssl client hello: coro resume, status: suspended +ssl client hello: coro yield: 2 +ssl client hello: coro resume, status: suspended +ssl client hello: coro result: coro_done +ssl client hello: coroutine test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 37: ssl_client_hello_by_lua* without yield API (simple logic) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello: simple test start") + + -- Simple calculations without yield + local sum = 0 + for i = 1, 10 do + sum = sum + i + end + + print("ssl client hello: calculated sum: ", sum) + + -- String operations + local str = "hello" + str = str .. " world" + print("ssl client hello: concatenated string: ", str) + + -- Table operations + local t = {a = 1, b = 2, c = 3} + local count = 0 + for k, v in pairs(t) do + count = count + v + end + print("ssl client hello: table sum: ", count) + + print("ssl client hello: simple test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello: simple test start|ssl client hello: calculated sum: 55|ssl client hello: concatenated string: hello world|ssl client hello: table sum: 6|ssl client hello: simple test done|test completed)/ +--- grep_error_log_out +ssl client hello: simple test start +ssl client hello: calculated sum: 55 +ssl client hello: concatenated string: hello world +ssl client hello: table sum: 6 +ssl client hello: simple test done +test completed + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 38: ssl_client_hello_by_lua* with multiple network operations (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("ssl client hello: multiple network test start") + + -- First TCP operation + local sock1 = ngx.socket.tcp() + sock1:settimeout(2000) + local ok, err = sock1:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if ok then + local bytes, err = sock1:send("version\r\n") + if bytes then + local res, err = sock1:receive() + if res then + print("ssl client hello: TCP1 version: ", res) + end + end + sock1:close() + end + + ngx.sleep(0.01) -- Small delay + + -- Second UDP operation + local sock2 = ngx.socket.udp() + sock2:settimeout(1000) + local ok, err = sock2:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if ok then + local req = "\0\1\0\0\0\1\0\0version\r\n" + local ok, err = sock2:send(req) + if ok then + local res, err = sock2:receive() + if res then + print("ssl client hello: UDP version reply length: ", #res) + end + end + sock2:close() + end + + print("ssl client hello: multiple network test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(ssl client hello: multiple network test start|ssl client hello: TCP1 version: VERSION|ssl client hello: UDP version reply length: \d+|ssl client hello: multiple network test done|test completed)/ +--- grep_error_log_out eval +[ +qr/ssl client hello: multiple network test start/, +qr/ssl client hello: TCP1 version: VERSION/, +qr/ssl client hello: UDP version reply length: \d+/, +qr/ssl client hello: multiple network test done/, +qr/test completed/, +] + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 39: ssl_cert_by_lua* and ssl_client_hello_by_lua* with sleep (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- http_config + ssl_certificate_by_lua_block { + print("cert by: starting with sleep") + local begin = ngx.now() + ngx.sleep(0.05) + print("cert by: slept for ", ngx.now() - begin, " seconds") + } + +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("client hello: starting with sleep") + local begin = ngx.now() + ngx.sleep(0.1) + print("client hello: slept for ", ngx.now() - begin, " seconds") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(client hello: starting with sleep|client hello: slept for 0\.\d+|cert by: starting with sleep|cert by: slept for 0\.\d+|test completed)/ +--- grep_error_log_out eval +[ +qr/client hello: starting with sleep/, +qr/client hello: slept for 0\.(?:09|1\d)\d+/, +qr/cert by: starting with sleep/, +qr/cert by: slept for 0\.0[4-6]\d+/, +qr/test completed/, +] + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 40: ssl_cert_by_lua* and ssl_client_hello_by_lua* with cosocket (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- http_config + ssl_certificate_by_lua_block { + print("cert by: cosocket test start") + + local sock = ngx.socket.udp() + sock:settimeout(1000) + + local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "cert by: failed to connect to memc: ", err) + return + end + + local req = "\0\1\0\0\0\1\0\0flush_all\r\n" + local ok, err = sock:send(req) + if not ok then + ngx.log(ngx.ERR, "cert by: failed to send flush_all to memc: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "cert by: failed to receive memc reply: ", err) + return + end + + print("cert by: received UDP memc reply of ", #res, " bytes") + sock:close() + print("cert by: cosocket test done") + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("client hello: cosocket test start") + + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "client hello: failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("version\r\n") + if not bytes then + ngx.log(ngx.ERR, "client hello: failed to send version command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "client hello: failed to receive memc reply: ", err) + return + end + + print("client hello: received memc reply: ", res) + sock:close() + print("client hello: cosocket test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(client hello: cosocket test start|client hello: received memc reply: VERSION|client hello: cosocket test done|cert by: cosocket test start|cert by: received UDP memc reply of \d+ bytes|cert by: cosocket test done|test completed)/ +--- grep_error_log_out eval +[ +qr/client hello: cosocket test start/, +qr/client hello: received memc reply: VERSION/, +qr/client hello: cosocket test done/, +qr/cert by: cosocket test start/, +qr/cert by: received UDP memc reply of \d+ bytes/, +qr/cert by: cosocket test done/, +qr/test completed/, +] + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 41: ssl_cert_by_lua* and ssl_client_hello_by_lua* with timer and uthread (yield API) +--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} +--- http_config + ssl_certificate_by_lua_block { + print("cert by: coroutine test start") + + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function coro_func() + local cnt = 0 + for i = 1, 2 do + print("cert by: coro yield: ", cnt) + cy() + cnt = cnt + 1 + end + return "cert_by_coro_done" + end + + local c = cc(coro_func) + for i = 1, 3 do + print("cert by: coro resume, status: ", coroutine.status(c)) + local ok, res = cr(c) + if not ok then + print("cert by: coro error: ", res) + break + end + if coroutine.status(c) == "dead" then + print("cert by: coro result: ", res) + break + end + end + + -- Small sleep to allow timer to execute + ngx.sleep(0.01) + print("cert by: coroutine test done") + } + +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("client hello: timer and uthread test start") + + -- Timer test + local function timer_handler() + print("client hello: timer executed") + end + + local ok, err = ngx.timer.at(0, timer_handler) + if not ok then + ngx.log(ngx.ERR, "client hello: failed to create timer: ", err) + return + end + + print("client hello: timer created") + + -- User thread test + local function worker() + ngx.sleep(0.01) + print("client hello: uthread worker executed") + return "client_hello_result" + end + + local t, err = ngx.thread.spawn(worker) + if not t then + ngx.log(ngx.ERR, "client hello: failed to spawn thread: ", err) + return + end + + print("client hello: uthread spawned") + + local ok, res = ngx.thread.wait(t) + if not ok then + ngx.log(ngx.ERR, "client hello: failed to wait thread: ", res) + return + end + + print("client hello: uthread result: ", res) + print("client hello: timer and uthread test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- response_body +test completed + +--- grep_error_log eval: qr/(client hello: timer and uthread test start|client hello: timer created|client hello: uthread spawned|client hello: uthread worker executed|client hello: uthread result: client_hello_result|client hello: timer and uthread test done|cert by: coroutine test start|cert by: coro resume, status: \w+|cert by: coro yield: \d+|cert by: coro result: cert_by_coro_done|cert by: coroutine test done|client hello: timer executed|test completed)/ +--- grep_error_log_out eval +[ +qr/client hello: timer and uthread test start/, +qr/client hello: timer created/, +qr/client hello: uthread spawned/, +qr/client hello: uthread worker executed/, +qr/client hello: uthread result: client_hello_result/, +qr/client hello: timer and uthread test done/, +qr/cert by: coroutine test start/, +qr/cert by: coro resume, status: suspended/, +qr/cert by: coro yield: 0/, +qr/cert by: coro resume, status: suspended/, +qr/cert by: coro yield: 1/, +qr/cert by: coro resume, status: suspended/, +qr/cert by: coro result: cert_by_coro_done/, +qr/cert by: coroutine test done/, +qr/client hello: timer executed/, +qr/test completed/, +] + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 42: ssl_cert_by_lua* with cosocket (yield API) and ssl_client_hello_by_lua* failure +--- skip_eval: 8:!$ENV{TEST_NGINX_USE_HTTP3} +--- http_config + ssl_certificate_by_lua_block { + print("cert by: test start") + ngx.exit(500) + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + ssl_client_hello_by_lua_block { + print("client hello: cosocket test start") + + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "client hello: failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("version\r\n") + if not bytes then + ngx.log(ngx.ERR, "client hello: failed to send version command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "client hello: failed to receive memc reply: ", err) + return + end + + print("client hello: received memc reply: ", res) + sock:close() + print("client hello: cosocket test done") + } + + location /t { + content_by_lua_block { + print("test completed") + ngx.say("test completed") + } + } +--- request +GET /t +--- ignore_response +--- curl_error eval +qr/Connection time/ +--- grep_error_log eval: qr/(client hello: cosocket test start|client hello: received memc reply: VERSION|client hello: cosocket test done|cert by: test start)/ +--- grep_error_log_out eval +[ +qr/client hello: cosocket test start/, +qr/client hello: received memc reply: VERSION/, +qr/client hello: cosocket test done/, +qr/cert by: test start/, +] + +--- no_error_log +[error] +[alert] +[emerg] diff --git a/t/166-worker-thread.t b/t/166-worker-thread.t new file mode 100644 index 0000000000..1cbeec5eba --- /dev/null +++ b/t/166-worker-thread.t @@ -0,0 +1,1675 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_EVENT_TYPE} + && $ENV{TEST_NGINX_EVENT_TYPE} !~ /^kqueue|epoll|eventport$/) + { + $SkipReason = "unavailable for the event type '$ENV{TEST_NGINX_EVENT_TYPE}'"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(1); + +plan tests => repeat_each() * (blocks() * 2); + +our $HtmlDir = html_dir; + +our $HttpConfig = qq{ + lua_package_path "$::HtmlDir/?.lua;./?.lua;;"; + lua_worker_thread_vm_pool_size 1; +}; + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: hello from worker thread +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : hello + + + +=== TEST 2: thread_pool not found +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +false : thread pool testpool not found + + + +=== TEST 3: pass table +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, ok_or_err = ngx.run_worker_thread("testpool", "hello", "hello", {["hello"]="world", [1]={["embed"]=1}}) + ngx.say(ok, " , ", ok_or_err) + } +} +--- user_files +>>> hello.lua +local function hello(arg1) + if arg1.hello == "world" and arg1[1].embed == 1 then + return true + end + return false +end +return {hello=hello} +--- request +GET /hello +--- response_body +true , true + + + +=== TEST 4: expecting at least 3 arguments +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, err = ngx.run_worker_thread("testpool") + ngx.say(ok, " : ", err) + } +} +--- request +GET /hello +--- response_body +false : expecting at least 3 arguments + + + +=== TEST 5: base64 +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, base64 = ngx.run_worker_thread("testpool", "hello", "enc", "hello") + ngx.say(ok, " , ", base64 == "aGVsbG8=") + } +} +--- user_files +>>> hello.lua +local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +local function enc(data) + return ((data:gsub('.', function(x) + local r,b='',x:byte() + for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end + return r; + end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) + if (#x < 6) then return '' end + local c=0 + for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end + return b:sub(c+1,c+1) + end)..({ '', '==', '=' })[#data%3+1]) +end + +return {enc=enc} +--- request +GET /hello +--- response_body +true , true + + + +=== TEST 6: return table +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, ret = ngx.run_worker_thread("testpool", "hello", "hello") + if ret.hello == "world" and ret[1].embed == 1 then + ngx.say(ok, " , ", true) + end + } +} +--- user_files +>>> hello.lua +local function hello() + return {["hello"]="world", [1]={["embed"]=1}} +end +return {hello=hello} +--- request +GET /hello +--- response_body +true , true + + + +=== TEST 7: unsupported argument type +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local function dummy() end + local ok, err = ngx.run_worker_thread("testpool", "hello", "hello", dummy) + ngx.say(ok, " : ", err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +false : unsupported Lua type: LUA_TFUNCTION in the argument + + + +=== TEST 8: multiple return values +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, res1, res2 = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", res1, " , ", res2) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello", 200 +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : hello , 200 + + + +=== TEST 9: module not found +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", err) + } +} +--- request +GET /hello +--- response_body_like +false : module 'hello' not found.* + + + +=== TEST 10: the number of Lua VM exceeds the pool size +--- no_http2 +--- quic_max_idle_timeout: 5 +--- main_config + thread_pool testpool threads=100; +--- http_config eval: $::HttpConfig +--- config +location /foo { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} + +location /bar { + default_type 'text/plain'; + + content_by_lua_block { + local ok, foobar_or_err = ngx.run_worker_thread("testpool", "foobar", "foobar") + ngx.say(ok, " : ", foobar_or_err) + } +} + +location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local function t(path) + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local req = "GET " .. path .. " HTTP/1.0\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 ret, err, part = sock:receive("*a") + local _, idx = string.find(ret, "\r\n\r\n"); + idx = idx + 1 + ngx.print(string.sub(ret, idx)) + ok, err = sock:close() + end + + local t1 = ngx.thread.spawn(t, "/foo") + local t2 = ngx.thread.spawn(t, "/bar") + ngx.thread.wait(t1) + ngx.thread.wait(t2) + } +} +--- user_files +>>> hello.lua +local function hello() + os.execute("sleep 3") + return "hello" +end +return {hello=hello} +>>> foobar.lua +local function foobar() + return "foobar" +end +return {foobar=foobar} +--- request +GET /t +--- response_body eval +"false : no available Lua vm\ntrue : hello\n" +--- timeout: 10 + + + +=== TEST 11: kill uthread before worker thread callback +--- no_http2 +--- quic_max_idle_timeout: 10 +--- main_config + thread_pool testpool threads=100; +--- http_config eval: $::HttpConfig +--- config +location /foo { + default_type 'text/plain'; + + content_by_lua_block { + local function t() + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + end + local t1 = ngx.thread.spawn(t) + if ngx.var.arg_kill == "kill" then + ngx.thread.kill(t1) + ngx.say("killed") + end + } +} + +location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local function t(path) + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local req = "GET " .. path .. " HTTP/1.0\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 ret, err, part = sock:receive("*a") + local _, idx = string.find(ret, "\r\n\r\n"); + idx = idx + 1 + ngx.print(string.sub(ret, idx)) + ok, err = sock:close() + end + + local t1 = ngx.thread.spawn(t, "/foo?kill=kill") + ngx.thread.wait(t1) + ngx.sleep(4) + local t2 = ngx.thread.spawn(t, "/foo") + ngx.thread.wait(t2) + } +} +--- user_files +>>> hello.lua +local function hello() + os.execute("sleep 1") + return "hello" +end +return {hello=hello} +>>> foobar.lua +local function foobar() + return "foobar" +end +return {foobar=foobar} +--- request +GET /t +--- response_body eval +"killed\ntrue : hello\n" +--- timeout: 10 + + + +=== TEST 12: exit before worker thread callback +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local function t() + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + end + ngx.thread.spawn(t) + ngx.exit(200) + } +} +--- user_files +>>> hello.lua +local function hello() + os.execute("sleep 3") + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +--- timeout: 10 + + + +=== TEST 13: unsupported argument type in nested table +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local function dummy() end + local ok, err = ngx.run_worker_thread("testpool", "hello", "hello", + {["hello"]="world", [1]={["embed"]=1, ["dummy"]=dummy}}) + ngx.say(ok, " : ", err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +false : unsupported Lua type: LUA_TFUNCTION in the argument + + + +=== TEST 14: return table with unsupported type +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, ret = ngx.run_worker_thread("testpool", "hello", "hello") + if ok == false then + ngx.say("false", " , ", ret) + end + if ret.hello == "world" and ret[1].embed == 1 then + ngx.say(ok, " , ", true) + end + } +} +--- user_files +>>> hello.lua +local function hello() + local function dummy() end + return {["hello"]="world", [1]={["embed"]=1, ["dummy"]=dummy}} +end +return {hello=hello} +--- request +GET /hello +--- response_body +false , unsupported Lua type: LUA_TFUNCTION in the return value + + + +=== TEST 15: the type of module name is not string +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local function dummy() end + local ok, err = ngx.run_worker_thread("testpool", dummy, "hello") + ngx.say(ok, " : ", err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +false : module name should be a string + + + +=== TEST 16: the type of function name is not string +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local function dummy() end + local ok, err = ngx.run_worker_thread("testpool", "hello", dummy) + ngx.say(ok, " : ", err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +false : function name should be a string + + + +=== TEST 17: the type of thread pool name is not string +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local function dummy() end + local ok, err = ngx.run_worker_thread(dummy, "hello", "hello") + ngx.say(ok, " : ", err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +false : threadpool should be a string + + + +=== TEST 18: ngx.encode_base64 +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.encode_base64("hello") +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : aGVsbG8= + + + +=== TEST 19: ngx.config.subsystem +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.config.subsystem +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : http + + + +=== TEST 20: ngx.hmac_sha1 +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + local key = "thisisverysecretstuff" + local src = "some string we want to sign" + local digest = ngx.hmac_sha1(key, src) + return ngx.encode_base64(digest) +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : R/pvxzHC4NLtj7S+kXFg/NePTmk= + + + +=== TEST 21: ngx.encode_args +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.encode_args({foo = 3, ["b r"] = "hello world"}) +end +return {hello=hello} +--- request +GET /hello +--- response_body eval +qr/foo=3&b%20r=hello%20world|b%20r=hello%20world&foo=3/ + + + +=== TEST 22: ngx.decode_args +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, ret = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", ret.a, " : ", ret.b) + } +} +--- user_files +>>> hello.lua +local function hello() + local args = "a=bar&b=foo" + args = ngx.decode_args(args) + return args +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : bar : foo + + + +=== TEST 23: ngx.quote_sql_str +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config + location /hello { + content_by_lua ' + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello", "a\\026b\\026") + ngx.say(ok, " : ", hello_or_err) + '; + } +--- user_files +>>> hello.lua +local function hello(str) + return ngx.quote_sql_str(str) +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : 'a\Zb\Z' + + + +=== TEST 24: ngx.decode_base64 +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.decode_base64("aGVsbG8=") +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : hello + + + +=== TEST 25: ngx.crc32_short +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.crc32_short("hello, world") +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : 4289425978 + + + +=== TEST 26: ngx.crc32_long +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.crc32_long("hello, world") +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : 4289425978 + + + +=== TEST 27: ngx.md5_bin +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + local s = ngx.md5_bin(45) + s = string.gsub(s, ".", function (c) + return string.format("%02x", string.byte(c)) + end) + return s +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : 6c8349cc7260ae62e3b1396831a8398f + + + +=== TEST 28: ngx.md5 +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.md5("hello") +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : 5d41402abc4b2a76b9719d911017c592 + + + +=== TEST 29: ngx.config.debug +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.config.debug +end +return {hello=hello} +--- request +GET /hello +--- response_body_like chop +^true : (?:true|false)$ + + + +=== TEST 30: ngx.config.prefix +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.config.prefix() +end +return {hello=hello} +--- request +GET /hello +--- response_body_like chop +^true : \/\S+$ + + + +=== TEST 31: ngx.config.nginx_version +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.config.nginx_version +end +return {hello=hello} +--- request +GET /hello +--- response_body_like chop +^true : \d+$ + + + +=== TEST 32: ngx.config.nginx_configure +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.config.nginx_configure() +end +return {hello=hello} +--- request +GET /hello +--- response_body_like chop +^\s*\-\-[^-]+ + + + +=== TEST 33: ngx.config.ngx_lua_version +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return ngx.config.ngx_lua_version +end +return {hello=hello} +--- request +GET /hello +--- response_body_like chop +^true : \d+$ + + + +=== TEST 34: write_log_file +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /write_log_file { + default_type 'text/plain'; + + access_by_lua_block { + local ok, err = ngx.run_worker_thread("testpool", "write_log_file", "log", ngx.var.arg_str) + if not ok then + ngx.say(ok, " : ", err) + return + end + ngx.say(ok) + } +} +--- user_files +>>> write_log_file.lua +local function log(str) + local file, err = io.open("/tmp/tmp.log", "w") + if not file then + return false, err + end + file:write(str) + file:flush() + file:close() + return true +end +return {log=log} +--- request +GET /write_log_file?str=hello +--- response_body +true + + + +=== TEST 35: shdict get, int value +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 10m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictget { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8) + local ok, err = ngx.run_worker_thread("testpool", "test_shdict", "dictget") + ngx.say(ok, ",", err) + } +} +--- user_files +>>> test_shdict.lua +local function dictget(str) + local dogs = ngx.shared.dogs + return dogs:get("Jim") +end +return {dictget=dictget} +--- request +GET /dictget +--- response_body +true,8 + + + +=== TEST 36: shdict set nil in main thread +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 10m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictget { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8) + local ok, err = ngx.run_worker_thread("testpool", "test_shdict", "dictget") + ngx.say(ok, ",", err) + dogs:set("Jim", nil) + local ok, err = ngx.run_worker_thread("testpool", "test_shdict", "dictget") + ngx.say(ok, ",", err) + } +} +--- user_files +>>> test_shdict.lua +local function dictget(str) + local dogs = ngx.shared.dogs + return dogs:get("Jim") +end +return {dictget=dictget} +--- request +GET /dictget +--- response_body +true,8 +true,nil + + + +=== TEST 37: shdict set nil in worker thread +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 10m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictsetnil { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8) + local ok, err = ngx.run_worker_thread("testpool", "test_shdict", "dictsetnil") + ngx.say(ok, ",", err) + ngx.say(ok, ",", dogs:get("Jim")) + } +} +--- user_files +>>> test_shdict.lua +local function dictsetnil(str) + local dogs = ngx.shared.dogs + return dogs:set("Jim", nil) +end +return {dictsetnil=dictsetnil} +--- request +GET /dictsetnil +--- response_body +true,true +true,nil + + + +=== TEST 38: shdict get_stale +For http3: curl: (55) ngtcp2_conn_handle_expiry returned error: ERR_IDLE_CLOSE +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 10m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictget { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8, 1) + ngx.sleep(2) + local ok, err = ngx.run_worker_thread("testpool", "test_shdict", "dictget") + ngx.say(ok, ",", err) + } +} +--- user_files +>>> test_shdict.lua +local function dictget(str) + local dogs = ngx.shared.dogs + return dogs:get_stale("Jim") +end +return {dictget=dictget} +--- request +GET /dictget +--- response_body +true,8 + + + +=== TEST 39: shdict add failed +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 10m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictadd { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8) + local ok, err, err2 = ngx.run_worker_thread("testpool", "test_shdict", "dictadd") + ngx.say(ok, ",", err, ",", err2) + } +} +--- user_files +>>> test_shdict.lua +local function dictadd(str) + local dogs = ngx.shared.dogs + local success, err = dogs:add("Jim", "hello") + return success, err +end +return {dictadd=dictadd} +--- request +GET /dictadd +--- response_body +true,false,exists + + + +=== TEST 40: shdict force add +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictadd { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + local bigstr = string.rep("A", 1024*1024*3) + dogs:set("Jim", bigstr) + local ok, ret, err, forcible = ngx.run_worker_thread("testpool", "test_shdict", "dictadd") + ngx.say(ok, ",", ret, ",", forcible, ",", dogs:get("Jim")) + } +} +--- user_files +>>> test_shdict.lua +local function dictadd(str) + local dogs = ngx.shared.dogs + local bigstr = string.rep("A", 1024*1024*5) + local success, err, forcible = dogs:add("King", bigstr) + return success, err, forcible +end +return {dictadd=dictadd} +--- request +GET /dictadd +--- response_body +true,true,true,nil + + + +=== TEST 41: shdict replace +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictreplace { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + local bigstr = string.rep("A", 1024*1024*3) + dogs:set("Jim", bigstr) + local ok, ret, err = ngx.run_worker_thread("testpool", "test_shdict", "dictreplace") + ngx.say(ok, ",", ret, ",", err, ",", dogs:get("Jim")) + } +} +--- user_files +>>> test_shdict.lua +local function dictreplace(str) + local dogs = ngx.shared.dogs + local success, err = dogs:replace("Jim", 8) + return success, err +end +return {dictreplace=dictreplace} +--- request +GET /dictreplace +--- response_body +true,true,nil,8 + + + +=== TEST 42: shdict replace not found +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictreplace { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + local ok, ret, err = ngx.run_worker_thread("testpool", "test_shdict", "dictreplace") + ngx.say(ok, ",", ret, ",", err) + } +} +--- user_files +>>> test_shdict.lua +local function dictreplace(str) + local dogs = ngx.shared.dogs + local success, err = dogs:replace("Jim", 8) + return success, err +end +return {dictreplace=dictreplace} +--- request +GET /dictreplace +--- response_body +true,false,not found + + + +=== TEST 43: shdict incr +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictincr { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + local success, err = dogs:set("Jim", 8) + local ok, ret, err = ngx.run_worker_thread("testpool", "test_shdict", "dictincr") + ngx.say(ok, ",", ret, ",", err, ",", dogs:get("Jim")) + } +} +--- user_files +>>> test_shdict.lua +local function dictincr(str) + local dogs = ngx.shared.dogs + local success, err = dogs:incr("Jim", 1) + return success, err +end +return {dictincr=dictincr} +--- request +GET /dictincr +--- response_body +true,9,nil,9 + + + +=== TEST 44: shdict lpush lpop +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictlpush { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:lpush("Jim", 8) + dogs:lpush("Jim", 9) + local ok, val, len, err = ngx.run_worker_thread("testpool", "test_shdict", "dictlpush") + ngx.say(ok, ",", val, ",", len, ",", err, ",", dogs:lpop("Jim")) + } +} +--- user_files +>>> test_shdict.lua +local function dictlpush(str) + local dogs = ngx.shared.dogs + local val = dogs:lpop("Jim") + local len, err = dogs:lpush("Jim", 7) + return val, len, err +end +return {dictlpush=dictlpush} +--- request +GET /dictlpush +--- response_body +true,9,2,nil,7 + + + +=== TEST 45: shdict expire ttl +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictexpire { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8) + local ok, success, err = ngx.run_worker_thread("testpool", "test_shdict", "dictexpire") + ngx.say(ok, ",", success, ",", err, ",", dogs:ttl("Jim") <= 1) + } +} +--- user_files +>>> test_shdict.lua +local function dictexpire(str) + local dogs = ngx.shared.dogs + local success, err = dogs:expire("Jim", 1) + return success, err +end +return {dictexpire=dictexpire} +--- request +GET /dictexpire +--- response_body +true,true,nil,true + + + +=== TEST 46: shdict flush_all +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictexpire { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8) + dogs:set("King", 9) + local ok = ngx.run_worker_thread("testpool", "test_shdict", "dictexpire") + ngx.say(ok, ",", dogs:get("Jim"), ",", dogs:get("King")) + } +} +--- user_files +>>> test_shdict.lua +local function dictexpire(str) + local dogs = ngx.shared.dogs + dogs:flush_all() +end +return {dictexpire=dictexpire} +--- request +GET /dictexpire +--- response_body +true,nil,nil + + + +=== TEST 47: shdict get_keys +--- main_config + thread_pool testpool threads=100; +--- http_config eval +" + lua_shared_dict dogs 6m; + lua_package_path '$::HtmlDir/?.lua;./?.lua;;'; +" +--- config +location /dictgetkeys { + default_type 'text/plain'; + + access_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("Jim", 8) + dogs:set("King", 9) + local ok, keys = ngx.run_worker_thread("testpool", "test_shdict", "dictgetkeys") + ngx.say(ok, ",", table.concat(keys, ":")) + } +} +--- user_files +>>> test_shdict.lua +local function dictgetkeys(str) + local dogs = ngx.shared.dogs + return dogs:get_keys() +end +return {dictgetkeys=dictgetkeys} +--- request +GET /dictgetkeys +--- response_body +true,Jim:King + + + +=== TEST 48: unsupported argument type in self-reference table +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local t = {} + t.a = t + local ok, ok_or_err = ngx.run_worker_thread("testpool", "hello", "hello", t) + ngx.say(ok, " , ", ok_or_err) + } +} +--- user_files +>>> hello.lua +local function hello(arg1) + return true +end +return {hello=hello} +--- request +GET /hello +--- response_body +false , suspicious circular references, table depth exceed max depth: 100 in the argument + + + +=== TEST 49: unsupported argument type in circular-reference table +--- main_config + thread_pool testpool threads=100; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local t = {} + local s = {} + t.a = s + s.b = t + + local ok, ok_or_err = ngx.run_worker_thread("testpool", "hello", "hello", t) + ngx.say(ok, " , ", ok_or_err) + } +} +--- user_files +>>> hello.lua +local function hello(arg1) + return true +end +return {hello=hello} +--- request +GET /hello +--- response_body +false , suspicious circular references, table depth exceed max depth: 100 in the argument + + + +=== TEST 50: call run_worker_thread twice +--- main_config + thread_pool testpool threads=1; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + + ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", hello_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return "hello" +end +return {hello=hello} +--- request +GET /hello +--- response_body +true : hello +true : hello + + + +=== TEST 51: big object +--- main_config + thread_pool testpool threads=1; +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config +location /hello { + default_type 'text/plain'; + + content_by_lua_block { + local ok, hello_or_err = ngx.run_worker_thread("testpool", "hello", "hello") + ngx.say(ok, " : ", #hello_or_err) + + local ok, gcsize_or_err = ngx.run_worker_thread("testpool", "hello", "gcsize") + ngx.say(ok, " : ", gcsize_or_err) + } +} +--- user_files +>>> hello.lua +local function hello() + return string.rep("helloworld", 1000000) +end + +local function gcsize() + return collectgarbage("count") +end + +return { + hello = hello, + gcsize = gcsize +} +--- request +GET /hello +--- response_body eval +qr/\Atrue : 10000000 +true : \d{3,4}\.\d+ +\z/ms diff --git a/t/167-server-rewrite.t b/t/167-server-rewrite.t new file mode 100644 index 0000000000..33a30f3842 --- /dev/null +++ b/t/167-server-rewrite.t @@ -0,0 +1,491 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; +use t::StapThread; + +our $GCScript = <<_EOC_; +$t::StapThread::GCScript + +F(ngx_http_lua_check_broken_connection) { + println("lua check broken conn") +} + +F(ngx_http_lua_request_cleanup) { + println("lua req cleanup") +} +_EOC_ + +our $StapScript = $t::StapThread::StapScript; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 10); + +#log_level("info"); +#no_long_string(); + +run_tests(); + +__DATA__ + +=== TEST 1: server_rewrite_by_lua_block in http +--- http_config + server_rewrite_by_lua_block { + ngx.ctx.a = "server_rewrite_by_lua_block in http" + } +--- config + location /lua { + content_by_lua_block { + ngx.say(ngx.ctx.a) + ngx.log(ngx.INFO, ngx.ctx.a) + } + } +--- request +GET /lua +--- response_body +server_rewrite_by_lua_block in http +--- error_log +server_rewrite_by_lua_block in http +--- no_error_log +[error] + + + +=== TEST 2: server_rewrite_by_lua_block in server +--- config + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "server_rewrite_by_lua_block in server") + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +OK +--- error_log +server_rewrite_by_lua_block in server +--- no_error_log +[error] + + + +=== TEST 3: redirect +--- config + server_rewrite_by_lua_block { + ngx.redirect("/foo") + } +--- request +GET /lua +--- raw_response_headers_like eval +qr{[Ll]ocation: /foo\r\n} +--- response_body_like: 302 Found +--- error_code: 302 +--- no_error_log +[error] + + + +=== TEST 4: flush +--- config + server_rewrite_by_lua_block { + ngx.say("foo") + ngx.flush(true) + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +foo +--- no_error_log +[error] + + + +=== TEST 5: eof +--- config + server_rewrite_by_lua_block { + ngx.say("foo") + ngx.eof() + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +foo +--- no_error_log +[error] + + + +=== TEST 6: send_headers +--- config + server_rewrite_by_lua_block { + ngx.header["Foox"] = {"conx1", "conx2" } + ngx.header["Fooy"] = {"cony1", "cony2" } + ngx.send_headers() + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +--- response_headers +Foox: conx1, conx2 +Fooy: cony1, cony2 +--- no_error_log +[error] + + + +=== TEST 7: read_body +--- config + server_rewrite_by_lua_block { + ngx.req.read_body() + ngx.say(ngx.var.request_body) + } +--- request +POST /lua +hello, world +--- response_body +hello, world +--- no_error_log +[error] + + + +=== TEST 8: req_sock +--- config + server_rewrite_by_lua_block { + local sock = ngx.req.socket() + sock:receive(2) + sock:receive(2) + sock:receive(1) + ngx.sleep(1) + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +POST /lua +hello + +--- stap2 eval: $::StapScript +--- stap eval: $::GCScript +--- stap_out +lua check broken conn +lua check broken conn +lua req cleanup +delete thread 1 + +--- wait: 1 +--- timeout: 0.2 +--- abort +--- ignore_response +--- no_error_log +[error] +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3} + + + +=== TEST 9: rewrite args (not break cycle by default) +--- config + location /bar { + echo "bar: $uri?$args"; + } + server_rewrite_by_lua_block { + if ngx.var.uri ~= "/bar" then + ngx.req.set_uri_args("hello") + ngx.req.set_uri("/bar", true) + end + } + location /foo { + + echo "foo: $uri?$args"; + } +--- request + GET /foo?world +--- response_body +bar: /bar?hello + + + +=== TEST 10: server_rewrite_by_lua_block overwrite by server +--- http_config + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "server_rewrite_by_lua_block in http") + } +--- config + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "server_rewrite_by_lua_block in server") + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +OK +--- error_log +server_rewrite_by_lua_block in server +--- no_error_log +[error] + + + +=== TEST 11: sleep +--- config + server_rewrite_by_lua_block { + ngx.sleep(0.001) + ngx.log(ngx.INFO, "server_rewrite_by_lua_block in server") + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +OK +--- error_log +server_rewrite_by_lua_block in server +--- no_error_log +[error] + + + +=== TEST 12: ngx.exit(ngx.OK) +--- config + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "ngx.exit") + ngx.exit(ngx.OK) + } + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +OK +--- error_log +ngx.exit +--- no_error_log +[error] + + + +=== TEST 13: ngx.exit(503) +--- config + server_rewrite_by_lua_block { + ngx.exit(503) + } + location /lua { + content_by_lua_block { + ngx.log(ngx.ERR, "content_by_lua") + ngx.say("OK") + } + } +--- request +GET /lua +--- error_code: 503 +--- no_error_log +[error] + + + +=== TEST 14: subrequests +--- config + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "is_subrequest:", ngx.is_subrequest) + } + + location /lua { + content_by_lua_block { + local res = ngx.location.capture("/sub") + ngx.print(res.body) + } + } + + location /sub { + content_by_lua_block { + ngx.say("OK") + } + } + +--- request +GET /lua +--- response_body +OK +--- error_log +is_subrequest:false +is_subrequest:true +--- no_error_log +[error] + + + +=== TEST 15: rewrite by ngx_http_rewrite_module +--- config + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "uri is ", ngx.var.uri) + } + + rewrite ^ /re; + + location /re { + content_by_lua_block { + ngx.say("RE") + } + } + + location /ok { + content_by_lua_block { + ngx.say("OK") + } + } + +--- request +GET /lua +--- response_body +RE +--- error_log +uri is /lua +--- no_error_log +[error] + + + +=== TEST 16: exec +--- config + server_rewrite_by_lua_block { + if ngx.var.uri ~= "/ok" then + ngx.exec("/ok") + end + ngx.log(ngx.INFO, "uri is ", ngx.var.uri) + } + + location /ok { + content_by_lua_block { + ngx.say("OK") + } + } + +--- request +GET /lua +--- response_body +OK +--- error_log +uri is /ok +--- no_error_log +[error] + + + +=== TEST 17: server_rewrite_by_lua and rewrite_by_lua +--- http_config + server_rewrite_by_lua_block { + ngx.log(ngx.INFO, "server_rewrite_by_lua_block in http") + } +--- config + location /lua { + rewrite_by_lua_block { + ngx.log(ngx.INFO, "rewrite_by_lua_block in location") + } + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- response_body +OK +--- error_log +server_rewrite_by_lua_block in http +rewrite_by_lua_block in location +--- no_error_log +[error] + + + +=== TEST 18: server_rewrite_by_lua_file +--- http_config + server_rewrite_by_lua_file 'html/foo.lua'; +--- config + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- user_files +>>> foo.lua +ngx.log(ngx.INFO, "rewrite_by_lua_file in server") +--- response_body +OK +--- error_log +rewrite_by_lua_file in server +--- no_error_log +[error] + + + +=== TEST 19: syntax error server_rewrite_by_lua_block in http +--- http_config + server_rewrite_by_lua_block { + 'for end'; + } +--- config + location /lua { + content_by_lua_block { + ngx.say("OK") + } + } +--- request +GET /lua +--- ignore_response +--- error_log +failed to load inlined Lua code: server_rewrite_by_lua(nginx.conf:25):2: unexpected symbol near ''for end'' +--- no_error_log +no_such_error +--- skip_eval: 2:$ENV{TEST_NGINX_USE_HUP} + + + +=== TEST 20: syntax error server_rewrite_by_lua_block in server +--- config + server_rewrite_by_lua_block { + 'for end'; + } + location /lua { + content_by_lua_block { + ngx.say("Hello world") + } + } +--- request +GET /lua +--- ignore_response +--- error_log +failed to load inlined Lua code: server_rewrite_by_lua(nginx.conf:39):2: unexpected symbol near ''for end'' +--- no_error_log +no_such_error diff --git a/t/168-tcp-socket-bind.t b/t/168-tcp-socket-bind.t new file mode 100644 index 0000000000..123b525fe3 --- /dev/null +++ b/t/168-tcp-socket-bind.t @@ -0,0 +1,428 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +# more times than usual(2) for test case 6 +repeat_each(4); + +plan tests => repeat_each() * (blocks() * 3 + 7); + +our $HtmlDir = html_dir; + +# get ip address in the dev which is default route outgoing dev +my $dev = `ip route | awk '/default/ {printf "%s", \$5}'`; +my $local_ip = `ip route | grep $dev | grep -o "src .*" | head -n 1 | awk '{print \$2}'`; +chomp $local_ip; + +$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir; +$ENV{TEST_NGINX_NOT_EXIST_IP} ||= '8.8.8.8'; +$ENV{TEST_NGINX_INVALID_IP} ||= '127.0.0.1:8899'; +$ENV{TEST_NGINX_SERVER_IP} ||= $local_ip; + +no_long_string(); +#no_diff(); + +#log_level 'warn'; +log_level 'debug'; + +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: upstream sockets bind 127.0.0.1 +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local ip = "127.0.0.1" + local port = ngx.var.port + + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.say("failed to bind", err) + return + end + + 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) + + local bytes, err = sock:send("GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent") + + 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") + local remote_ip = string.match(data, "(bind: %d+%.%d+%.%d+%.%d+)") + ngx.say(remote_ip) + + ngx.say("done") + } + } + + location /foo { + echo bind: $remote_addr; + } +--- request +GET /t +--- response_body +connected: 1 +request sent +received response +bind: 127.0.0.1 +done +--- no_error_log +["[error]", +"bind(127.0.0.1) failed"] +--- error_log eval +"lua tcp socket bind ip: 127.0.0.1" + + + +=== TEST 2: upstream sockets bind server ip, not 127.0.0.1 +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local ip = "$TEST_NGINX_SERVER_IP" + local port = ngx.var.port + + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.say("failed to bind", err) + return + end + + 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) + + local bytes, err = sock:send("GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent") + + 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") + local remote_ip = string.match(data, "(bind: %d+%.%d+%.%d+%.%d+)") + if remote_ip == "bind: $TEST_NGINX_SERVER_IP" then + ngx.say("ip matched") + end + + ngx.say("done") + } + } + + location /foo { + echo bind: $remote_addr; + } +--- request +GET /t +--- response_body +connected: 1 +request sent +received response +ip matched +done +--- no_error_log eval +["[error]", +"bind($ENV{TEST_NGINX_SERVER_IP}) failed"] +--- error_log eval +"lua tcp socket bind ip: $ENV{TEST_NGINX_SERVER_IP}" + + + +=== TEST 3: add setkeepalive +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;./?.lua;;';" +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local test = require "test" + local t1 = test.go() + local t2 = test.go() + ngx.say("t2 - t1: ", t2 - t1) + } + } +--- user_files +>>> test.lua +local _M = {} + +function _M.go() + local ip = "127.0.0.1" + local port = ngx.var.port + + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.say("failed to bind", err) + return + end + + ngx.say("bind: ", ip) + + 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) + + local reused = sock:getreusedtimes() + + local ok, err = sock:setkeepalive() + if not ok then + ngx.say("failed to set reusable: ", err) + end + + return reused +end + +return _M +--- request +GET /t +--- response_body +bind: 127.0.0.1 +connected: 1 +bind: 127.0.0.1 +connected: 1 +t2 - t1: 1 +--- no_error_log +["[error]", +"bind(127.0.0.1) failed"] +--- error_log eval +"lua tcp socket bind ip: 127.0.0.1" + + + +=== TEST 4: upstream sockets bind not exist ip +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local ip = "$TEST_NGINX_NOT_EXIST_IP" + local port = ngx.var.port + + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.say("failed to bind", err) + return + end + + ngx.say("bind: ", ip) + + 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) + } + } +--- request +GET /t +--- response_body +bind: 8.8.8.8 +failed to connect: cannot assign requested address +--- error_log eval +["bind(8.8.8.8) failed", +"lua tcp socket bind ip: 8.8.8.8"] + + + +=== TEST 5: upstream sockets bind invalid ip +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local ip = "$TEST_NGINX_INVALID_IP" + local port = ngx.var.port + + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.say("failed to bind: ", err) + return + end + + ngx.say("bind: ", ip) + + 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) + } + } +--- request +GET /t +--- response_body +failed to bind: bad address +--- no_error_log +[error] + + + +=== TEST 6: tcpsock across request after bind +--- http_config + init_worker_by_lua_block { + -- this is not the recommend way, just for test + local function tcp() + local sock = ngx.socket.tcp() + + local ok, err = sock:bind("127.0.0.1") + if not ok then + ngx.log(ngx.ERR, "failed to bind") + end + + package.loaded.share_sock = sock + end + + local ok, err = ngx.timer.at(0, tcp) + if not ok then + ngx.log(ngx.ERR, "failed to create timer") + end + } +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local port = ngx.var.port + + -- make sure share_sock is created + ngx.sleep(0.002) + + local sock = package.loaded.share_sock + if sock ~= nil then + package.loaded.share_sock = nil + + 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) + + sock:close() + collectgarbage("collect") + else + -- the sock from package.loaded.share_sock is just + -- for the first request after worker init + -- add following code to keep the same result for other request + ngx.say("connected: ", 1) + end + } + } +--- request +GET /t +--- response_body +connected: 1 +--- no_error_log +[error] + + + +=== TEST 7: upstream sockets bind with ip port +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local ip = "127.0.0.1" + local port = ngx.var.port + + local sock = ngx.socket.tcp() + + local ok, err = sock:bind(ip, 12345) + if not ok then + ngx.say("failed to bind", err) + return + end + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local ok, err = sock:setoption("reuseaddr", 1) + if not ok then + ngx.say("setoption reuseaddr failed: ", err) + end + + ngx.say("connected: ", ok) + + local bytes, err = sock:send("GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + 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 + sock:close() + ngx.say(data) + + } + } + + location /foo { + echo bind: $remote_addr:$remote_port; + } +--- request +GET /t +--- response_body eval +qr/bind:\s127\.0\.0\.1:12345|failed\s+to\s+connect:\s+address\s+already\s+in\s+use/ +--- error_log eval +"lua tcp socket bind ip: 127.0.0.1" diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t new file mode 100644 index 0000000000..95ecc1f3a7 --- /dev/null +++ b/t/169-proxy-ssl-verify.t @@ -0,0 +1,1337 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL (\d+)\.(\d+)\.(\d+)/) { + my ($major, $minor, $patch) = ($1, $2, $3); + + if ($major < 3 || ($major == 3 && $minor == 0 && $patch < 2)) { + plan(skip_all => "too old OpenSSL, need >= 3.0.2, was " . + "$major.$minor.$patch"); + } else { + plan tests => repeat_each() * (blocks() * 5 + 19); + } +} elsif ($openssl_version =~ m/running with BoringSSL/) { + plan(skip_all => "does not support BoringSSL"); +} else { + die "unknown SSL"; +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: invalid proxy_pass url +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass http://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } + } +--- request +GET /t +--- error_log +proxy_ssl_verify_by_lua* should be used with proxy_pass https url +--- must_die + + + +=== TEST 2: proxy_ssl_verify_by_lua in http {} block +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } +--- config + location /t { + proxy_pass http://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + } +--- request +GET /t +--- error_log +"proxy_ssl_verify_by_lua_block" directive is not allowed here +--- must_die + + + +=== TEST 3: proxy_ssl_verify_by_lua in server {} block +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } + +--- config + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } + + location /t { + proxy_pass http://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + } +--- request +GET /t +--- error_log +"proxy_ssl_verify_by_lua_block" directive is not allowed here +--- must_die + + + +=== TEST 4: simple logging +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") + } + } +--- request +GET /t +--- response_body +simple logging return +--- error_log +proxy ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 5: sleep +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("sleep") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in proxy ssl verify by lua: ", ngx.now() - begin) + } + } +--- request +GET /t +--- response_body +sleep +--- error_log eval +qr/elapsed in proxy ssl verify by lua: 0.(?:09|1\d)\d+ while loading proxy ssl verify by lua,/, +--- no_error_log +[error] +[alert] + + + +=== TEST 6: timer +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("timer") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + local function f() + print("my timer run!") + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + } + } +--- request +GET /t +--- response_body +timer +--- error_log +my timer run! +--- no_error_log +[error] +[alert] + + + +=== TEST 7: ngx.exit(0) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(0) no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + ngx.exit(0) + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- response_body +ngx.exit(0) no yield +--- error_log +lua exit with code 0 +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 8: ngx.exit(ngx.ERROR) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(ngx.ERROR) no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'lua exit with code -1', +'proxy_ssl_verify_by_lua: handler return value: -1, cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 9: ngx.exit(0) - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(0) yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.001) + ngx.exit(0) + + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- response_body +ngx.exit(0) yield +--- error_log +lua exit with code 0 +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 10: ngx.exit(ngx.ERROR) - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(ngx.ERROR) yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.001) + ngx.exit(ngx.ERROR) + + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'lua exit with code -1', +'proxy_ssl_verify_by_lua: cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 11: lua exception - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua exception - no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):2: bad bad bad', +'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 12: lua exception - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua exception - yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.001) + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):3: bad bad bad', +'proxy_ssl_verify_by_lua: cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 13: get phase +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("get phase return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + print("get_phase: ", ngx.get_phase()) + } + } +--- request +GET /t +--- response_body +get phase return +--- error_log +get_phase: proxy_ssl_verify +--- no_error_log +[error] +[alert] + + + +=== TEST 14: subrequests disabled +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("subrequests disabled") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.location.capture("/foo") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'proxy_ssl_verify_by_lua(nginx.conf:65):2: API disabled in the context of proxy_ssl_verify_by_lua*', +'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +[alert] + + + +=== TEST 15: simple logging (by_lua_file) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging by lua file") + } + + more_clear_headers Date; + } + } +--- user_files +>>> a.lua +print("proxy ssl verify by lua is running!") + +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_file html/a.lua; + } +--- request +GET /t +--- response_body +simple logging by lua file +--- error_log +a.lua:1: proxy ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 16: coroutine API +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("coroutine API") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function f() + local cnt = 0 + for i = 1, 20 do + print("co yield: ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i = 1, 3 do + print("co resume, status: ", coroutine.status(c)) + cr(c) + end + } + } +--- request +GET /t +--- response_body +coroutine API +--- grep_error_log eval: qr/co (?:yield: \d+|resume, status: \w+)/ +--- grep_error_log_out +co resume, status: suspended +co yield: 0 +co resume, status: suspended +co yield: 1 +co resume, status: suspended +co yield: 2 +--- no_error_log +[error] +[alert] + + + +=== TEST 17: simple user thread wait with yielding +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple user thread wait with yielding") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + local function f() + ngx.sleep(0.01) + print("uthread: hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "uthread: failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + print("uthread: thread created: ", coroutine.status(t)) + + local ok, res = ngx.thread.wait(t) + if not ok then + print("uthread: failed to wait thread: ", res) + return + end + + print("uthread: ", res) + } + } +--- request +GET /t +--- response_body +simple user thread wait with yielding +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: thread created: running while loading proxy ssl verify by lua +uthread: hello in thread while loading proxy ssl verify by lua +uthread: done while loading proxy ssl verify by lua + + + +=== TEST 18: uthread (kill) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("uthread (kill)") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + local function f() + ngx.log(ngx.INFO, "uthread: hello from f()") + ngx.sleep(1) + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + local ok, res = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.ERR, "failed to kill thread: ", res) + return + end + + ngx.log(ngx.INFO, "uthread: killed") + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.INFO, "uthread: failed to kill: ", err) + end + } + } +--- request +GET /t +--- response_body +uthread (kill) +--- no_error_log +[error] +[alert] +[emerg] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: hello from f() while loading proxy ssl verify by lua +uthread: killed while loading proxy ssl verify by lua +uthread: failed to kill: already waited or killed while loading proxy ssl verify by lua + + + +=== TEST 19: ngx.exit(ngx.OK) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(ngx.OK) - no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.exit(ngx.OK) + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- response_body +ngx.exit(ngx.OK) - no yield +--- error_log eval +[ +'proxy_ssl_verify_by_lua: handler return value: 0, cert verify callback exit code: 1', +qr/\[debug\] .*? SSL_do_handshake: 1/, +'lua exit with code 0', +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 20: proxy_ssl_verify_by_lua* without yield API (simple logic) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("without yield API, simple logic") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + print("proxy ssl verify: simple test start") + + -- Simple calculations without yield + local sum = 0 + for i = 1, 10 do + sum = sum + i + end + + print("proxy ssl verify: calculated sum: ", sum) + + -- String operations + local str = "hello" + str = str .. " world" + print("proxy ssl verify: concatenated string: ", str) + + -- Table operations + local t = {a = 1, b = 2, c = 3} + local count = 0 + for k, v in pairs(t) do + count = count + v + end + print("proxy ssl verify: table sum: ", count) + + print("proxy ssl verify: simple test done") + } + } +--- request +GET /t +--- response_body +without yield API, simple logic +--- grep_error_log eval: qr/(proxy ssl verify: simple test start|proxy ssl verify: calculated sum: 55|proxy ssl verify: concatenated string: hello world|proxy ssl verify: table sum: 6|proxy ssl verify: simple test done)/ +--- grep_error_log_out +proxy ssl verify: simple test start +proxy ssl verify: calculated sum: 55 +proxy ssl verify: concatenated string: hello world +proxy ssl verify: table sum: 6 +proxy ssl verify: simple test done + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 21: lua_upstream_skip_openssl_default_verify default off +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua_upstream_skip_openssl_default_verify default off") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") + } + } +--- request +GET /t +--- error_log +proxy_ssl_verify_by_lua: openssl default verify +--- no_error_log +[error] +[alert] + + + +=== TEST 22: lua_upstream_skip_openssl_default_verify on +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua_upstream_skip_openssl_default_verify default off") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + lua_upstream_skip_openssl_default_verify on; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") + } + } +--- request +GET /t +--- response_body +lua_upstream_skip_openssl_default_verify default off +--- error_log +proxy ssl verify by lua is running! +--- no_error_log +proxy_ssl_verify_by_lua: openssl default verify +[error] +[alert] + + + +=== TEST 23: ngx.ctx to pass data from downstream phase to upstream phase +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + rewrite_by_lua_block { + ngx.ctx.greeting = "I am from rewrite phase" + } + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "greeting: ", ngx.ctx.greeting) + } + } +--- request +GET /t +--- response_body +simple logging return +--- error_log +greeting: I am from rewrite phase +--- no_error_log +[error] +[alert] + + + +=== TEST 24: upstream connection aborted +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + proxy_connect_timeout 100ms; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.2) + } + } +--- request +GET /t +--- error_code: 504 +--- response_body_like: 504 Gateway Time-out +--- error_log +upstream timed out (110: Connection timed out) while loading proxy ssl verify by lua +proxy_ssl_verify_by_lua: cert verify callback aborted +--- no_error_log +[alert] +--- wait: 0.5 + + + +=== TEST 25: cosocket +--- http_config + server { + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1; + server_name test.com; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { + ngx.sleep(0.1) + + ngx.status = 201 + ngx.say("foo") + ngx.exit(201) + } + more_clear_headers Date; + } + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + ngx.log(ngx.INFO, "connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.log(ngx.ERR, "failed to send http request: ", err) + return + end + + ngx.log(ngx.INFO, "sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.log(ngx.ERR, "failed to receive response status line: ", err) + break + end + + ngx.log(ngx.INFO, "received: ", line) + end + + local ok, err = sock:close() + ngx.log(ngx.INFO, "close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } +--- request +GET /t +--- response_body +simple logging return +--- error_log +connected: 1 +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil +--- no_error_log +[error] +[alert] diff --git a/t/185-ngx-buf-double-free.t b/t/185-ngx-buf-double-free.t new file mode 100644 index 0000000000..699083db27 --- /dev/null +++ b/t/185-ngx-buf-double-free.t @@ -0,0 +1,25 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket 'no_plan'; + +repeat_each(2); + +run_tests(); + +__DATA__ + +=== TEST 1: one buf was linked to multiple ngx_chain_t nodes +--- config + location /t { + content_by_lua_block { + local str = string.rep(".", 1300) + ngx.print(str) + ngx.flush() + ngx.print("small chunk") + ngx.flush() + } + body_filter_by_lua_block {local dummy=1} + } +--- request +GET /t +--- response_body_like: small chunk diff --git a/t/186-cosocket-busy-bufs.t b/t/186-cosocket-busy-bufs.t new file mode 100644 index 0000000000..7439443761 --- /dev/null +++ b/t/186-cosocket-busy-bufs.t @@ -0,0 +1,91 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + + +use Test::Nginx::Socket; +use Test::Nginx::Socket::Lua::Stream; + +log_level('warn'); +repeat_each(2); + +if (defined $ENV{TEST_NGINX_USE_HTTP3}) { + plan(skip_all => "HTTP3 does not support client abort"); +} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) { + plan(skip_all => "HTTP2 does not support client abort"); +} else { + plan tests => repeat_each() * (blocks() * 2); +} + +run_tests(); + +__DATA__ + +=== TEST 1: ngx.say and cosocket +--- stream_server_config + content_by_lua_block { + local sock = assert(ngx.req.socket(true)) + sock:settimeout(1000) + while true do + local data = sock:receive(5) + if not data then + return + end + ngx.print(data) + ngx.flush(true) + end + } +--- config + location /test { + content_by_lua_block { + ngx.say("hello") + --ngx.flush(true) + + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", ngx.var.server_port + 1) + assert(ok) + + local last_duration = 0 + local cnt = 0 + local t1, t2 + local err_cnt = 0 + local ERR_THRESHOLD_MS = 100 + + for i = 1,100000 do + if cnt == 0 then + ngx.update_time() + t1 = ngx.now() + end + + cnt = cnt + 1 + + local sent = sock:send("hello") + local data = sock:receive(5) + assert(data=="hello") + + if cnt == 1000 then + cnt = 0 + ngx.update_time() + t2 = ngx.now() + local duration = (t2 - t1) * 1000 + if last_duration > 0 and (duration - last_duration) > ERR_THRESHOLD_MS then + if err_cnt >= 3 then + ngx.log(ngx.ERR, + "more than ", err_cnt, " times, duration larger than ", + ERR_THRESHOLD_MS, " ms, ", + "last_duration: ", math.floor(duration), " ms") + return ngx.exit(500) + end + err_cnt = err_cnt + 1 + end + last_duration = duration + end + end + + sock:close() + ngx.exit(200) + } + } +--- no_error_log +[error] +--- timeout: 30 +--- request +GET /test diff --git a/t/187-ssl-two-verification.t b/t/187-ssl-two-verification.t new file mode 100644 index 0000000000..bea4aaee0f --- /dev/null +++ b/t/187-ssl-two-verification.t @@ -0,0 +1,145 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL (0\S*|1\.0\S*|1\.1\.0\S*)/) { + plan(skip_all => "too old OpenSSL, need >= 1.1.1, was $1"); +} elsif ($openssl_version =~ m/running with BoringSSL/) { + plan(skip_all => "does not support BoringSSL"); +} else { + plan tests => repeat_each() * (blocks() * 7); +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: simple logging +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + #listen 127.0.0.1:4433 ssl; + server_name test.com; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + #ssl_trusted_certificate ../../cert/test.crt; + ssl_client_certificate ../../cert/test.crt; + ssl_verify_client on; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + log_by_lua_block { + ngx.log(ngx.INFO, "ssl_client_s_dn: ", ngx.var.ssl_client_s_dn) + } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_certificate ../../cert/test.crt; + lua_ssl_certificate_key ../../cert/test.key; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + -- local ok, err = sock:connect("127.0.0.1", 4433) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: cdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +ssl_client_s_dn: emailAddress=agentzh@gmail.com,CN=test.com,OU=OpenResty,O=OpenResty,L=San Francisco,ST=California,C=US + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/ssl_client_hello_by_lua\(.*?,|\bssl client hello: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log_out eval +# Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0) +# before call ssl callback function +$Test::Nginx::Util::NginxVersion >= 1.017009 ? +qr/reusable connection: 0 +ssl client hello: connection reusable: 0 +ssl_client_hello_by_lua\(nginx.conf:\d+\):1: ssl client hello by lua is running!,/ +: qr /reusable connection: 1 +ssl client hello: connection reusable: 1 +reusable connection: 0 +ssl_client_hello_by_lua\(nginx.conf:\d+\):1: ssl client hello by lua is running!,/ diff --git a/t/188-balancer_keepalive_pool_max_retry.t b/t/188-balancer_keepalive_pool_max_retry.t new file mode 100644 index 0000000000..679ee680f8 --- /dev/null +++ b/t/188-balancer_keepalive_pool_max_retry.t @@ -0,0 +1,89 @@ +# vim:set ft= ts=4 sw=4 et: + +use Test::Nginx::Socket::Lua; +use Cwd qw(cwd); + +log_level('info'); +repeat_each(1); + +plan tests => repeat_each() * (blocks() * 6); + +my $pwd = cwd(); + +no_long_string(); + +check_accum_error_log(); +run_tests(); + +__DATA__ + +=== TEST 1: sanity +--- http_config + lua_shared_dict request_counter 1m; + upstream my_upstream { + server 127.0.0.1; + balancer_by_lua_block { + local balancer = require "ngx.balancer" + + if not ngx.ctx.tries then + ngx.ctx.tries = 0 + end + + ngx.ctx.tries = ngx.ctx.tries + 1 + ngx.log(ngx.INFO, "tries ", ngx.ctx.tries) + + if ngx.ctx.tries == 1 then + balancer.set_more_tries(5) + end + + local host = "127.0.0.1" + local port = $TEST_NGINX_RAND_PORT_1; + + local ok, err = balancer.set_current_peer(host, port) + if not ok then + ngx.log(ngx.ERR, "failed to set the current peer: ", err) + return ngx.exit(500) + end + + balancer.set_timeouts(60000, 60000, 60000) + + local ok, err = balancer.enable_keepalive(60, 100) + if not ok then + ngx.log(ngx.ERR, "failed to enable keepalive: ", err) + return ngx.exit(500) + end + } + } + + server { + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1; + location /hello { + content_by_lua_block{ + local request_counter = ngx.shared.request_counter + local first_request = request_counter:get("first_request") + if first_request == nil then + request_counter:set("first_request", "yes") + ngx.print("hello") + else + ngx.exit(ngx.HTTP_CLOSE) + end + } + } + } +--- config + location = /t { + proxy_pass http://my_upstream; + proxy_set_header Connection "keep-alive"; + + rewrite_by_lua_block { + ngx.req.set_uri("/hello") + } + } +--- pipelined_requests eval +["GET /t HTTP/1.1" , "GET /t HTTP/1.1"] +--- response_body eval +["hello", qr/502/] +--- error_code eval +[200, 502] +--- no_error_log eval +qr/tries 7/ diff --git a/t/cert/dst-ca.crt b/t/cert/dst-ca.crt index 738121f63a..7b714c60a9 100644 --- a/t/cert/dst-ca.crt +++ b/t/cert/dst-ca.crt @@ -1,63 +1,26 @@ -subject=/C=US/O=Digital Signature Trust Co./OU=TrustID Server/CN=TrustID Server CA A5 -issuer=/O=Digital Signature Trust Co./CN=DST Root CA X3 +# Comodo AAA Services root -----BEGIN CERTIFICATE----- -MIIGiTCCBXGgAwIBAgIRALCqqJD4YLUEJLy76IZcvf8wDQYJKoZIhvcNAQEFBQAw -PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD -Ew5EU1QgUm9vdCBDQSBYMzAeFw0wNzA4MjQxNzU0MTlaFw0xNDA4MjQxNDEzMzVa -MGsxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVz -dCBDby4xFzAVBgNVBAsTDlRydXN0SUQgU2VydmVyMR0wGwYDVQQDExRUcnVzdElE -IFNlcnZlciBDQSBBNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOae -jyufM+mrKgTCpPm9ha5UBAORNLhAv3PFlmzA8n6lnEvBTJfeG5vUZYQrlZMMKmZl -F1D3mYRFAhT/qFbBsTMlF9XsDNF40NB21ABTquuEB3Ji8mmC1Z2cZjwa4VeKxGIq -kTdBypQgWekopNVvoOGLPbRXgYKW4UCc6pt7Q+We+eUuonp2XIRIOvmJQtXb/rNz -4B1ahu3QB4HnystmQ9dW8BySSGDhZ/kzIcSU8GuHcL58SHsei07gjFzXRhMkkbJH -MT8zS/hrjNsB8Z5kamI4QL7nLVNuCF1Sg9zgB1POdMPLEnm4uDx+BRLwVMGZMwza -MNfEATnWTvLazzIDarMCAwEAAaOCA1IwggNOMA8GA1UdEwEB/wQFMAMBAf8wDgYD -VR0PAQH/BAQDAgHGMEcGA1UdJQRAMD4GCCsGAQUFBwMBBggrBgEFBQcDAwYIKwYB -BQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBwYKKwYBBAGCNwoDATCCATIGA1UdIASC -ASkwggElMIIBIQYKYIZIAYb5LwAGAzCCAREwSgYIKwYBBQUHAgEWPmh0dHBzOi8v -c2VjdXJlLmlkZW50cnVzdC5jb20vY2VydGlmaWNhdGVzL3BvbGljeS90cy9pbmRl -eC5odG1sMIHCBggrBgEFBQcCAjCBtRqBslRoaXMgVHJ1c3RJRCBTZXJ2ZXIgQ2Vy -dGlmaWNhdGUgaGFzIGJlZW4gaXNzdWVkIGluIGFjY29yZGFuY2Ugd2l0aCBJZGVu -VHJ1c3QncyBUcnVzdElEIENlcnRpZmljYXRlIFBvbGljeSBmb3VuZCBhdCBodHRw -czovL3NlY3VyZS5pZGVudHJ1c3QuY29tLyBjZXJ0aWZpY2F0ZXMvcG9saWN5L3Rz -L2luZGV4Lmh0bWwwgbkGA1UdHwSBsTCBrjAuoCygKoYoaHR0cDovL2NybC5pZGVu -dHJ1c3QuY29tL0RTVFJPT1RDQVgzLmNybDB8oHqgeIZ2bGRhcDovL2xkYXAuaWRl -bnRydXN0LmNvbS9jbj1EU1QlMjBSb290JTIwQ0ElMjBYMyxvPURpZ2l0YWwlMjBT -aWduYXR1cmUlMjBUcnVzdCUyMENvLj9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0 -O2JpbmFyeTCBrwYIKwYBBQUHAQEEgaIwgZ8wJQYIKwYBBQUHMAGGGWh0dHA6Ly9v -Y3NwLmlkZW50cnVzdC5jb20wdgYIKwYBBQUHMAKGamxkYXA6Ly9sZGFwLmlkZW50 -cnVzdC5jb20vY249RFNUJTIwUm9vdCUyMENBJTIwWDMsbz1EaWdpdGFsJTIwU2ln -bmF0dXJlJTIwVHJ1c3QlMjBDby4/Y0FDZXJ0aWZpY2F0ZTtiaW5hcnkwHwYDVR0j -BBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAwHQYDVR0OBBYEFLeoy2iu3EVD0tyJ -EamBZhn5+3XpMA0GCSqGSIb3DQEBBQUAA4IBAQAtreoVqLQx+G35L8byM/IvGqNa -MzbWYzGIidH3+hT8B2e0duWfZCDazgu272ErWL/T4HL5psUEOIHfzNxEWHA24XhY -zBFsYR64vaTRV7T+iYKv5Nxptidk26zOZ2/F+TKZvOfhPg5IqnH3FyXHEcRDWm1/ -QJsZ4mqgUFx38pc50i1ed6XUdvBObMnGJc1fGdfVqcNqbvHACNrw+WJE9PIeMKJQ -HC/NkE87xFP/Q1OW7hPmqQvmIIbm6BLZ7U0kxqNL3HmegxkJ2yJQntsX+5BooJqj -/uVpwzAEei7IjKsdtomxkHCDPFGEJYBldDLBH0ScSlngQZ6V5HNM473l8bWs +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- - -subject=/O=Digital Signature Trust Co./CN=DST Root CA X3 -issuer=/O=Digital Signature Trust Co./CN=DST Root CA X3 ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow -PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD -Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O -rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq -OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b -xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw -7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD -aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV -HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG -SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 -ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr -AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz -R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 -JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo -Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- - diff --git a/t/cert/gen-test-passphrase.sh b/t/cert/gen-test-passphrase.sh new file mode 100755 index 0000000000..45e6806a39 --- /dev/null +++ b/t/cert/gen-test-passphrase.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Set variables +SUBJECT="/C=CN/ST=Guangdong/L=ShenZhen/O=OpenResty/OU=OpenResty/CN=test.com/emailAddress=guanglinlv@gmail.com" +DAYS=49000 # Approximately 134 years +KEY_FILE="test_passphrase.key" +CERT_FILE="test_passphrase.crt" +PASSWORD="123456" + +# Generate a new 2048-bit RSA private key, encrypted with the password +openssl genrsa -aes256 -passout pass:$PASSWORD -out $KEY_FILE 2048 + +# Generate a new self-signed certificate +openssl req -x509 -new -nodes -key $KEY_FILE -sha256 -days $DAYS \ + -out $CERT_FILE -subj "$SUBJECT" -passin pass:$PASSWORD + +# Display information about the new certificate +openssl x509 -in $CERT_FILE -text -noout + +echo "New 2048-bit certificate generated successfully!" +echo "Private key (encrypted): $KEY_FILE" +echo "Certificate: $CERT_FILE" + diff --git a/t/cert/gen-test2.sh b/t/cert/gen-test2.sh new file mode 100755 index 0000000000..ed7a9de398 --- /dev/null +++ b/t/cert/gen-test2.sh @@ -0,0 +1,3 @@ +openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \ + -subj "/C=US/ST=California/L=San Francisco/O=OpenResty/CN=test2.com/emailAddress=openresty@gmail.com" \ + -keyout test2.key -out test2.crt diff --git a/t/cert/http3/http3.crt b/t/cert/http3/http3.crt new file mode 100644 index 0000000000..8683f41adf --- /dev/null +++ b/t/cert/http3/http3.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFCTCCAvGgAwIBAgIUHLeNm7JwH368JWXBYJ1Dv+xcL6kwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIxMTEwNTA2NTQxNVoXDTQxMDEw +NDA2NTQxNVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAx5/M18tKWKfacgldf9gBguTdLA3JWiblRWM/38fSZGKL +Mcb1vLErY/qQyLYxoLKBht0FUZAEZ08y/iheYFZT7H082b8bNvwY+v6bScOQOvcX +hkTWNlSQORDH4qIFxVXq0soXga+0ukSZ2RQRcCUWeKUaZwhGCYNIj04/FB9Lef95 +Ku7LkNauTBmRGIwXgQWhfnoPM21o9D9i54R9L2RHU7fGeTGhiS0nCe1nPPB7KBgO +s9rBHMXV9qHxCNMWWiVNsMX049S7aD9yvRrV7NAHssVQdaRR234IPJxb7BYSUPi8 +5U+8l43Ornd+cY/R/sXDQluFidlnZvHT+akdy0ObWDK0lMUweDWvFVLY3ZvMvsGR +rQE8RR5/fy83y7w//0734EBk4ttEzlilmjA8UvnumOpK0UYaW8LkikvxB+48e89s +NOEKZf3Mfw/fDRz0tO+cr4cIgUPQ4ru6KNVnGH/ZiD97AVMPXJO9nPOUIRVH9aXM +wC74CSt5idWOwpTKy+sLg7anM235NvZ9bTiS+V5CTzBlqL/wKzEI0Injds3kBc1a +Y8Lk/cIdNhuwlN01fYluA8XyB3DWoeQYbySEoHC5ksvaFLBM3yPomWrmyM5lJrj5 +QbWa46b0bONJQ2qmrQa5KREVvDthHQvKELsabHX1qbCShSxoG45aclqpmKy2AjcC +AwEAAaNTMFEwHQYDVR0OBBYEFA2T9Sgv31hCl3INL5MB++NrMu0iMB8GA1UdIwQY +MBaAFA2T9Sgv31hCl3INL5MB++NrMu0iMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBAJdxtUq4T/sh5Ww7pMOB4+JF5zsxEBpZiFMzT2hYZ+6a2mYh +UTy7ff1lNfNl0BfslaOD404Qt0SSgI2TByLs838/sKpVk+QePcw6kl6IFIPERUBO +uxq/+QXqsDHOXb6m4qpEBmrknUa65dThdUw5r4sHo0XD2l/y2/zEZtd4e3ZGAWts +spdjPBk4UHPtG3p29eY/8ehw+zDXuWG4nlJYjn+6PRnLhvpgt2/E/Wr9PeYcv3IC +UgavxXVtk9fnclg8BuKlnZYki7txn8+F9Rh03CVVZ9R16Q/aVALI2iTs8T4gCt7Z +eexfLGfBkLBkLvpL7wpzxNsvOC2b5bJZCDUTprLVmdpmMQu75qLg1mOfubMo983H +8G91V4XOokRoCRub/SLKA16/gpEwnE2aDsVMUVSxwpRu2Rjw4GpzbCNAHUzmblrh +zYMSAsEuTcsZEAdZQrzmhGc1Yg5Q88V4o+qyywzkgd86O65QUozhnkCs+eS9ikMV +cPLXoW5SDIsrrcoTR6bH5MdDjS7ILKUUC5+x0qo6EhK94Fx49TkRBNIYk3o0fG7j +o/0YvozXjqTRnodYegL4LKoGZyfL4qbuh3t8ZGQ/Z0ECmvjcmJzPyObIiMe2InT3 +GRY+ypPTyeiumjHFFVO0zx+DAv+HFPtq1XaygWvxKY1DTP6FNN0BzQdzAgKm +-----END CERTIFICATE----- diff --git a/t/cert/http3/http3.key b/t/cert/http3/http3.key new file mode 100644 index 0000000000..5825540f58 --- /dev/null +++ b/t/cert/http3/http3.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDHn8zXy0pYp9py +CV1/2AGC5N0sDclaJuVFYz/fx9JkYosxxvW8sStj+pDItjGgsoGG3QVRkARnTzL+ +KF5gVlPsfTzZvxs2/Bj6/ptJw5A69xeGRNY2VJA5EMfiogXFVerSyheBr7S6RJnZ +FBFwJRZ4pRpnCEYJg0iPTj8UH0t5/3kq7suQ1q5MGZEYjBeBBaF+eg8zbWj0P2Ln +hH0vZEdTt8Z5MaGJLScJ7Wc88HsoGA6z2sEcxdX2ofEI0xZaJU2wxfTj1LtoP3K9 +GtXs0AeyxVB1pFHbfgg8nFvsFhJQ+LzlT7yXjc6ud35xj9H+xcNCW4WJ2Wdm8dP5 +qR3LQ5tYMrSUxTB4Na8VUtjdm8y+wZGtATxFHn9/LzfLvD//TvfgQGTi20TOWKWa +MDxS+e6Y6krRRhpbwuSKS/EH7jx7z2w04Qpl/cx/D98NHPS075yvhwiBQ9Diu7oo +1WcYf9mIP3sBUw9ck72c85QhFUf1pczALvgJK3mJ1Y7ClMrL6wuDtqczbfk29n1t +OJL5XkJPMGWov/ArMQjQieN2zeQFzVpjwuT9wh02G7CU3TV9iW4DxfIHcNah5Bhv +JISgcLmSy9oUsEzfI+iZaubIzmUmuPlBtZrjpvRs40lDaqatBrkpERW8O2EdC8oQ +uxpsdfWpsJKFLGgbjlpyWqmYrLYCNwIDAQABAoICAENb1qESRbn4matVIamb15a1 +ZzQQStsSuNZbERiPspyQ6+sV+aF8HuoTiHtRjxlsYmyBc+P7tqCthsVgFchoGNV5 +xOispaA+HKfE9d1EEgzzh4qU+7tFeYzn7qq4hT37KcuKybfG9DLOJyOqs9+lhBmd +jHUrw4Y+OGOywXImxS8bV2V3QlVTO2kOT3l6/AtbPQ0SXsK5rmqMYPFCMYOmULMd +FembJ6jEBaJB604Sz1vOElf5/qOY1gPszQpvP+GXKMn3YhTmmX4puqu4vGq2H4Lh +Na8cjUqFEn5xPEtDf1a3N/Ygm8B/5zfTtmTXZMKVNLfVbg//vfZsr1xVBmqqG2Zk +PUxZ9pyRn25zAYIgIC6DBgmM32S5Zl0axeoWIHdgOTAOwqLLF/ZFozdVSN+skJhl +q1ndTw+MmVHi19HDUt8YfIICn8g+Dw9NRdX+VjXrRMBweCL41JzHzP6YKo73VJty +WIVt/LwH2sxdGH0QeP6+Dajel3/ouXAcS31ZpeWZ0QUfa5I0DOn50mvZSuaC0m7Z +baHBGqawq9EKdjbU5WN6popOCWCx23BZDorKmPGBQ4x54LxfUeet0HAYjsvzUZ0q +a/AC6pMMHUjG4i5dPhOAhjxkpwfhLjIBTC8VrelGfap50Mlst5InCjClUsSKg43w +tsRg0HQflxYsPXUGtBqxAoIBAQD29Q6gPNsd7JMzmVzK7fy5YaKq7aI/vOU/tp7U +LQqhBfsy70U3UNsclsQQ/1RG2cgpFVctlpE8ITpFqSlUDRtlvrK9NaIPEYBV84Xn +bWPqFjGiRowuXiCR8axbPnsEquO2+VHNuVD/PsCocvbpqtpbfEGmcuyWiO4uFMyW +beOcRxEsn3U7ABZGDIhxYjpmkiVMu1pRjj+ddqtNMia398K86c3ETRg+zrHnpzio +kuhxv6TjtnXT1ibI0yKzdQ5sKpa3DYUN/wJgeFVHsSuCZjCzufcMdjCjG3kTTljT +FrMbweISSTJNs6KsbCqzmc6dmpwuU1ySX07F9sgdJ/7lBCXTAoIBAQDO7wy9Zr8C +Ridte1EaMP04OSfqc2Jha/U2te47R1VDyHKzpFHvHwWHqFng6J181M9iubUsh/63 +d12uyjZoToMgT7AJ7PMwbEKJbMxpIjqF7Dp1aM11TjRq0mQbZ3wkeuYdK7jYc92j +X4hF+tJpCYo/0lMA0UFYhMyUPbi33xqlJKLghVncBYmKHluf3k0g73eRg4MGlYHG +tZ5qVdgwFTztpBb4ySJMOYmjb/OoYPdfoLC8P4ZoHiax4MU30R8b/oGCmYzwOsMC +dNz9dZgeCFo3+lrzMUnIh3y+YcGo2JJ9ZW1LxGvEIU0aEQ7mSPV8k7m3QiH2MiPQ +P0t17w1hQP+NAoIBAHJch1pi9CGGZaB2e8cpsGf0s8yt4P3dLthzbFfbR9nLmEk9 +DnOQSPeTRdaNNuzce1mzHTzqRfVvebm6nX3j1/Uk+0atqI+Lzj9/V1oViThk8LUy +MEZkpnaPUP6sD3HY5TzddilrkPuyhqs7GeaZjSbigtBe1frcDFhgn2FmIApFysk8 +SqB46NelhCXllB/du9ItzKSJ2CHGS4ujFtUIsjCjoPsvrHOhajdZc950sZnDYstk +umnP+QP06lPqeDRVAJhidWRG3EXqU6uwevKW+iSwkJw/u0Q9O7NaC74s++J1xYgs +R1Q+RK3OJXQoXMsVRxAY4HyUEDmSj5cY52wMoKsCggEAQMXu9PJOY8XV3Z02G76t +5IVviyGm79u9G+0Cryd69watcLHEu9a4AmieCZqGgWaTq9F5doDzKDaC6o19TlUV +Em4fKlwzGzsn8KBPs7D1JKp2+f1eIpPiMHW+xB02bKzTjtn6uDY8cEEdBNqoNhy4 +W5XYSW82xyB6cQSI53U8f+jh2umi4Q4SqVsrTvVkqySKBtBlmQ//WVXMSniofRSI +x9IPJry+saFpBfGrEU+Y3yQLbkFsLvcRIai70ubwl/CoVVr/FMsv83rlGalPfkcb +Bl6lTW5mLBDM6ULsPY/c+sde2NKY8QGDgt9IDKlVvjL3dPeMbeXv8+V8F2RGieSw +mQKCAQEA1n4t4CLKZMWZ4oonBEp8u9rjK23ObCSxANsfoIw94v2zHTFqLraFg9aH +s+DIc6M/XWX+Ie55v1QuYt8LrMtc9/rtOJdISybAtnheSqjQ+IkTEvBab/8pqhYg +Jhv/RxmBnCLwiIzRGVpxv9/5bbKXq1JKgdQKO3RBG53lcFFMO1O5srhKqj2KgAHv +XCQxBmtj83e6gp6hvUOU4YC2aKyL9QqNVndGzPJikEguyvJPUFw6RoB0StVQnzLY +UOgIH+8VTjzL90nQ/JitVT8uGdw1Ge5xJfCXDe/PEYpDsY65DKPrIMWFeQs6T/Mb +nvNeBReQOuYYpcCc2GM96RMUz3iyoA== +-----END PRIVATE KEY----- diff --git a/t/cert/mtls_ca.crt b/t/cert/mtls_ca.crt new file mode 100644 index 0000000000..1fe7e1f985 --- /dev/null +++ b/t/cert/mtls_ca.crt @@ -0,0 +1,78 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 32:ed:21:56:d8:4e:aa:03:89:a9:4a:a4:e2:85:2d:8a:3b:2b:89:22 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA + Validity + Not Before: Mar 13 15:49:00 2022 GMT + Not After : Mar 8 15:49:00 2042 GMT + Subject: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:e6:37:d2:c6:17:36:c7:b2:7f:7d:cf:d0:62:87: + 99:d9:21:b8:de:ff:d8:e2:3a:1c:68:90:8f:ce:17: + 68:22:b0:60:30:cc:29:e8:34:ee:ff:b2:25:de:6e: + 1a:d4:df:10:19:11:4b:40:61:d3:a9:4d:80:ed:97: + 81:4e:c5:74:e8:4d:63:e3:5f:21:bc:5a:6e:22:a0: + 17:91:c1:cb:25:53:9b:9d:4e:e1:51:5b:f6:52:e7: + 0a:27:f6:16:c2:31:cb:6c:47:f4:89:51:15:cc:06: + be:31:3e:1c:ea:ee:81:9b:c4:97:96:fd:e5:1c:95: + 9e:c0:65:cd:a9:9a:cb:68:67:f2:62:a0:21:eb:5a: + c5:a1:92:ed:32:41:28:f9:47:34:eb:44:ae:d6:e7: + 76:71:11:98:c9:2e:ce:6c:7c:10:1b:c7:4c:c3:14: + 89:4e:d9:4c:d9:c7:43:e9:3c:29:ca:62:a9:91:b3: + 87:e7:d7:b4:18:ab:65:f9:6b:ed:82:ca:a1:36:35: + 18:05:cb:5c:24:26:13:13:f8:99:ac:99:be:9b:a6: + 73:df:0d:16:95:b1:dc:be:fe:7a:c2:b6:dc:c8:93: + cf:10:e0:29:03:0e:28:78:18:84:ee:14:92:ab:be: + 5a:a0:14:a2:4a:2f:d3:d0:b8:0e:00:d2:5a:cd:e4: + bd:a1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Subject Key Identifier: + F0:D7:4B:14:73:E1:67:00:6B:54:B4:19:20:76:12:9F:9D:8E:C8:09 + Signature Algorithm: sha256WithRSAEncryption + 6d:52:21:6d:6e:8c:e5:4a:28:07:65:6d:d8:7c:23:2e:c6:c1: + d0:ec:27:b3:b0:c3:d3:e8:fa:72:b9:de:32:4e:ff:97:8d:86: + a9:6d:b3:a9:b4:2d:77:ca:28:97:6a:3d:7b:a2:15:ed:34:dc: + 72:9f:6f:e7:01:0c:d3:28:6a:80:1b:50:09:fd:d7:2c:d8:92: + d5:10:c4:73:15:20:7d:99:dc:de:30:7b:3c:6e:e9:66:b2:0e: + 4e:1a:c1:51:57:6e:5b:b0:a9:f6:ff:0b:8f:07:67:31:40:5b: + 11:a9:06:d3:d3:76:c5:d2:56:95:9a:9e:4a:16:44:4b:32:e5: + af:dd:4b:4d:5d:57:b8:85:69:36:93:2a:c6:0c:8f:e1:42:35: + be:8e:f3:e7:35:d3:2c:3a:03:31:40:75:8e:e8:dd:57:35:20: + 5e:18:a9:76:ce:85:be:7e:3a:cf:6e:08:58:5b:47:d5:e9:c4: + ec:0e:e9:8e:3c:2d:5c:7b:59:20:5b:24:92:a0:e0:1e:a3:5a: + 67:d8:ff:7f:a5:82:f1:df:db:05:65:79:88:b1:3c:e6:01:d1: + 5a:c7:d2:6e:9a:e6:a2:da:4a:c7:19:78:d9:14:71:6e:1f:70: + f3:41:e5:b3:78:31:d5:22:0e:7c:1a:b2:43:d9:86:ff:53:ea: + 2b:ba:d2:27 +-----BEGIN CERTIFICATE----- +MIIDhDCCAmygAwIBAgIUMu0hVthOqgOJqUqk4oUtijsriSIwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAoT +CU9wZW5SZXN0eTEiMCAGA1UEAxMZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe +Fw0yMjAzMTMxNTQ5MDBaFw00MjAzMDgxNTQ5MDBaMFoxCzAJBgNVBAYTAlVTMRMw +EQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQKEwlPcGVuUmVzdHkxIjAgBgNVBAMT +GU9wZW5SZXN0eSBUZXN0aW5nIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDmN9LGFzbHsn99z9Bih5nZIbje/9jiOhxokI/OF2gisGAwzCno +NO7/siXebhrU3xAZEUtAYdOpTYDtl4FOxXToTWPjXyG8Wm4ioBeRwcslU5udTuFR +W/ZS5won9hbCMctsR/SJURXMBr4xPhzq7oGbxJeW/eUclZ7AZc2pmstoZ/JioCHr +WsWhku0yQSj5RzTrRK7W53ZxEZjJLs5sfBAbx0zDFIlO2UzZx0PpPCnKYqmRs4fn +17QYq2X5a+2CyqE2NRgFy1wkJhMT+Jmsmb6bpnPfDRaVsdy+/nrCttzIk88Q4CkD +Dih4GITuFJKrvlqgFKJKL9PQuA4A0lrN5L2hAgMBAAGjQjBAMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTw10sUc+FnAGtUtBkgdhKf +nY7ICTANBgkqhkiG9w0BAQsFAAOCAQEAbVIhbW6M5UooB2Vt2HwjLsbB0Owns7DD +0+j6crneMk7/l42GqW2zqbQtd8ool2o9e6IV7TTccp9v5wEM0yhqgBtQCf3XLNiS +1RDEcxUgfZnc3jB7PG7pZrIOThrBUVduW7Cp9v8LjwdnMUBbEakG09N2xdJWlZqe +ShZESzLlr91LTV1XuIVpNpMqxgyP4UI1vo7z5zXTLDoDMUB1jujdVzUgXhipds6F +vn46z24IWFtH1enE7A7pjjwtXHtZIFskkqDgHqNaZ9j/f6WC8d/bBWV5iLE85gHR +WsfSbprmotpKxxl42RRxbh9w80Hls3gx1SIOfBqyQ9mG/1PqK7rSJw== +-----END CERTIFICATE----- diff --git a/t/cert/mtls_ca.key b/t/cert/mtls_ca.key new file mode 100644 index 0000000000..d39b42f9d9 --- /dev/null +++ b/t/cert/mtls_ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA5jfSxhc2x7J/fc/QYoeZ2SG43v/Y4jocaJCPzhdoIrBgMMwp +6DTu/7Il3m4a1N8QGRFLQGHTqU2A7ZeBTsV06E1j418hvFpuIqAXkcHLJVObnU7h +UVv2UucKJ/YWwjHLbEf0iVEVzAa+MT4c6u6Bm8SXlv3lHJWewGXNqZrLaGfyYqAh +61rFoZLtMkEo+Uc060Su1ud2cRGYyS7ObHwQG8dMwxSJTtlM2cdD6TwpymKpkbOH +59e0GKtl+WvtgsqhNjUYBctcJCYTE/iZrJm+m6Zz3w0WlbHcvv56wrbcyJPPEOAp +Aw4oeBiE7hSSq75aoBSiSi/T0LgOANJazeS9oQIDAQABAoIBAQDhH9+uNE8uUv/X +MNvvLfklWpOlBf25o+fZ3NuzRjJgEafOsCee2fyI8FWVwIfeeE8OpFm5GLDZk1+r +dwdM10xuSheO5Z1gyfF/TJwfvamA09SNrPArFkm3YhUNZNl2hykMtwSLL06oWEOu +dbXjit4VS9aNIbTlEe7O5/6Ih0W3zmr1yvUua2swmAZMx3GFA4kbjZZ9vDs27sdu +K+VY3DYRbq1HkiNFT0otfke5bObFBCG7Yp8JLyhYaIkGYFoBXuZ6JNY8EuU2+YyP +6r40tJ7StR1Q6eZJh9/1leaYGZLCh5oFyKpilTuxHbRbr5A28RJKjKvPsdDgTtQn +yHGg70FRAoGBAOhC3TQlFcT2WCCZHHql9JEEHnHVBWnL3Jg7VJuL1i6pEIz7qQkW +AtBEIY/nnTcVNfJ6eXznYtutYvvRSgQTUsBNRoj3s1z9wKOo4uw4LoIUXDEmHCr+ +49DiQyIO21SNMHA+dVxvGRDDjLI9Uc+Scb64QOodoX75HLRZG++24mtdAoGBAP2/ +gCjga2p8Jx9UnhIcrEIIGANyxEQeBdhF56Nt9CJy/Iwi3a6qQ/GkbeoDm5FhXnXo +xcBaHyv2lwi4uO/hONY8eRnYxAWMwAKMZe6VnU1hWI2Ytkh+OcMPMh7NIGQf6X1o +JZrBtnTms060TuuDjLeIlaubDR/xDrMWTMKjKbsVAoGAVLuYAZ8J6xpIGlRhbGlA +6OrMxJCHcgpahvsWKc0BLXKmRBjHmTX7fslsSRihZWgKj1SZH7U2fpgpxV6cFxKJ +nPhUJEHhoKo+bjZ92tnANdqBq7iQjCsDJ8Bz52fuIlGD+1795+PsDA6bNKdkQkrV +zlNf80kuEqmFDFJ5+6EHx00CgYAf+jkpbZa71aeMgDpnZ+uhaqm0DYuEVhBAgBa/ +9sRUbw86jc5IC7cCRcmAOzIosQ+ZZls9cV4KSUohVD4iJMzn2rkcM8AIPwOXjp/t +4DbxoHnrZjpaimW3Gjwju5AAbjEbl7tddFoNA2HHYlurvGlIW9MYzDJsOxGyKfZE +dRF2PQKBgQDUKNHgDYEjLJ99S5Fm5zN/64bKzzDtktGdqOxik5pBKcs/BvOdLM0i +eCjGz/3qrEoenFIBwF/IRz3ug90Zr8bWOu6DudReflAKI/N13dZ2gOTAfaX4ljJF +w0ohSi6xs+mu1GmtipGtNxHi/J3na2BeSnSRFSUg6Zd+oh8BZQKmNg== +-----END RSA PRIVATE KEY----- diff --git a/t/cert/mtls_cert_gen/.gitignore b/t/cert/mtls_cert_gen/.gitignore new file mode 100644 index 0000000000..f375caaefe --- /dev/null +++ b/t/cert/mtls_cert_gen/.gitignore @@ -0,0 +1,4 @@ +*.pem +*.csr +cfssl +cfssljson diff --git a/t/cert/mtls_cert_gen/generate.sh b/t/cert/mtls_cert_gen/generate.sh new file mode 100755 index 0000000000..46625fdd07 --- /dev/null +++ b/t/cert/mtls_cert_gen/generate.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +rm *.pem *.csr cfssl cfssljson + +wget -O cfssl https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_linux_amd64 +wget -O cfssljson https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssljson_1.6.1_linux_amd64 +chmod +x cfssl cfssljson + +./cfssl gencert -initca -config profile.json mtls_ca.json | ./cfssljson -bare mtls_ca + +./cfssl gencert -ca mtls_ca.pem -ca-key mtls_ca-key.pem -config profile.json -profile=client mtls_client.json | ./cfssljson -bare mtls_client +./cfssl gencert -ca mtls_ca.pem -ca-key mtls_ca-key.pem -config profile.json -profile=server mtls_server.json | ./cfssljson -bare mtls_server + +openssl x509 -in mtls_ca.pem -text > ../mtls_ca.crt +mv mtls_ca-key.pem ../mtls_ca.key + +openssl x509 -in mtls_client.pem -text > ../mtls_client.crt +mv mtls_client-key.pem ../mtls_client.key + +openssl x509 -in mtls_server.pem -text > ../mtls_server.crt +mv mtls_server-key.pem ../mtls_server.key + +rm *.pem *.csr cfssl cfssljson diff --git a/t/cert/mtls_cert_gen/mtls_ca.json b/t/cert/mtls_cert_gen/mtls_ca.json new file mode 100644 index 0000000000..0a4a7ab139 --- /dev/null +++ b/t/cert/mtls_cert_gen/mtls_ca.json @@ -0,0 +1,18 @@ +{ + "CA": { + "expiry": "175200h", + "pathlen": 0 + }, + "CN": "OpenResty Testing Root CA", + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "US", + "O": "OpenResty", + "ST": "California" + } + ] +} diff --git a/t/cert/mtls_cert_gen/mtls_client.json b/t/cert/mtls_cert_gen/mtls_client.json new file mode 100644 index 0000000000..4d59f47a5b --- /dev/null +++ b/t/cert/mtls_cert_gen/mtls_client.json @@ -0,0 +1,18 @@ +{ + "CN": "foo@example.com", + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "US", + "O": "OpenResty", + "ST": "California" + } + ], + "hosts": [ + "foo@example.com", + "bar@example.com" + ] +} diff --git a/t/cert/mtls_cert_gen/mtls_server.json b/t/cert/mtls_cert_gen/mtls_server.json new file mode 100644 index 0000000000..655af54ef8 --- /dev/null +++ b/t/cert/mtls_cert_gen/mtls_server.json @@ -0,0 +1,17 @@ +{ + "CN": "example.com", + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "US", + "O": "OpenResty", + "ST": "California" + } + ], + "hosts": [ + "example.com" + ] +} diff --git a/t/cert/mtls_cert_gen/profile.json b/t/cert/mtls_cert_gen/profile.json new file mode 100644 index 0000000000..d3b6ab16cb --- /dev/null +++ b/t/cert/mtls_cert_gen/profile.json @@ -0,0 +1,27 @@ +{ + "signing": { + "default": { + "expiry": "175200h" + }, + "profiles": { + "server": { + "usages": [ + "signing", + "digital signing", + "key encipherment", + "server auth" + ], + "expiry": "175199h" + }, + "client": { + "usages": [ + "signing", + "digital signature", + "key encipherment", + "client auth" + ], + "expiry": "175199h" + } + } + } +} diff --git a/t/cert/mtls_client.crt b/t/cert/mtls_client.crt new file mode 100644 index 0000000000..dd0efdf7f8 --- /dev/null +++ b/t/cert/mtls_client.crt @@ -0,0 +1,87 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 19:0a:a3:a8:9c:d4:0f:dc:c6:fa:23:7b:f8:fc:bd:f4:73:4e:7e:b1 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA + Validity + Not Before: Mar 13 15:49:00 2022 GMT + Not After : Mar 8 14:49:00 2042 GMT + Subject: C = US, ST = California, O = OpenResty, CN = foo@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:be:5b:09:4c:94:71:d3:82:54:4a:42:6a:76:aa: + 34:5d:28:d9:45:e6:44:9a:74:9f:a6:e6:78:49:9e: + c6:20:75:32:5f:92:3b:ec:6e:4b:7b:b0:75:1c:75: + 09:00:05:77:d6:59:ca:55:5b:13:b6:76:3a:c6:18: + dc:37:6a:20:93:e6:26:56:5d:0b:96:8c:01:f2:96: + 38:08:08:36:a2:64:12:21:a0:8d:48:cd:9a:26:78: + 92:29:b6:63:eb:14:d9:b6:e5:87:f7:d5:55:a4:cc: + 53:1c:a3:7c:b8:bd:ad:7c:a4:d4:86:1f:a7:1c:43: + c5:1a:b5:f1:03:bd:fe:19:98:1d:b7:13:2b:93:a2: + 2a:0e:21:7e:42:a9:bb:28:69:49:59:e7:89:0e:7d: + 5a:ce:fb:d4:0c:20:6a:e1:db:b2:6a:e5:a7:55:e0: + d0:58:4a:e2:08:78:82:b9:06:0c:65:f9:24:06:e6: + 8a:13:b2:9a:ef:1b:4a:b2:3a:b4:98:7f:dd:3c:0e: + 85:0b:a6:c6:47:2f:63:c2:73:52:41:db:7c:06:c3: + 2a:b5:2d:d1:e1:30:d5:c4:79:c9:b9:35:68:46:ad: + c4:45:57:ea:11:88:27:37:ed:ac:49:2d:c4:d6:c6: + a6:74:8d:d3:bc:e0:d9:69:25:0c:0c:b0:e3:b7:cb: + 8d:99 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Key Identifier: + 22:70:5E:30:8C:4D:66:39:E7:60:C9:29:A2:ED:95:32:34:63:5C:C0 + X509v3 Authority Key Identifier: + keyid:F0:D7:4B:14:73:E1:67:00:6B:54:B4:19:20:76:12:9F:9D:8E:C8:09 + + X509v3 Subject Alternative Name: + email:foo@example.com, email:bar@example.com + Signature Algorithm: sha256WithRSAEncryption + 96:e7:2a:fc:2a:56:16:80:e2:d3:79:0c:46:db:c3:88:ab:d3: + ef:39:66:4b:a9:ab:6c:0e:30:08:07:7c:fc:03:6c:f7:dd:fb: + 3e:a8:c8:68:28:ab:4e:73:97:80:27:5d:c5:9d:52:00:aa:08: + 25:c8:f9:dc:df:64:73:a4:58:5b:bd:5f:1a:53:a4:33:a3:b1: + 45:38:2d:be:d7:f3:a4:c4:f4:7a:07:71:44:f1:a2:65:02:e4: + 71:84:01:b5:83:4b:de:83:b5:ad:ac:b9:3c:17:42:0c:9a:7d: + eb:7f:ab:26:dd:9b:3a:fd:95:37:55:cc:01:c3:3f:20:df:e5: + ed:49:51:7a:42:ea:f3:8a:3f:da:6e:c1:1a:11:b9:45:4d:6e: + c9:21:f4:e3:4f:31:72:5b:bb:01:92:b6:7f:f1:8a:9e:6c:d0: + 7f:96:d7:eb:29:09:53:38:26:41:00:f2:33:04:77:bd:a9:ee: + 60:9e:06:b7:7d:26:ae:1c:4f:56:bd:a5:b6:50:40:be:be:84: + 2a:54:21:59:47:7d:a5:1e:63:6d:28:36:4d:a6:e4:62:69:9b: + 9b:fa:2b:48:e8:64:d7:14:f4:62:a2:26:17:a5:05:58:4a:38: + d2:44:e7:33:90:b9:c1:8c:85:02:99:b8:03:1a:03:d2:cf:ac: + a5:6b:44:98 +-----BEGIN CERTIFICATE----- +MIID3DCCAsSgAwIBAgIUGQqjqJzUD9zG+iN7+Py99HNOfrEwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAoT +CU9wZW5SZXN0eTEiMCAGA1UEAxMZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe +Fw0yMjAzMTMxNTQ5MDBaFw00MjAzMDgxNDQ5MDBaMFAxCzAJBgNVBAYTAlVTMRMw +EQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQKEwlPcGVuUmVzdHkxGDAWBgNVBAMM +D2Zvb0BleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AL5bCUyUcdOCVEpCanaqNF0o2UXmRJp0n6bmeEmexiB1Ml+SO+xuS3uwdRx1CQAF +d9ZZylVbE7Z2OsYY3DdqIJPmJlZdC5aMAfKWOAgINqJkEiGgjUjNmiZ4kim2Y+sU +2bblh/fVVaTMUxyjfLi9rXyk1IYfpxxDxRq18QO9/hmYHbcTK5OiKg4hfkKpuyhp +SVnniQ59Ws771AwgauHbsmrlp1Xg0FhK4gh4grkGDGX5JAbmihOymu8bSrI6tJh/ +3TwOhQumxkcvY8JzUkHbfAbDKrUt0eEw1cR5ybk1aEatxEVX6hGIJzftrEktxNbG +pnSN07zg2WklDAyw47fLjZkCAwEAAaOBozCBoDAOBgNVHQ8BAf8EBAMCBaAwEwYD +VR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUInBeMIxN +ZjnnYMkpou2VMjRjXMAwHwYDVR0jBBgwFoAU8NdLFHPhZwBrVLQZIHYSn52OyAkw +KwYDVR0RBCQwIoEPZm9vQGV4YW1wbGUuY29tgQ9iYXJAZXhhbXBsZS5jb20wDQYJ +KoZIhvcNAQELBQADggEBAJbnKvwqVhaA4tN5DEbbw4ir0+85Zkupq2wOMAgHfPwD +bPfd+z6oyGgoq05zl4AnXcWdUgCqCCXI+dzfZHOkWFu9XxpTpDOjsUU4Lb7X86TE +9HoHcUTxomUC5HGEAbWDS96Dta2suTwXQgyafet/qybdmzr9lTdVzAHDPyDf5e1J +UXpC6vOKP9puwRoRuUVNbskh9ONPMXJbuwGStn/xip5s0H+W1+spCVM4JkEA8jME +d72p7mCeBrd9Jq4cT1a9pbZQQL6+hCpUIVlHfaUeY20oNk2m5GJpm5v6K0joZNcU +9GKiJhelBVhKONJE5zOQucGMhQKZuAMaA9LPrKVrRJg= +-----END CERTIFICATE----- diff --git a/t/cert/mtls_client.key b/t/cert/mtls_client.key new file mode 100644 index 0000000000..7d77e5506d --- /dev/null +++ b/t/cert/mtls_client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAvlsJTJRx04JUSkJqdqo0XSjZReZEmnSfpuZ4SZ7GIHUyX5I7 +7G5Le7B1HHUJAAV31lnKVVsTtnY6xhjcN2ogk+YmVl0LlowB8pY4CAg2omQSIaCN +SM2aJniSKbZj6xTZtuWH99VVpMxTHKN8uL2tfKTUhh+nHEPFGrXxA73+GZgdtxMr +k6IqDiF+Qqm7KGlJWeeJDn1azvvUDCBq4duyauWnVeDQWEriCHiCuQYMZfkkBuaK +E7Ka7xtKsjq0mH/dPA6FC6bGRy9jwnNSQdt8BsMqtS3R4TDVxHnJuTVoRq3ERVfq +EYgnN+2sSS3E1samdI3TvODZaSUMDLDjt8uNmQIDAQABAoIBACqRsUKu78WdH7x7 +ndNrvMoYmH5JQI5KBmoMoFnWZ/haPSmiSkRVZgwDKi1y/tBCaMpGyjjMZVwolHw4 +kwbRdPeeQHSP2keQh974OQ+SxqUKPAPJI89kK1TvIcCySSYJQ6bjLcT+sGhqSSve +Y8XspR96vQxBh92KSknu5jcwBeMy/eG0mmszzP3y2R0BPztuZdE6dq/KxWQ/R4/P +JG9V1rNkIY+1JZvIICIH1Ehn4UKjiE+FJmyDbDlPKEi7W4CpRnShMLOF4cCFnQLW +RQds3Dj9GcVY+8Q/GLZF0ATjekIyEsKZEgrMAUF5ZSGRpjJQEHX7oseAiQGQxtHT +nj5b1AECgYEAwewXbbd1MqRQ6ohfsQ8j5HSMY6ahvUzs1dZUckr2jw8B98tfi/uj +a6Jq1KZe12+4dfwruRSaYdTsSVuvNiSJOxElY0C1p+lXdprFf7XfoQ6UNtg22jcH +9f8cftnlJoV5whh3YKjqnnnAWUQZ61FTNJ258/t+x0ZgpBJvqBoHwDUCgYEA+0qp +FZ5xS4FLJMc+Xf/hUeXo+04e4OD/se3atYqyuh1ghmQZfRRPOC110HG99H+rzq/x +xPMvRFahkAMyi+/3oIcBEuXvoQyqscIsAhkWD/e9t3Qc9OsWe1hlAgWKZxr6oR2U +KKR1FD7UVecOH+FKCKaL5UpEt4yEigc1NtSlTFUCgYBnV5agrIyzQSex5J0CMWxS +Od362PkGdXEc/8we4F4GnNvSnrm7Uo2jNXmy+zo9mtb1YT43sogXLK4C5e44bz4G +kTuYagqkgdBPb2lihpy3KprHo2+P2JXQfXRFEX9xiN37Fqi/hSUK8R0VNRqO8dbi +ik9nexXzwkiMBxsjvUN2JQKBgFy62FpZ9YTfWVNhEuqtGgCWzrqtwUdKwBBwrVyA +qiNz48Kz/ZPigrlATVF2J5qp4kSLOLRs6OxW65exFl39V2utZgALSbosanDeLk83 +4qRRz3h7KJRYjBtIKz3rvX7+va3mtF2rEmk+Jizs7pFlGWTH0Kf0GBeDiwVEU6bA +IZ9hAoGAQTjnRGMjvyhq0aPYP+mRFiMKSkcL1nyXizYInfAnbfbL/uEODH7D+iMf +kak+UgmeD9ce5d/APmZp3/FzYH/M8ivBgG+MnaI+MLVMhmQdLZyMtbSKKaDpiim7 +DdN1wCXYbur0HlO2t+wemMZPpQu7wybgEOLlIG7Yj/0OWDcal1c= +-----END RSA PRIVATE KEY----- diff --git a/t/cert/mtls_server.crt b/t/cert/mtls_server.crt new file mode 100644 index 0000000000..7cbc2a804b --- /dev/null +++ b/t/cert/mtls_server.crt @@ -0,0 +1,87 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 2f:d5:41:13:5a:ff:c7:1c:5b:ce:28:cd:a6:f7:a5:5a:0d:c0:e2:d2 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA + Validity + Not Before: Mar 13 15:49:00 2022 GMT + Not After : Mar 8 14:49:00 2042 GMT + Subject: C = US, ST = California, O = OpenResty, CN = example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:d7:03:80:a7:42:7d:06:5a:7b:70:d8:11:96:dd: + 63:35:53:07:28:71:52:05:40:55:83:61:a7:14:ac: + cf:4b:9b:ab:b7:4e:9d:79:e9:13:3d:bc:c3:67:8f: + dd:88:d9:8b:c2:31:aa:b8:28:9e:13:70:db:76:b0: + 12:1c:f8:35:c6:2e:33:9c:b9:04:e3:47:e0:f9:e4: + 7f:a5:55:03:0c:2d:b2:54:17:29:12:dd:61:6e:5c: + 33:9f:e5:8f:8a:2b:41:53:dc:e1:98:49:63:df:e3: + 00:30:2d:1b:bb:f0:8f:cb:04:ec:c9:98:c4:09:5b: + b4:ba:a9:a0:0a:77:d2:42:76:7c:ac:64:c3:97:85: + 50:5d:7d:02:61:2a:00:93:d0:69:5e:87:22:f0:c1: + 1e:53:46:02:40:37:c9:55:77:99:7d:9d:3d:35:14: + 74:84:e3:73:ca:e7:4a:ab:33:98:26:aa:41:4b:b5: + e6:63:7c:a4:1e:25:6a:88:f4:56:d9:2c:63:dd:89: + 19:fa:25:41:44:95:87:40:a7:9b:4e:3a:91:29:32: + 79:66:05:f4:2f:68:2c:06:53:df:4d:60:be:ac:09: + 20:61:9c:6f:1a:a6:07:5a:e7:41:91:9d:36:77:38: + 18:3a:69:7b:67:29:9f:1d:e0:c2:d2:8f:16:5b:14: + e8:e1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Key Identifier: + 16:07:B5:C2:4C:B5:2D:4F:B8:E9:D6:FA:2F:3F:C0:1B:B6:4F:20:E6 + X509v3 Authority Key Identifier: + keyid:F0:D7:4B:14:73:E1:67:00:6B:54:B4:19:20:76:12:9F:9D:8E:C8:09 + + X509v3 Subject Alternative Name: + DNS:example.com + Signature Algorithm: sha256WithRSAEncryption + d9:c0:c0:d6:8b:44:04:26:b3:98:24:2c:12:82:6d:15:79:92: + 76:c9:77:94:c1:be:8f:8a:18:78:96:04:68:c9:0a:d1:84:c5: + de:cd:ba:b5:a2:3b:d4:0a:70:be:00:49:19:c0:6e:ca:e9:e5: + 8b:b6:e3:a2:39:0d:d8:ee:55:1a:08:73:39:19:d3:07:07:33: + 8c:d8:1b:0f:1b:73:0e:84:72:cf:e6:c1:a1:da:39:aa:c0:2e: + 3d:b9:a6:8f:ec:98:3a:07:58:34:c2:5e:4c:1a:6b:db:ce:51: + 92:25:1d:ba:78:4b:11:b6:f1:69:02:cb:ac:32:bb:80:f9:15: + 91:bf:4e:6a:ab:51:51:7c:7b:1a:72:80:96:eb:0c:fa:56:0e: + f2:87:3c:16:8a:04:aa:8a:9d:0c:d9:e0:c4:2a:20:42:5a:12: + 41:52:30:50:3d:85:f8:07:31:6b:af:a4:d2:44:38:69:ab:88: + 05:d4:5b:68:34:02:dc:99:5a:6c:b7:ea:fc:79:76:fe:68:29: + df:94:22:58:46:f2:40:cb:e1:92:17:d8:1e:3d:fa:a2:56:4f: + ac:3c:3d:ae:f7:90:12:ac:3b:6c:1e:1f:26:48:08:87:9a:0e: + 8d:9d:75:ef:86:1e:63:ac:e9:14:47:ad:3f:4f:10:57:2a:d1: + 95:ec:6f:24 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUL9VBE1r/xxxbzijNpvelWg3A4tIwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAoT +CU9wZW5SZXN0eTEiMCAGA1UEAxMZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe +Fw0yMjAzMTMxNTQ5MDBaFw00MjAzMDgxNDQ5MDBaMEwxCzAJBgNVBAYTAlVTMRMw +EQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQKEwlPcGVuUmVzdHkxFDASBgNVBAMT +C2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wOA +p0J9Blp7cNgRlt1jNVMHKHFSBUBVg2GnFKzPS5urt06deekTPbzDZ4/diNmLwjGq +uCieE3DbdrASHPg1xi4znLkE40fg+eR/pVUDDC2yVBcpEt1hblwzn+WPiitBU9zh +mElj3+MAMC0bu/CPywTsyZjECVu0uqmgCnfSQnZ8rGTDl4VQXX0CYSoAk9BpXoci +8MEeU0YCQDfJVXeZfZ09NRR0hONzyudKqzOYJqpBS7XmY3ykHiVqiPRW2Sxj3YkZ ++iVBRJWHQKebTjqRKTJ5ZgX0L2gsBlPfTWC+rAkgYZxvGqYHWudBkZ02dzgYOml7 +ZymfHeDC0o8WWxTo4QIDAQABo4GOMIGLMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE +DDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQWB7XCTLUtT7jp +1vovP8Abtk8g5jAfBgNVHSMEGDAWgBTw10sUc+FnAGtUtBkgdhKfnY7ICTAWBgNV +HREEDzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEA2cDA1otEBCaz +mCQsEoJtFXmSdsl3lMG+j4oYeJYEaMkK0YTF3s26taI71ApwvgBJGcBuyunli7bj +ojkN2O5VGghzORnTBwczjNgbDxtzDoRyz+bBodo5qsAuPbmmj+yYOgdYNMJeTBpr +285RkiUdunhLEbbxaQLLrDK7gPkVkb9OaqtRUXx7GnKAlusM+lYO8oc8FooEqoqd +DNngxCogQloSQVIwUD2F+Acxa6+k0kQ4aauIBdRbaDQC3JlabLfq/Hl2/mgp35Qi +WEbyQMvhkhfYHj36olZPrDw9rveQEqw7bB4fJkgIh5oOjZ1174YeY6zpFEetP08Q +VyrRlexvJA== +-----END CERTIFICATE----- diff --git a/t/cert/mtls_server.key b/t/cert/mtls_server.key new file mode 100644 index 0000000000..f5b85d18d8 --- /dev/null +++ b/t/cert/mtls_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA1wOAp0J9Blp7cNgRlt1jNVMHKHFSBUBVg2GnFKzPS5urt06d +eekTPbzDZ4/diNmLwjGquCieE3DbdrASHPg1xi4znLkE40fg+eR/pVUDDC2yVBcp +Et1hblwzn+WPiitBU9zhmElj3+MAMC0bu/CPywTsyZjECVu0uqmgCnfSQnZ8rGTD +l4VQXX0CYSoAk9BpXoci8MEeU0YCQDfJVXeZfZ09NRR0hONzyudKqzOYJqpBS7Xm +Y3ykHiVqiPRW2Sxj3YkZ+iVBRJWHQKebTjqRKTJ5ZgX0L2gsBlPfTWC+rAkgYZxv +GqYHWudBkZ02dzgYOml7ZymfHeDC0o8WWxTo4QIDAQABAoIBAEnmZUiXnJsbbEPr +r5f3vYptYA9xa2xsoTeHz8JWZuUouwtE1PE6v6c/grXMh6rqgpObOH8VTseFyZhw +ibk1Ql48MPcTzG9FnDinZYvwvRxpdFpcn3xhZIRm4kN5xi0KEuj9CPireM1RmxXz +2w1scC+qIKxlejNxNpvVgzE136mBqEFKJzecP+yZuH/A86MQCgwqqa3jSz5ApNg+ +1aJE34cGFieDbAN+9sdqWA3OkRrHoy8EakUf4JEvwX1AwUN832mj+N/LfmcCGMeD +YhzybzlPBV2q2T1+pHIdNT99JVNPkgdTe1903EjnG5oSDGHt2i9MdnNkMsffDWNt +pJiqSHECgYEA2hL6l8Py4oa5AJ2WXriuHRJykAs90K0akftQt4i4lWCbeRhaGh7h +kPgpDS33RkE4SymVVr0c05abMCKabQBwbu4PNCqetCFtfmIQdQCTUbLbXjL8UuD2 +QnF7nbHiwyGBKRMU/F74oX3z7lXLgRtIiyyo5yYgIAQqpz3oJAaXNTUCgYEA/GhE +Ziez8FXVAg3XwwrE3SexRFKv1JqipYE4mr+ouzfpn9yn8mttxbOORiAAEBl3ZPhd +ZUBzLy19fdFZ8RJ0zPsqoZxsd09/XetaBU56C/g9u0fycj1L2elh9rQAlOW0Grus +l8jBh01TGtlg0xobK0zjwdGPcbYkp1IzIqyD9n0CgYEAicBvVyrJ5FnhxwfEkrTq +FycuAtt3Arg2DnzH8geFQaayzv2Y/OMA7Yg0tkSQ7GoKW0A7O31eFjIOeYuCLNSY +MRpjtDov4e0zsx/S8XWZmYP3mjtutBOyuyngQi655TTm18FcAkcjmy9qxOShFj7b +xj5BuzGUHWVEZDxwxUD8hvkCgYBnrcyqyZQ4HImqllUSYNIMpclC71QaWIqGwVWm ++yMsBAOLDvBNu6MTmnXOiEZ+VnecmgiDFr45ms35aI0xYQtpR6JzT/Wd7KG8ynfn +xhyL3iQ9UYhdNKB7mkoLNFUo1FHuyThUALq+AR0p4jDLheWzG5pSeuoZI2Ba+oDW +tVZfYQKBgC5phtERR5LKU5Wkzm+uY2j+Nzh4kuKkdLosB9pUW8VnrwFDLZ+r1CxG +L6CxOZ0AylCMIlrFeUXMa91kLDJYch0NUPHuGBkdIBDXi2kqN7GflTdV3Z8uev20 +uMjErA93yVOWHTR3Wo8WIHy5mdsNRQgGAPw1RVW7rnYIyXJW/mTs +-----END RSA PRIVATE KEY----- diff --git a/t/cert/test.crl b/t/cert/test.crl index 098fd54bf9..0abfa77ac2 100644 --- a/t/cert/test.crl +++ b/t/cert/test.crl @@ -1,11 +1,14 @@ -----BEGIN X509 CRL----- -MIIBjzCB+QIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMxEzARBgNV -BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoM -CU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNv -bTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20XDTE0MDcyMTIxNDEy -MloXDTE0MDgyMDIxNDEyMlowHDAaAgkApQ5tVpK3luIXDTE0MDcyMTIxNDEwMlqg -DzANMAsGA1UdFAQEAgIQATANBgkqhkiG9w0BAQUFAAOBgQBDZ6UY0Qg7qDoLrXXl -gJElFilZ7LiKPqjE3+Rfx7XkgdbPxjGCr77TfMm+smdvawk7WHv1AOvRH7kGrgGT -kGJZwqJ4vKa/NpEWJIMAZ1Gq9BIH/Ig6ffmPk+S9ozcVHKJDW7x4nMuotyj1hILN -EePv78DZCYMZgf8WwMElNgz6Hw== +MIICGzCCAQMCAQEwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYD +VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQK +DAlPcGVuUmVzdHkxEjAQBgNVBAsMCU9wZW5SZXN0eTERMA8GA1UEAwwIdGVzdC5j +b20xIDAeBgkqhkiG9w0BCQEWEWFnZW50emhAZ21haWwuY29tFw0yMzEwMDYwNjM4 +MzlaFw0zMTEyMjMwNjM4MzlaMCcwJQIUImcuJ8MJpeNhvYBEoEGGz5oCBg4XDTIz +MTAwNjA2MjkyNVqgDjAMMAoGA1UdFAQDAgEEMA0GCSqGSIb3DQEBCwUAA4IBAQBj +FciorrAuXxn1ULW0XJ7PElwTxZtBrb838EHYvkZ5OdT5tZcucYR6XTZpfT1Up/Px +rC9EZ1/aZ0wSQfYEQuctafyVCJoPN71IV9IWpPTWm8JyEvnE1W3SgHJujItanyZ3 +aMDihljxV9eKyEQnZPQZkaOjAKhj8d2/XZLQ3uIrjWy+/OxcaBQK4a8bDoSM3GZT +J6YCD2UBVYKSiROMZZAj3m1thLAGm1RM7A6vjEcH7rPyoxhok5SdxXH5ERY94/ro +McjNCv6zV8/Ue5/+ajz5pu/48T901mlIicHry8uImJvlnqAXlH8ReJ+hiSfGhbZ5 +WYNf0wN81NXwTxPIb+v2 -----END X509 CRL----- diff --git a/t/cert/test.crt b/t/cert/test.crt index a69a01141a..9fed1bc3b4 100644 --- a/t/cert/test.crt +++ b/t/cert/test.crt @@ -1,17 +1,22 @@ -----BEGIN CERTIFICATE----- -MIICqTCCAhICCQClDm1WkreW4jANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC -VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x -EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQD -DAh0ZXN0LmNvbTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20wIBcN -MTQwNzIxMDMyMzQ3WhgPMjE1MTA2MTMwMzIzNDdaMIGXMQswCQYDVQQGEwJVUzET -MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG -A1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRl -c3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNvbTCBnzANBgkq -hkiG9w0BAQEFAAOBjQAwgYkCgYEA6P18zUvtmaKQK2xePy8ZbFwSyTLw+jW6t9eZ -aiTec8X3ibN9WemrxHzkTRikxP3cAQoITRuZiQvF4Q7DO6wMkz/b0zwfgX5uedGq -047AJP6n/mwlDOjGSNomBLoXQzo7tVe60ikEm3ZyDUqnJPJMt3hImO5XSop4MPMu -Za9WhFcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQA4OBb9bOyWB1//93nSXX1mdENZ -IQeyTK0Dd6My76lnZxnZ4hTWrvvd0b17KLDU6JnS2N5ee3ATVkojPidRLWLIhnh5 -0eXrcKalbO2Ce6nShoFvQCQKXN2Txmq2vO/Mud2bHAWwJALg+qi1Iih/gVYB9sct -FLg8zFOzRlYiU+6Mmw== +MIIDtzCCAp8CFCJnLifDCaXjYb2ARKBBhs+aAgYOMA0GCSqGSIb3DQEBCwUAMIGX +MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu +IEZyYW5jaXNjbzESMBAGA1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVz +dHkxETAPBgNVBAMMCHRlc3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdt +YWlsLmNvbTAeFw0yMzA5MDUwNDE5MjhaFw0zMzA5MDIwNDE5MjhaMIGXMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j +aXNjbzESMBAGA1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAP +BgNVBAMMCHRlc3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNv +bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJzRMFoLDuYOwJ8szrS4 +nOibtiimiXZJGx3I/RcFZxaH4nL/WcEb1fwftMQxx73IBJnqnDYkDOzUmzItPMn0 +t2WrNYesC5GqLNRm87m6PVt010tZvq/WxTn6+9qruiGm1PhFxzLQfrClpEeOshlG +UeoQjPOMrhCmofDM2NQo3D4wIQT0kCJxIPq6wCZt22/Yqz1EmR0UnF/R3ZtiB8O+ +SQGcsUKy4se3919xq+ZkzBdMxLneO5sofUiDC9MgRfiU960tbHPGX9I9P+kLK89S +yajPEYaRUkSBFjV5kdDK3+L6XckdMbY2pvwhAnVXSmd13Bf2V9XisUrX2Mr4YlnS +sy0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVPY/z6Mvjg5EGHzU8bXyuXqxrx8Q +GBwf3PY25aDF6ofRrTCzMdIhthv8eRtGwHinkpgaK34D7hI/dPB7aswQTzED5c+l +S2au5OzzCj454oXdhSRA5Rt0mu/+pxmQ+iNk+7XJxgTN0mk1dYQqodyZ+vC4NIYb +javMlU4zDm4JPtwDs0Mz/d7gf14MU60jppF2vl6AYFHKYBLMHBmqxjy6H9YHjRjQ +oe4TNpn0zxJAPu5LqMkfB2+eLOe6ced7DcLLbbeVJ4Xtqj6Y5KsAyVojWQxrk4vW +3WO/953pHofO5F2ricS/rsf+5ivTmfiP8mQYTtp7k3T11sIZ4DOmtNwO4A== -----END CERTIFICATE----- diff --git a/t/cert/test.key b/t/cert/test.key index 6c13527117..4c8c905b2f 100644 --- a/t/cert/test.key +++ b/t/cert/test.key @@ -1,15 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQDo/XzNS+2ZopArbF4/LxlsXBLJMvD6Nbq315lqJN5zxfeJs31Z -6avEfORNGKTE/dwBCghNG5mJC8XhDsM7rAyTP9vTPB+Bfm550arTjsAk/qf+bCUM -6MZI2iYEuhdDOju1V7rSKQSbdnINSqck8ky3eEiY7ldKingw8y5lr1aEVwIDAQAB -AoGBANgB66sKMga2SKN5nQdHS3LDCkevCutu1OWM5ZcbB4Kej5kC57xsf+tzPtab -emeIVGhCPOAALqB4YcT+QtMX967oM1MjcFbtH7si5oq6UYyp3i0G9Si6jIoVHz3+ -8yOUaqwKbK+bRX8VS0YsHZmBsPK5ryN50iUwsU08nemoA94BAkEA9GS9Q5OPeFkM -tFxsIQ1f2FSsZAuN/1cpZgJqY+YaAN7MSPGTWyfd7nWG/Zgk3GO9/2ihh4gww+7B -To09GkmW4QJBAPQOHC2V+t2TA98+6Lj6+TYwcGEkhOENfVpH25mQ+kXgF/1Bd6rA -nosT1bdAY+SnmWXbSw6Kv5C20Em+bEX8WjcCQCSRRjhsRdVODbaW9Z7kb2jhEoJN -sEt6cTlQNzcHYPCsZYisjM3g4zYg47fiIfHQAsfKkhDDcfh/KvFj9LaQOEECQQCH -eBWYEDpSJ7rsfqT7mQQgWj7nDThdG/nK1TxGP71McBmg0Gg2dfkLRhVJRQqt74Is -kc9V4Rp4n6F6baL4Lh19AkEA6pZZer0kg3Kv9hjhaITIKUYdfIp9vYnDRWbQlBmR -atV8V9u9q2ETZvqfHpN+9Lu6NYR4yXIEIRf1bnIZ/mr9eQ== ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCc0TBaCw7mDsCf +LM60uJzom7Yopol2SRsdyP0XBWcWh+Jy/1nBG9X8H7TEMce9yASZ6pw2JAzs1Jsy +LTzJ9LdlqzWHrAuRqizUZvO5uj1bdNdLWb6v1sU5+vvaq7ohptT4Rccy0H6wpaRH +jrIZRlHqEIzzjK4QpqHwzNjUKNw+MCEE9JAicSD6usAmbdtv2Ks9RJkdFJxf0d2b +YgfDvkkBnLFCsuLHt/dfcavmZMwXTMS53jubKH1IgwvTIEX4lPetLWxzxl/SPT/p +CyvPUsmozxGGkVJEgRY1eZHQyt/i+l3JHTG2Nqb8IQJ1V0pnddwX9lfV4rFK19jK ++GJZ0rMtAgMBAAECggEABjaOkcllis1o/yrVZMPPabLpAHV6tZ5MuKfNiUOMSPr+ +HfF1OFQL7MxCdfyFQ1prqOp/9nAut+puMgp99wAfDQ7qanNGq7vgQKkfPSD+dy4V +rUquELBJH6nh9SZqfpSqKaJgHlNe6vehHuRYikJRkrJwVzegGjuekm3B+y6Zl/gc +e0p5Ha3MTLTFjocwYzgTjJlxD40wlbjpuVnmzKjo8AKNv1F1azMaqBmt1VfPiDn0 +Xyq4SPEsWKnEAl2kZdaIBR6zIx7Z3zNUwkfb32QwNoSyo8wS7lCgf2GVS7r1Eul6 +iiCE/Gd7w10alW4Pu96shVqkvKn7ROF2nBP9xOSPwQKBgQDCuD6mlNpA07iOX364 +aAzIAYookceVA0I9L/fbOQW7RgpvYpM8lxr31TQ3fBDkXSgjzMMYjnk4kz+xN+BB +WFdjb4raUBtrvip8Q8QZ53DVQK/LodHh0XhipbOxZrDm+6o5nQD0fTqHCBIHSVFF +tXX2Y90t1cxWMMleRhfNEuzkQQKBgQDOK0rs7mf04Xhc4ZIRIxOtNFnthGp4Kqp7 +SD8VQpbPOLV8iqZEtXIy/hvoTpfQW30c1931KgDQ3Pv5MZYpI7PLqrqkj4tGCQ91 +DJ03GWkSXcMwlPmJRbvgWIeCLgShU5PLxmQu3mH2DP+uGFUBq5/6miDDVjF9z6vb +BwYlG66j7QKBgA0n/bOrowN2SqXz9c/n19U7pWYQU3fR/Iu9zfVV6Pk6RkI4WtJh +M0VDdn+5Njr3wFqK3zOtjKsx57/FkrVXjq/9PVh6yR+CfcRfn8RQSuNdt4L+r/ud +95BSuc1mrtUsc9for8PVIjs1ZGJxpbgcBphbLvqF04SPT0u7WKhWewMBAoGAcJO/ +RAUiitsbaExcADORKQDvIf0uThOuJ8dZevhzdQ/YOftTsy0JAMM05fMUfteWR8uw +DZE0BNjGVlo3TpuKL+o4JGele0azRAzxRAcCEt9UGBEg+U40utpclD8glB8ZEypv +xg/0mfCbJKtwr4rRvnuu7DsCp1pg0ybQui6VfDkCgYBXHwcrZwmv7kgr4pUG6oZj +fzjFenQFqibvb2h7QESyCW13O885GxU13DKv4zg1yi6EqPIopz16qCiUNCvWr5Us +6sI74wEVI3MzmzG0Htgl29q5yWpeY+7libC/fbZYG8GFgdINq58ko9be1u/8644S +t2hoKM9/vrVFh9p9qGzckg== +-----END PRIVATE KEY----- diff --git a/t/cert/test2.crt b/t/cert/test2.crt index edc3b0df3b..6ba864c3fe 100644 --- a/t/cert/test2.crt +++ b/t/cert/test2.crt @@ -1,16 +1,24 @@ -----BEGIN CERTIFICATE----- -MIIChzCCAfACCQDjCkJpJUtZmjANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMC -VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x -EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZI -hvcNAQkBFhNvcGVucmVzdHlAZ21haWwuY29tMCAXDTE0MDkxMzAwMTgxMFoYDzIx -MTQwODIwMDAxODEwWjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju -aWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU9wZW5SZXN0eTES -MBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZIhvcNAQkBFhNvcGVucmVzdHlAZ21h -aWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDy+OVI2u5NBOeB2Cyz -Gnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7f -JrKFziJxMy4g4Kdn9G659vE7CWu/UAVjRUtc+mTBAEfjdbumizmHLG7DmnNhGl3R -NGiVNLsUInSMGfUlJRzZJXhI4QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAEMmRvyN -N7uE24Tc6TR19JadNHK8g3YGktRoXWiqd/y0HY4NRPgvnK/nX7CY/wXa1j+uDO8K -e6/Ldm5RZrjtvfHJmTSAu8zkqTJz8bqRDH7kzL5Ni2Ky2x8r9dtB0ImpOiSlwvZN -snMvbrxEdwBiqlC9prV2f9aG+ACo1KnPL0j6 +MIID7zCCAtegAwIBAgIUS1f/CoGJaRaJvBgrXEXeTSm7kM4wDQYJKoZIhvcNAQEL +BQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAMMCXRl +c3QyLmNvbTEiMCAGCSqGSIb3DQEJARYTb3BlbnJlc3R5QGdtYWlsLmNvbTAeFw0y +NDAzMjQxMDI1NDhaFw0zNDAzMjIxMDI1NDhaMIGGMQswCQYDVQQGEwJVUzETMBEG +A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAGA1UE +CgwJT3BlblJlc3R5MRIwEAYDVQQDDAl0ZXN0Mi5jb20xIjAgBgkqhkiG9w0BCQEW +E29wZW5yZXN0eUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQChXBMRP5X9Y0l5NogyIZatou1YDT2BDE9tza6dYKUdraJlGo1T4DZf7LoD +mKFvIpVqLedppZ9z9SrlrsZ8Be6OUbw3kxSyUPDvpauI4XXBR2ZuAHIzTy/Uare1 +1iZSjbT0KATiWviMWCWWblyBU5QPMFJJOmcjRDAZocfIUs4CzglRaDorl5qUFJHr +mROaIsag/10nnClTwaGwj1pHmFitKRHTIrYICYd5wGgstte9mDFz15s9a3f/Dwhc +/7neEHYaryRVhR627aDGVHHpcMRWr+FC8QmZTTkiFmO80eeaiyGGTS0AEW51EPI9 +Nr142PEaQETpjQaQnfNhZGnwTZHTAgMBAAGjUzBRMB0GA1UdDgQWBBQb73HlJHF7 +B4a1PWRkb3aovk7EtDAfBgNVHSMEGDAWgBQb73HlJHF7B4a1PWRkb3aovk7EtDAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCQJcLh7+NzAukTG4hV +nVd572U4sCnlXwTN4lnRIkSGbQxOWwbuI7p05iAT9HJrFLm+rV0EOzxyl/fYkPgR +DKYfj7sGPp4Lo0NyQ3xhoVypqDDmv7Wm9CLy2mVTJ07khW5tV8GHg9mFwgb/XBTQ +c+RnNalIapqdlkQbxms5HLzKzTVRuDt+wvqcAsiKFwKDAL9DFrp36TP1t023w+vd +Vb6Ms+l+xFFcxMCxFEOkSPc7szwSce9Q+Hk2C6JdCU30gJPyEBUMl6vD+0l0IFG0 +ZEw1BhBp4d92xHiYgdnDEH1RdLPI0q1r7LJL+5ic+VFWycgbC0KRHCarduYgvMYy +GbRG -----END CERTIFICATE----- diff --git a/t/cert/test2.key b/t/cert/test2.key index 82ce6ce91d..3bd546da0e 100644 --- a/t/cert/test2.key +++ b/t/cert/test2.key @@ -1,15 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDy+OVI2u5NBOeB2CyzGnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4 -PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7fJrKFziJxMy4g4Kdn9G659vE7CWu/UAVj -RUtc+mTBAEfjdbumizmHLG7DmnNhGl3RNGiVNLsUInSMGfUlJRzZJXhI4QIDAQAB -AoGAEqBB83PVENJvbOTFiHVfUAjGtr3R/Wnwd4jOcjHHZB3fZ9sjVoxJntxfp3s1 -dwZir2rxlqVS6i3VAFiGiVTOGo2Vvzhw2J7f58twCECmnLb2f863AkGEYe4dAndD -GHGD0WI0CBMD1sT18YCj561o0Wol5deWH0gM9pr2N3HkeIECQQD6hUKFlFhrpaHP -WNJsl6BxgE6pB5kxLcMcpIQ7P+kHUvtyvCJl5QZJqPrpPGjRsAI5Ph92rpsp/zDp -/IZNWGVjAkEA+Ele31Rt+XbV32MrLKZgBDBk+Pzss5LTn9fZ5v1k/7hrMk2VVWvk -AD6n5QiGe/g59woANpPb1T9l956SBf0d6wJABTXOS17pc9uvANP1FGMW6CVl/Wf2 -DKrJ+weE5IKQwyE7r4gwIvRfbBrClSU3fNzvPueG2f4JphbzmnoxBNzIxwJAYivY -mGNwzHehXx99/byXMHDWK+EN0n8WsBgP75Z3rekEcbJdfpYXY8Via1vwmOnwOW65 -4NqbzHix37PSNw37GwJBALxaGNpREO2Tk+oWOvsD2QyviMVae3mXAJHc6nLVdKDM -q0YvDT6VdeNYYFTkAuzJacsVXOpn6AnUMFj0OBedMhc= ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQChXBMRP5X9Y0l5 +NogyIZatou1YDT2BDE9tza6dYKUdraJlGo1T4DZf7LoDmKFvIpVqLedppZ9z9Srl +rsZ8Be6OUbw3kxSyUPDvpauI4XXBR2ZuAHIzTy/Uare11iZSjbT0KATiWviMWCWW +blyBU5QPMFJJOmcjRDAZocfIUs4CzglRaDorl5qUFJHrmROaIsag/10nnClTwaGw +j1pHmFitKRHTIrYICYd5wGgstte9mDFz15s9a3f/Dwhc/7neEHYaryRVhR627aDG +VHHpcMRWr+FC8QmZTTkiFmO80eeaiyGGTS0AEW51EPI9Nr142PEaQETpjQaQnfNh +ZGnwTZHTAgMBAAECggEAAhemuvqx8A25YQQsgvM9jXXckbTmPxtQ7QcVmb6BlZ9v +834DJtB0VA0b97pNyZpPXrJGsG+F4E6QZlcRsCpcMWXs72Q4Xfw/tj6AjsSsWWZw +H8K2Dbqb+lYB7i6J463TvWDGzhqeOM+seRq9l1hlRfHQogN1spbf8CJ8Fo8VqHf3 +OyId5db0nRUFYV9ciPvJSnk8xi+0aJ2LWVAkCaXQkntbI3529x8KPQm9bGUjdMgo +A+HXAKZTZL7GtGZoSU4jjyymGLM4hPtzXBExbkX7s7HOP1AeWGom4reOC6XNScQq +ysfs0wVzJAVSl0vFbQfUT6GZ2ZsaTdkT+JgXrYwp5QKBgQDIzY1O/Hj2koVgfKmS +2+1JVehX8+yjgz3rSNVUauISLoMaYN4hipsKb7Vi6aDFl+Db296Qql7kIzzd/nFk +UwdBjShUi/xbewGcrYEXbdcXSIxt9ius76eJ9INfa4EmLrZoiX/3Am1u+CcdtcKm +jkqy1o9zoIPbD6lc242zGFK2nQKBgQDNtukEZop/PngCX0yDGWLxisrvFYSrHxnW +x9/Fw0B56qmyG2FpFg5WDlErenHXh+KSlQ3pV1sWK/+3pVzgGP2ZK+nv3ThEQJmg +IbI32dABmUG9+HckxgOuAzfqGW6Y4m5I8YpODjn26V6GeS+xQj0bolr4lZ00QSEG +R5BxxLLHLwKBgQDAzYzWwFgs+HaUNF5lokFt3syeVplqTsOPPHmI+q9iocJD+6qN +Lha7qJLTDFvQHYpL0AsdgFhoWOVCieK6X3ZiHHUS6O4/sBXWKEoBAvg5ZPFhS7KX +8+w768iQQBrbXJLMQOnbdDs7B44XWsQxRAK5QIawkPjJx3norO6bfck9iQKBgA05 +Ze9ffCXD6UkVwUBoQGEQsA0AkB/EBxA0lyEKdTmhKTmG4xMzVMaZcwRdgbX+SUVt +CZDnibZ6K50tpzPMx3iyRv6hdP2GPZn6sI9AlEuWA5DnyRj3FcN49344FlEDtV1B +EcgEyBskU2xHnBZEENOW80wpqgpy6WGS9ikqIOgHAoGAeAUetHWIdZZ+Qt6SS1s2 +R17YAiZe88QFAo3uK/dyApLgY7wvbM0n7Cor/3tbpzpzAujUb/qNqTdTXsZIeRfq +bI767g4/pNl7+jhDUKmDI001YdfHvcRSwjzyjshuSinJY9rydXLUIEWtkTG3f9v8 +d+yLXD2Dz9gF6l2Msj+C3To= +-----END PRIVATE KEY----- diff --git a/t/cert/test_der.crt b/t/cert/test_der.crt new file mode 100644 index 0000000000..273b98638f Binary files /dev/null and b/t/cert/test_der.crt differ diff --git a/t/cert/test_der.key b/t/cert/test_der.key new file mode 100644 index 0000000000..14e9fa5821 Binary files /dev/null and b/t/cert/test_der.key differ diff --git a/t/cert/test_passphrase.crt b/t/cert/test_passphrase.crt new file mode 100644 index 0000000000..78d57577b0 --- /dev/null +++ b/t/cert/test_passphrase.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDTCCAvWgAwIBAgIUZn0PL6eNqoKWughHd0SFO4aE/UswDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ2RvbmcxETAPBgNVBAcM +CFNoZW5aaGVuMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAsMCU9wZW5SZXN0 +eTERMA8GA1UEAwwIdGVzdC5jb20xIzAhBgkqhkiG9w0BCQEWFGd1YW5nbGlubHZA +Z21haWwuY29tMCAXDTI1MDMwNDAxNDkwNFoYDzIxNTkwNTAxMDE0OTA0WjCBlDEL +MAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzERMA8GA1UEBwwIU2hlblpo +ZW4xEjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYD +VQQDDAh0ZXN0LmNvbTEjMCEGCSqGSIb3DQEJARYUZ3VhbmdsaW5sdkBnbWFpbC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmsvo9t9/lhfavK6S8 +7lqP8lfpV1gZE4ahyRCQI0vgQitlhGjEzvBc7qTtkunQpSWkuHSczOR1WuKzO+PG +ubH0gjeyvEE2X/cipQ1kjiO9Mv8w2qOm9BkC9Wrob01X4mOhGZUgthfwRs0QFSUh +tzvjg+aApWyIlpmgE/T8bnCwtPS7BuzwEpTt5a51q8JKOr4AMTy64mrMFQRqtcIk +GYL9CnoxAjYT6NAcEqoapSLDm1xJ7w2RDL1mqkAdXYUQeNPwMivrtb+rCDD4p99A +TZ8rOuKQzDSxZ3BdCmR5WUXp9z+NeQE5SBu+vkOYMTvEN+e9ApRRnZTO8UHeRB0+ +6vJZAgMBAAGjUzBRMB0GA1UdDgQWBBSmnXGC+KOqNzyGXoujr3TMe3pepDAfBgNV +HSMEGDAWgBSmnXGC+KOqNzyGXoujr3TMe3pepDAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQAokEqnbJNDUICvbsRQBFrDBZXjZaEPu69GrTg9OceB +N9Qu4CKRTL4WsV6uSbdj+Ox/r21UtCI1590oBpC3RiJ+WnGmpdUGVytGIODt/vJ7 +/R03EBy0o9e7XdlR4GfKMC7FVBsP/kkeacR2fswHYXtQrNlyuLhZW04HTDdYNNqb +LNCnsmwQ4WR4ek9PdcpeFfMY1Up6hTYZ441K8b6xSzMV0/LjzfmHXWoT3U1rQrfM +esm9t/R3sOvB/wBWQaRe0BbLYpkS7pcq8NQsWvSUFNl5TzrGtUf8KTyvEEdcUcZf +im48Ea7WYpE3RYs0laLBk/D3sz7vsK5wfiD0R/ASLE+s +-----END CERTIFICATE----- diff --git a/t/cert/test_passphrase.key b/t/cert/test_passphrase.key new file mode 100644 index 0000000000..2cabedfb57 --- /dev/null +++ b/t/cert/test_passphrase.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQ2v8WxwirSc2M7jaZ +mrWsPgICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEAlDrk7LLHI/jJqN +QJ6z8MYEggTQtV86xHehGFn/Qkn5qSZicJ/sDLXQ+m4dFXgw9KMnm0zLG8kzEmb7 +sobUJ4CnwmpBv/GmpPxgy5v1fuvc8HXRJWr7xwv1FoD4lwVOWVFv/qTaII4TfcC6 +3vKqO7MbRaksyI/NmmepGKTGhbzW6XlEdE1TCG7tO02I3puSN0MbJACTpop53TG9 +g+b8HXegPXkLJ2p+ECjUqPwA+h6jZi2pIAztMHQpIizokomrLJ4+2ol3czJpA5nn +n8JbuYt1XWekq+PBi4l0+lAbk/RiLLK1PCN/Njp7iBBAtNtz9+yJd2Ev7wNDDxKk +dW73s8uTGde/n6hR4sl/akK8HLaw9tDcWsaHbIMl8zn3WZXksVUfb6CZVOU+BjBa +yZu3CnP1JTda16HgdZ5dGSNPNbrUeLLIRzM/OxK0yTtmkexOVIgSW0hGE1BXxFXk +v+mnzrjFrHNRQFhoCFciPqv+WfXsecSASeN0aLWP/RP6CLz6QXWQaK68Dn92v/WP +xc1NKk8l54wNCLqGaSXadUoTqEdHQH3GV5EkXP7MTb+fYi9/Y/S8wcXJtjUi3ABe +W+XCOh4zuuQPG7C3i6l/jLIL1ogQNYldad7MoM8eBU8u+TF+NqiJjTLCB1Quycqn +sN5oVS1VZfsx8XCFsvUAZgxzhjeXViBfwxKSKyT0b1drQZaY58LnyzjmzGkOpRfO +uzGzWPvHRxkGb9071XaRxD8YDLIngHCw3F2USto88efzmvBfB8ZIxk90S9TZZDeJ +rqIKpNXsfM4nCR07DW48n3IoiHLCtRFinfpboS5/47ZN4XBE7Q/QNJrWa2P07aWR +HdOBdD/N3DnM5bbSz7m+5TA+xC4/hEWLXt7Au+vnnhVyDdU80a3+3JY7y4vScHLS +Fn2grTk5F9pqieNhwawpXUCYNGFtJOY8LOQpUZdMpy6n6N5qzSjZ+vgE1Nwr0G/S ++n8ORUcJzF3s8h+u+SN8OC9W6chnftSIWKiqGsU4oM0FFkr2nOrfNNKqAQo2OU3W ++5lJovyqAcxtj+4qQsBrYzH7vRzdkigXhb/R7tRDnBRWBpWlMmnUhkaWG8EI0JLs +gjKNOUFCT1HgTSfonRxScDE5IAg3JxieNz9ppDCphvc0ii3wwdS7uwstKGZCC+Wr +lQsiV0Wa4WlWVMfLCONI13+zeOli4BmwepgkjJc+YogvLodblcHN14kTTCDvhDMp +KZglwFT+3zkxH9ArnO7/a8WQCWZV6bJ29HFUfAioE8Sie04WD4zgYEfU02r9A+vq +wl/w5UK6eXOO6Hz2OrfYh2dZXiCOLp+DhftKq6MKIIAcqFuAzrPvzxd7Uup/fxyX +n2Ub4YKmUV89RzJ2ipZRjiBcQo+9h4O9dIh4+2I0m7NPALqirutCFBe2cHu/Fwho +SzacLI21OzWLKbyZ2D6ueH4NrhnOZ10bo/lKbc5d3JzuGv6KW1NmGo32qiKeKNzG +Q52+sm0/TLiLxy2RlqJ1p7R9NLLZTY8bZTD125n2RtNaJWT564vseY8SaiUW62r8 +ksU5aPINa7Qkf4xgsmkSJymFs/9AjXfSCS/aZ13PQMUuI5A4fY9IZjSoiUlSx1bX ++2B91azW4yfHcAJrluIjZ13LvBU08ooPKPm2TazDi89/8GjBMpHUQN4= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/t/lib/CRC32.lua b/t/lib/CRC32.lua index fcf260cd4c..8ca0d5634e 100755 --- a/t/lib/CRC32.lua +++ b/t/lib/CRC32.lua @@ -156,7 +156,7 @@ end -- -- CRC32.lua -- --- A pure Lua implementation of a CRC32 hashing algorithm. Slower than using a C implemtation, +-- A pure Lua implementation of a CRC32 hashing algorithm. Slower than using a C implementation, -- but useful having no other dependencies. -- -- diff --git a/t/lib/Memcached.lua b/t/lib/Memcached.lua index e3288ac4fb..1f8c7f9578 100755 --- a/t/lib/Memcached.lua +++ b/t/lib/Memcached.lua @@ -541,7 +541,7 @@ end -- -- memcache:set_hash(hashfunc) -- Sets a custom hash function for key values. The default is a CRC32 hashing function. --- 'hashfunc' should be defined receiving a single string parameter and returing a single integer value. +-- 'hashfunc' should be defined receiving a single string parameter and returning a single integer value. -- -- memcache:set_encode(func) -- Sets a custom encode function for serialising table values. 'func' should be defined receiving a single diff --git a/util/build-with-dd.sh b/util/build-with-dd.sh new file mode 100755 index 0000000000..4f4e785f53 --- /dev/null +++ b/util/build-with-dd.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# this script is for developers only. +# dependent on the ngx-build script from the nginx-devel-utils repository: +# https://github.com/openresty/nginx-devel-utils/blob/master/ngx-build +# the resulting nginx is located at ./work/nginx/sbin/nginx + +root=`pwd` +version=${1:-1.4.1} +home=~ +force=$2 + +add_fake_shm_module="--add-module=$root/t/data/fake-shm-module" + +time ngx-build $force $version \ + --with-threads \ + --with-pcre-jit \ + --with-ipv6 \ + --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE2_INC -I$OPENSSL_INC -DDDEBUG=1" \ + --with-http_v2_module \ + --with-http_v3_module \ + --with-http_realip_module \ + --with-http_ssl_module \ + --add-module=$root/../ndk-nginx-module \ + --add-module=$root/../set-misc-nginx-module \ + --with-ld-opt="-L$PCRE2_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \ + --without-mail_pop3_module \ + --without-mail_imap_module \ + --with-http_image_filter_module \ + --without-mail_smtp_module \ + --with-stream \ + --with-stream_ssl_module \ + --without-http_upstream_ip_hash_module \ + --without-http_memcached_module \ + --without-http_auth_basic_module \ + --without-http_userid_module \ + --with-http_auth_request_module \ + --add-module=$root/../echo-nginx-module \ + --add-module=$root/../memc-nginx-module \ + --add-module=$root/../srcache-nginx-module \ + --add-module=$root \ + --add-module=$root/../lua-upstream-nginx-module \ + --add-module=$root/../headers-more-nginx-module \ + --add-module=$root/../drizzle-nginx-module \ + --add-module=$root/../rds-json-nginx-module \ + --add-module=$root/../coolkit-nginx-module \ + --add-module=$root/../redis2-nginx-module \ + --add-module=$root/../stream-lua-nginx-module \ + --add-module=$root/t/data/fake-module \ + $add_fake_shm_module \ + --add-module=$root/t/data/fake-delayed-load-module \ + --with-http_gunzip_module \ + --with-http_dav_module \ + --with-select_module \ + --with-poll_module \ + $opts \ + --with-debug + diff --git a/util/build-without-ssl.sh b/util/build-without-ssl.sh new file mode 100755 index 0000000000..022d9617e2 --- /dev/null +++ b/util/build-without-ssl.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# this script is for developers only. +# dependent on the ngx-build script from the nginx-devel-utils repository: +# https://github.com/openresty/nginx-devel-utils/blob/master/ngx-build +# the resulting nginx is located at ./work/nginx/sbin/nginx + +root=`pwd` +version=${1:-1.4.1} +home=~ +force=$2 + +add_fake_shm_module="--add-module=$root/t/data/fake-shm-module" + +rm -fr buildroot +time ngx-build $force $version \ + --with-threads \ + --with-pcre-jit \ + --with-ipv6 \ + --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE2_INC" \ + --with-http_v2_module \ + --with-http_realip_module \ + --add-module=$root/../ndk-nginx-module \ + --add-module=$root/../set-misc-nginx-module \ + --with-ld-opt="-L$PCRE2_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB" \ + --without-mail_pop3_module \ + --without-mail_imap_module \ + --with-http_image_filter_module \ + --without-mail_smtp_module \ + --with-stream \ + --without-http_upstream_ip_hash_module \ + --without-http_memcached_module \ + --without-http_auth_basic_module \ + --without-http_userid_module \ + --with-http_auth_request_module \ + --add-module=$root/../echo-nginx-module \ + --add-module=$root/../memc-nginx-module \ + --add-module=$root/../srcache-nginx-module \ + --add-module=$root \ + --add-module=$root/../lua-upstream-nginx-module \ + --add-module=$root/../headers-more-nginx-module \ + --add-module=$root/../drizzle-nginx-module \ + --add-module=$root/../rds-json-nginx-module \ + --add-module=$root/../coolkit-nginx-module \ + --add-module=$root/../redis2-nginx-module \ + --add-module=$root/../stream-lua-nginx-module \ + --add-module=$root/t/data/fake-module \ + $add_fake_shm_module \ + --add-module=$root/t/data/fake-delayed-load-module \ + --with-http_gunzip_module \ + --with-http_dav_module \ + --with-select_module \ + --with-poll_module \ + $opts \ + --with-debug + diff --git a/util/build.sh b/util/build.sh index 14e9fd46d9..501f74bdb9 100755 --- a/util/build.sh +++ b/util/build.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # this script is for developers only. -# dependent on the ngx-build script from the nginx-devel-utils repostory: +# dependent on the ngx-build script from the nginx-devel-utils repository: # https://github.com/openresty/nginx-devel-utils/blob/master/ngx-build # the resulting nginx is located at ./work/nginx/sbin/nginx @@ -24,45 +24,49 @@ force=$2 add_fake_shm_module="--add-module=$root/t/data/fake-shm-module" +answer=`$root/util/ver-ge "$version" 1.25.1` + time ngx-build $force $version \ - --with-pcre-jit \ - --with-ipv6 \ - --with-cc-opt="-I$PCRE_INC -I$OPENSSL_INC" \ - --with-http_v2_module \ - --with-http_realip_module \ - --with-http_ssl_module \ - --add-module=$root/../ndk-nginx-module \ - --add-module=$root/../set-misc-nginx-module \ - --with-ld-opt="-L$PCRE_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \ - --without-mail_pop3_module \ - --without-mail_imap_module \ - --with-http_image_filter_module \ - --without-mail_smtp_module \ - --with-stream \ - --with-stream_ssl_module \ - --without-http_upstream_ip_hash_module \ - --without-http_memcached_module \ - --without-http_auth_basic_module \ - --without-http_userid_module \ - --with-http_auth_request_module \ - --add-module=$root/../echo-nginx-module \ - --add-module=$root/../memc-nginx-module \ - --add-module=$root/../srcache-nginx-module \ - --add-module=$root \ - --add-module=$root/../lua-upstream-nginx-module \ - --add-module=$root/../headers-more-nginx-module \ - --add-module=$root/../drizzle-nginx-module \ - --add-module=$root/../rds-json-nginx-module \ - --add-module=$root/../coolkit-nginx-module \ - --add-module=$root/../redis2-nginx-module \ - --add-module=$root/../stream-lua-nginx-module \ - --add-module=$root/t/data/fake-module \ - $add_fake_shm_module \ - --add-module=$root/t/data/fake-delayed-load-module \ - --with-http_gunzip_module \ - --with-http_dav_module \ + --with-threads \ + --with-pcre-jit \ + --with-ipv6 \ + --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE2_INC -I$OPENSSL_INC" \ + --with-http_v2_module \ + --with-http_v3_module \ + --with-http_realip_module \ + --with-http_ssl_module \ + --add-module=$root/../ndk-nginx-module \ + --add-module=$root/../set-misc-nginx-module \ + --with-ld-opt="-L$PCRE2_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \ + --without-mail_pop3_module \ + --without-mail_imap_module \ + --with-http_image_filter_module \ + --without-mail_smtp_module \ + --with-stream \ + --with-stream_ssl_module \ + --without-http_upstream_ip_hash_module \ + --without-http_memcached_module \ + --without-http_auth_basic_module \ + --without-http_userid_module \ + --with-http_auth_request_module \ + --add-module=$root/../echo-nginx-module \ + --add-module=$root/../memc-nginx-module \ + --add-module=$root/../srcache-nginx-module \ + --add-module=$root \ + --add-module=$root/../lua-upstream-nginx-module \ + --add-module=$root/../headers-more-nginx-module \ + --add-module=$root/../drizzle-nginx-module \ + --add-module=$root/../rds-json-nginx-module \ + --add-module=$root/../coolkit-nginx-module \ + --add-module=$root/../redis2-nginx-module \ + --add-module=$root/../stream-lua-nginx-module \ + --add-module=$root/t/data/fake-module \ + $add_fake_shm_module \ + --add-module=$root/t/data/fake-delayed-load-module \ + --with-http_gunzip_module \ + --with-http_dav_module \ --with-select_module \ --with-poll_module \ - $opts \ - --with-debug + $opts \ + --with-debug diff --git a/util/nc_server.py b/util/nc_server.py new file mode 100644 index 0000000000..48c6f4bf15 --- /dev/null +++ b/util/nc_server.py @@ -0,0 +1,25 @@ +import select, socket +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.setblocking(0) +server.bind(('localhost', 65110)) +server.listen(5) +inputs = [server] + +while inputs: + outputs = [] + readable, writable, exceptional = select.select( + inputs, outputs, inputs) + for s in readable: + if s is server: + connection, client_address = s.accept() + connection.setblocking(0) + inputs.append(connection) + else: + data = s.recv(1024) + if not data: + inputs.remove(s) + s.close() + + for s in exceptional: + inputs.remove(s) + s.close() diff --git a/util/run-ci.sh b/util/run-ci.sh new file mode 100755 index 0000000000..bb23926165 --- /dev/null +++ b/util/run-ci.sh @@ -0,0 +1,193 @@ +#!/bin/bash + +#export CC=clang +export CC=gcc +export NGX_BUILD_CC=$CC + +mkdir -p download-cache + +export JOBS=$(nproc) +export NGX_BUILD_JOBS=$JOBS +export VALGRIND_INC=/usr/include/valgrind/ + +export LUAJIT_PREFIX=/opt/luajit21 +export LUAJIT_LIB=$LUAJIT_PREFIX/lib +export LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 +export LUA_INCLUDE_DIR=$LUAJIT_INC + +export PCRE2_VER=10.45 +export PCRE2_PREFIX=/opt/pcre2 +export PCRE2_LIB=$PCRE2_PREFIX/lib +export PCRE2_INC=$PCRE2_PREFIX/include + +export OPENSSL_VER=3.5.0 +export OPENSSL_PATCH_VER=3.5.0 +export OPENSSL_PREFIX=/opt/ssl3 +export OPENSSL_LIB=$OPENSSL_PREFIX/lib +export OPENSSL_INC=$OPENSSL_PREFIX/include + +export LIBDRIZZLE_PREFIX=/opt/drizzle +export LIBDRIZZLE_INC=$LIBDRIZZLE_PREFIX/include/libdrizzle-1.0 +export LIBDRIZZLE_LIB=$LIBDRIZZLE_PREFIX/lib +export DRIZZLE_VER=2011.07.21 + +#export TEST_NGINX_SLEEP=0.006 +export NGINX_VERSION=1.27.1 +#export NGX_BUILD_ASAN=1 + +export PATH=/opt/bin:$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH + +if [ ! -f /opt/bin/curl ]; then + wget https://github.com/stunnel/static-curl/releases/download/8.14.1/curl-linux-x86_64-glibc-8.14.1.tar.xz + tar -xf curl-linux-x86_64-glibc-8.14.1.tar.xz + tar -xf curl-linux-x86_64-glibc-8.14.1.tar.xz + sudo mkdir -p /opt/bin + sudo mv curl /opt/bin/ +fi + +function git_download() +{ + dir=${!#} + if [ ! -d $dir ]; then + git clone $@ + fi +} + +function download_deps() +{ + if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then + wget -P download-cache https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-$DRIZZLE_VER.tar.gz + fi + + if [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then + wget -P download-cache https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz + fi + + if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then + wget -P download-cache https://github.com/openssl/openssl/releases/download/openssl-$OPENSSL_VER/openssl-$OPENSSL_VER.tar.gz + fi + + git_download https://github.com/openresty/test-nginx.git + git_download https://github.com/openresty/openresty.git ../openresty + git_download https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx + git_download https://github.com/openresty/openresty-devel-utils.git + git_download https://github.com/openresty/mockeagain.git + git_download https://github.com/openresty/lua-cjson.git lua-cjson + git_download https://github.com/openresty/lua-upstream-nginx-module.git ../lua-upstream-nginx-module + git_download https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module + git_download https://github.com/openresty/nginx-eval-module.git ../nginx-eval-module + git_download https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module + git_download https://github.com/FRiCKLE/ngx_coolkit.git ../coolkit-nginx-module + git_download https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module + git_download https://github.com/openresty/drizzle-nginx-module.git ../drizzle-nginx-module + git_download https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module + git_download https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module + git_download https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module + git_download https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module + git_download https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module + git_download https://github.com/openresty/lua-resty-core.git ../lua-resty-core + git_download https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache + git_download https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql + git_download https://github.com/openresty/lua-resty-string.git ../lua-resty-string + git_download https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module + git_download -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2 +} + +function make_deps() +{ + if [ "$TEST_NGINX_BUILD_DEPS" = "n" ]; then + return + fi + + cd luajit2/ + make clean + make -j"$JOBS" CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS="-DLUAJIT_USE_VALGRIND -I$VALGRIND_INC -DLUAJIT_USE_SYSMALLOC -DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2" > build.log 2>&1 || (cat build.log && exit 1) + sudo make install PREFIX=$LUAJIT_PREFIX + cd .. + + tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz + cd openssl-$OPENSSL_VER/ + patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch > ssl.log + ./config -DOPENSSL_TLS_SECURITY_LEVEL=1 shared enable-ssl3 enable-ssl3-method -g -O2 --prefix=$OPENSSL_PREFIX --libdir=lib -DPURIFY >> ssl.log + make -j$JOBS >> ssl.log + sudo make PATH=$PATH install_sw >> ssl.log + cd .. + + tar zxf download-cache/pcre2-$PCRE2_VER.tar.gz; + cd pcre2-$PCRE2_VER/; + ./configure --prefix=$PCRE2_PREFIX --enable-jit --enable-utf + sudo PATH=$PATH make install + cd .. + + tar xzf download-cache/drizzle7-$DRIZZLE_VER.tar.gz && cd drizzle7-$DRIZZLE_VER + ./configure --prefix=$LIBDRIZZLE_PREFIX --without-server + make libdrizzle-1.0 -j$JOBS + sudo make install-libdrizzle-1.0 + cd .. + + cd mockeagain/ && make CC=$CC -j$JOBS + cd .. + + cd lua-cjson/ && make -j$JOBS && sudo make install + cd .. +} + +function make_ngx() +{ + if [ "$TEST_NGINX_FRESH_BUILD" = "y" ]; then + rm -fr buildroot + fi + + sh util/build.sh $NGINX_VERSION 2>&1 | tee build.log +} + +download_deps +make_deps +make_ngx + +find t -name "*.t" | xargs reindex >/dev/null 2>&1 + +#nginx -V + +export LD_PRELOAD=$PWD/mockeagain/mockeagain.so +export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH + +#export ASAN_OPTIONS=detect_leaks=0,log_path=/tmp/asan/asan,log_exe_name=true +#export LD_PRELOAD=/lib64/libasan.so.5:$PWD/mockeagain/mockeagain.so + +export LD_LIBRARY_PATH=$LUAJIT_LIB:$OPENSSL_LIB:$LD_LIBRARY_PATH +export TEST_NGINX_RESOLVER=8.8.4.4 + +#export TEST_NGINX_NO_CLEAN=1 +#export TEST_NGINX_CHECK_LEAK=1 +#export TEST_NGINX_CHECK_LEAK_COUNT=100 +#export TEST_NGINX_TIMEOUT=5 + +#export TEST_NGINX_USE_VALGRIND=1 +#export TEST_NGINX_VALGRIND_EXIT_ON_FIRST_ERR=1 +#export TEST_NGINX_USE_HTTP2=1 + +export MALLOC_PERTURB_=33 +export TEST_NGINX_HTTP3_CRT=$PWD/t/cert/http3/http3.crt +export TEST_NGINX_HTTP3_KEY=$PWD/t/cert/http3/http3.key +#export TEST_NGINX_USE_HTTP3=1 + +#export TEST_NGINX_VERBOSE=1 + +#export TEST_NGINX_EVENT_TYPE=poll +#export TEST_NGINX_POSTPONE_OUTPUT=1 +#export MOCKEAGAIN=r +#export MOCKEAGAIN=w +#export MOCKEAGAIN=rw +#export MOCKEAGAIN_VERBOSE=1 + +ldd `which nginx`|grep -E 'luajit|ssl|pcre' +which nginx +nginx -V + +#export TEST_NGINX_INIT_BY_LUA="debug.sethook(function () collectgarbage() end, 'l') jit.off() package.path = '/usr/share/lua/5.1/?.lua;$PWD/../lua-resty-core/lib/?.lua;$PWD/../lua-resty-lrucache/lib/?.lua;' .. (package.path or '') require 'resty.core' require('resty.core.base').set_string_buf_size(1) require('resty.core.regex').set_buf_grow_ratio(1)" + + +# comment out TEST_NGINX_RANDOMIZE when debugging one test case +export TEST_NGINX_RANDOMIZE=1 +prove -j$JOBS -I. -Itest-nginx/inc -Itest-nginx/lib -r t/ diff --git a/util/ver-ge b/util/ver-ge new file mode 100755 index 0000000000..8d7a401e94 --- /dev/null +++ b/util/ver-ge @@ -0,0 +1,41 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +sub usage { + die "Usage: $0 \n"; +} + +my $a = shift or usage(); +my $b = shift or usage(); + +my @as = split /\./, $a; +my @bs = split /\./, $b; + +my $n = @as > @bs ? scalar(@as) : scalar(@bs); + +for (my $i = 0; $i < $n; $i++) { + my $x = $as[$i]; + my $y = $bs[$i]; + + if (!defined $x) { + $x = 0; + } + + if (!defined $y) { + $y = 0; + } + + if ($x > $y) { + print "Y\n"; + exit; + + } elsif ($x < $y) { + print "N\n"; + exit; + } +} + +print "Y\n"; + diff --git a/valgrind.suppress b/valgrind.suppress index 7fff542ffe..743fb67d77 100644 --- a/valgrind.suppress +++ b/valgrind.suppress @@ -207,3 +207,100 @@ fun:ngx_epoll_init fun:ngx_event_process_init } +{ + + Memcheck:Param + sendmsg(msg.msg_iov[0]) + fun:__sendmsg_nocancel + fun:ngx_write_channel + fun:ngx_pass_open_channel + fun:ngx_start_worker_processes +} +{ + + Memcheck:Param + sendmsg(msg.msg_iov[0]) + fun:__sendmsg_nocancel + fun:ngx_write_channel + fun:ngx_pass_open_channel + fun:ngx_start_cache_manager_processes +} +{ + + Memcheck:Param + sendmsg(msg.msg_iov[0]) + fun:__sendmsg_nocancel + fun:ngx_write_channel + fun:ngx_pass_open_channel + fun:ngx_start_privileged_agent_processes +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_regex_malloc + fun:pcre2_compile_context_create_8 + fun:ngx_regex_compile + fun:ngx_http_regex_compile + fun:ngx_http_core_regex_location + fun:ngx_http_core_location + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_http_core_server + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_http_block + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_init_cycle + fun:main +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_regex_malloc + fun:pcre2_compile_context_create_8 + fun:ngx_regex_compile + fun:ngx_http_regex_compile + fun:ngx_http_rewrite + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_http_core_location + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_http_core_server + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_http_block + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_init_cycle + fun:main +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_regex_malloc + fun:pcre2_compile_context_create_8 + fun:ngx_regex_compile + fun:ngx_http_regex_compile + fun:ngx_http_rewrite + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_http_core_server + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_http_block + fun:ngx_conf_handler + fun:ngx_conf_parse + fun:ngx_init_cycle + fun:main +}